mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-27 03:13:13 +00:00
8156217: Selected text is shifted on HiDPI display
Reviewed-by: prr, serb
This commit is contained in:
parent
4a4be07df3
commit
e8c27e9a2f
@ -46,7 +46,11 @@ public abstract class TextUI extends ComponentUI
|
||||
* @return the coordinates as a {@code Rectangle}
|
||||
* @exception BadLocationException if the given position does not
|
||||
* represent a valid location in the associated document
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #modelToView2D(JTextComponent, int, Position.Bias)}
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
public abstract Rectangle modelToView(JTextComponent t, int pos) throws BadLocationException;
|
||||
|
||||
/**
|
||||
@ -59,7 +63,11 @@ public abstract class TextUI extends ComponentUI
|
||||
* @return the coordinates as a {@code Rectangle}
|
||||
* @exception BadLocationException if the given position does not
|
||||
* represent a valid location in the associated document
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #modelToView2D(JTextComponent, int, Position.Bias)}
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
public abstract Rectangle modelToView(JTextComponent t, int pos, Position.Bias bias) throws BadLocationException;
|
||||
|
||||
/**
|
||||
@ -92,7 +100,11 @@ public abstract class TextUI extends ComponentUI
|
||||
* should be in the same coordinate system as the mouse
|
||||
* events.
|
||||
* @return the offset from the start of the document >= 0
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #viewToModel2D(JTextComponent, Point2D, Position.Bias[])}
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
public abstract int viewToModel(JTextComponent t, Point pt);
|
||||
|
||||
/**
|
||||
@ -110,7 +122,11 @@ public abstract class TextUI extends ComponentUI
|
||||
*
|
||||
* @return the location within the model that best represents the
|
||||
* given point in the view >= 0
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #viewToModel2D(JTextComponent, Point2D, Position.Bias[])}
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
public abstract int viewToModel(JTextComponent t, Point pt,
|
||||
Position.Bias[] biasReturn);
|
||||
|
||||
@ -222,7 +238,11 @@ public abstract class TextUI extends ComponentUI
|
||||
* @return a {@code String} containing the tooltip
|
||||
* @see javax.swing.text.JTextComponent#getToolTipText
|
||||
* @since 1.4
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #getToolTipText2D(JTextComponent, Point2D)}
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
public String getToolTipText(JTextComponent t, Point pt) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -28,6 +28,8 @@ import java.util.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.datatransfer.*;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.im.InputContext;
|
||||
import java.beans.*;
|
||||
import java.io.*;
|
||||
@ -1047,7 +1049,12 @@ public abstract class BasicTextUI extends TextUI implements ViewFactory {
|
||||
* @exception BadLocationException if the given position does not
|
||||
* represent a valid location in the associated document
|
||||
* @see TextUI#modelToView
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #modelToView2D(JTextComponent, int, Position.Bias)}
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
@Override
|
||||
public Rectangle modelToView(JTextComponent tc, int pos) throws BadLocationException {
|
||||
return modelToView(tc, pos, Position.Bias.Forward);
|
||||
}
|
||||
@ -1064,8 +1071,30 @@ public abstract class BasicTextUI extends TextUI implements ViewFactory {
|
||||
* @exception BadLocationException if the given position does not
|
||||
* represent a valid location in the associated document
|
||||
* @see TextUI#modelToView
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #modelToView2D(JTextComponent, int, Position.Bias)}
|
||||
*/
|
||||
public Rectangle modelToView(JTextComponent tc, int pos, Position.Bias bias) throws BadLocationException {
|
||||
@Deprecated(since = "9")
|
||||
@Override
|
||||
public Rectangle modelToView(JTextComponent tc, int pos, Position.Bias bias)
|
||||
throws BadLocationException
|
||||
{
|
||||
return (Rectangle) modelToView(tc, pos, bias, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D modelToView2D(JTextComponent tc, int pos,
|
||||
Position.Bias bias)
|
||||
throws BadLocationException
|
||||
{
|
||||
return modelToView(tc, pos, bias, true);
|
||||
}
|
||||
|
||||
private Rectangle2D modelToView(JTextComponent tc, int pos,
|
||||
Position.Bias bias, boolean useFPAPI)
|
||||
throws BadLocationException
|
||||
{
|
||||
Document doc = editor.getDocument();
|
||||
if (doc instanceof AbstractDocument) {
|
||||
((AbstractDocument)doc).readLock();
|
||||
@ -1076,7 +1105,7 @@ public abstract class BasicTextUI extends TextUI implements ViewFactory {
|
||||
rootView.setSize(alloc.width, alloc.height);
|
||||
Shape s = rootView.modelToView(pos, alloc, bias);
|
||||
if (s != null) {
|
||||
return s.getBounds();
|
||||
return useFPAPI ? s.getBounds2D() : s.getBounds();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@ -1099,7 +1128,12 @@ public abstract class BasicTextUI extends TextUI implements ViewFactory {
|
||||
* @return the offset from the start of the document >= 0,
|
||||
* -1 if not painted
|
||||
* @see TextUI#viewToModel
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #viewToModel2D(JTextComponent, Point2D, Position.Bias[])}
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
@Override
|
||||
public int viewToModel(JTextComponent tc, Point pt) {
|
||||
return viewToModel(tc, pt, discardBias);
|
||||
}
|
||||
@ -1116,9 +1150,25 @@ public abstract class BasicTextUI extends TextUI implements ViewFactory {
|
||||
* @return the offset from the start of the document >= 0,
|
||||
* -1 if the component doesn't yet have a positive size.
|
||||
* @see TextUI#viewToModel
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #viewToModel2D(JTextComponent, Point2D, Position.Bias[])}
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
@Override
|
||||
public int viewToModel(JTextComponent tc, Point pt,
|
||||
Position.Bias[] biasReturn) {
|
||||
return viewToModel(tc, pt.x, pt.y, biasReturn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int viewToModel2D(JTextComponent tc, Point2D pt,
|
||||
Position.Bias[] biasReturn) {
|
||||
return viewToModel(tc, (float) pt.getX(), (float) pt.getY(), biasReturn);
|
||||
}
|
||||
|
||||
private int viewToModel(JTextComponent tc, float x, float y,
|
||||
Position.Bias[] biasReturn) {
|
||||
int offs = -1;
|
||||
Document doc = editor.getDocument();
|
||||
if (doc instanceof AbstractDocument) {
|
||||
@ -1128,7 +1178,7 @@ public abstract class BasicTextUI extends TextUI implements ViewFactory {
|
||||
Rectangle alloc = getVisibleEditorRect();
|
||||
if (alloc != null) {
|
||||
rootView.setSize(alloc.width, alloc.height);
|
||||
offs = rootView.viewToModel(pt.x, pt.y, alloc, biasReturn);
|
||||
offs = rootView.viewToModel(x, y, alloc, biasReturn);
|
||||
}
|
||||
} finally {
|
||||
if (doc instanceof AbstractDocument) {
|
||||
|
||||
@ -38,6 +38,8 @@ import javax.swing.plaf.ComponentUI;
|
||||
import javax.swing.JComponent;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import javax.accessibility.Accessible;
|
||||
|
||||
/**
|
||||
@ -97,7 +99,11 @@ public class MultiTextUI extends TextUI {
|
||||
*
|
||||
* @return the value obtained from the first UI, which is
|
||||
* the UI obtained from the default <code>LookAndFeel</code>
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #modelToView2D(JTextComponent, int, Position.Bias)}
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
public Rectangle modelToView(JTextComponent a, int b)
|
||||
throws BadLocationException {
|
||||
Rectangle returnValue =
|
||||
@ -113,7 +119,12 @@ public class MultiTextUI extends TextUI {
|
||||
*
|
||||
* @return the value obtained from the first UI, which is
|
||||
* the UI obtained from the default <code>LookAndFeel</code>
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #modelToView2D(JTextComponent, int, Position.Bias)}
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
@Override
|
||||
public Rectangle modelToView(JTextComponent a, int b, Position.Bias c)
|
||||
throws BadLocationException {
|
||||
Rectangle returnValue =
|
||||
@ -124,12 +135,24 @@ public class MultiTextUI extends TextUI {
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle2D modelToView2D(JTextComponent a, int b, Position.Bias c) throws BadLocationException {
|
||||
Rectangle2D returnValue =
|
||||
((TextUI) (uis.elementAt(0))).modelToView2D(a,b,c);
|
||||
for (int i = 1; i < uis.size(); i++) {
|
||||
((TextUI) (uis.elementAt(i))).modelToView2D(a,b,c);
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the <code>viewToModel</code> method on each UI handled by this object.
|
||||
*
|
||||
* @return the value obtained from the first UI, which is
|
||||
* the UI obtained from the default <code>LookAndFeel</code>
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
@Override
|
||||
public int viewToModel(JTextComponent a, Point b) {
|
||||
int returnValue =
|
||||
((TextUI) (uis.elementAt(0))).viewToModel(a,b);
|
||||
@ -145,6 +168,8 @@ public class MultiTextUI extends TextUI {
|
||||
* @return the value obtained from the first UI, which is
|
||||
* the UI obtained from the default <code>LookAndFeel</code>
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
@Override
|
||||
public int viewToModel(JTextComponent a, Point b, Position.Bias[] c) {
|
||||
int returnValue =
|
||||
((TextUI) (uis.elementAt(0))).viewToModel(a,b,c);
|
||||
@ -154,6 +179,16 @@ public class MultiTextUI extends TextUI {
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int viewToModel2D(JTextComponent a, Point2D b, Position.Bias[] c) {
|
||||
int returnValue =
|
||||
((TextUI) (uis.elementAt(0))).viewToModel2D(a,b,c);
|
||||
for (int i = 1; i < uis.size(); i++) {
|
||||
((TextUI) (uis.elementAt(i))).viewToModel2D(a,b,c);
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the <code>getNextVisualPositionFrom</code> method on each UI handled by this object.
|
||||
*
|
||||
|
||||
@ -98,26 +98,27 @@ class GlyphPainter1 extends GlyphView.GlyphPainter {
|
||||
Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();
|
||||
|
||||
// determine the x coordinate to render the glyphs
|
||||
int x = alloc.x;
|
||||
float x = alloc.x;
|
||||
int p = v.getStartOffset();
|
||||
int[] justificationData = getJustificationData(v);
|
||||
if (p != p0) {
|
||||
text = v.getText(p, p0);
|
||||
int width = Utilities.getTabbedTextWidth(v, text, metrics, x, expander, p,
|
||||
justificationData);
|
||||
float width = Utilities.getTabbedTextWidth(v, text, metrics, x,
|
||||
expander, p,
|
||||
justificationData);
|
||||
x += width;
|
||||
SegmentCache.releaseSharedSegment(text);
|
||||
}
|
||||
|
||||
// determine the y coordinate to render the glyphs
|
||||
int y = alloc.y + metrics.getHeight() - metrics.getDescent();
|
||||
float y = alloc.y + metrics.getHeight() - metrics.getDescent();
|
||||
|
||||
// render the glyphs
|
||||
text = v.getText(p0, p1);
|
||||
g.setFont(metrics.getFont());
|
||||
|
||||
Utilities.drawTabbedText(v, text, x, y, g, expander,p0,
|
||||
justificationData);
|
||||
justificationData, true);
|
||||
SegmentCache.releaseSharedSegment(text);
|
||||
}
|
||||
|
||||
@ -210,9 +211,9 @@ class GlyphPainter1 extends GlyphView.GlyphPainter {
|
||||
TabExpander expander = v.getTabExpander();
|
||||
Segment s = v.getText(p0, v.getEndOffset());
|
||||
int[] justificationData = getJustificationData(v);
|
||||
int index = Utilities.getTabbedTextOffset(v, s, metrics, (int)x, (int)(x+len),
|
||||
int index = Utilities.getTabbedTextOffset(v, s, metrics, x, (x+len),
|
||||
expander, p0, false,
|
||||
justificationData);
|
||||
justificationData, true);
|
||||
SegmentCache.releaseSharedSegment(s);
|
||||
int p1 = p0 + index;
|
||||
return p1;
|
||||
|
||||
@ -145,8 +145,9 @@ class GlyphPainter2 extends GlyphView.GlyphPainter {
|
||||
|
||||
// vertical at the baseline, should use slope and check if glyphs
|
||||
// are being rendered vertically.
|
||||
alloc.setRect(alloc.getX() + locs[0], alloc.getY(), 1, alloc.getHeight());
|
||||
return alloc;
|
||||
Rectangle2D rect = new Rectangle2D.Float();
|
||||
rect.setRect(alloc.getX() + locs[0], alloc.getY(), 1, alloc.getHeight());
|
||||
return rect;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -49,6 +49,8 @@ import java.awt.im.InputContext;
|
||||
import java.awt.im.InputMethodRequests;
|
||||
import java.awt.font.TextHitInfo;
|
||||
import java.awt.font.TextAttribute;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
import java.awt.print.Printable;
|
||||
import java.awt.print.PrinterException;
|
||||
@ -1370,11 +1372,37 @@ public abstract class JTextComponent extends JComponent implements Scrollable, A
|
||||
* @exception BadLocationException if the given position does not
|
||||
* represent a valid location in the associated document
|
||||
* @see TextUI#modelToView
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #modelToView2D(int)}
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
public Rectangle modelToView(int pos) throws BadLocationException {
|
||||
return getUI().modelToView(this, pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given location in the model to a place in
|
||||
* the view coordinate system.
|
||||
* The component must have a positive size for
|
||||
* this translation to be computed (i.e. layout cannot
|
||||
* be computed until the component has been sized). The
|
||||
* component does not have to be visible or painted.
|
||||
*
|
||||
* @param pos the position {@code >= 0}
|
||||
* @return the coordinates as a rectangle, with (r.x, r.y) as the location
|
||||
* in the coordinate system, or null if the component does
|
||||
* not yet have a positive size.
|
||||
* @exception BadLocationException if the given position does not
|
||||
* represent a valid location in the associated document
|
||||
* @see TextUI#modelToView2D
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
public Rectangle2D modelToView2D(int pos) throws BadLocationException {
|
||||
return getUI().modelToView2D(this, pos, Position.Bias.Forward);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given place in the view coordinate system
|
||||
* to the nearest representative location in the model.
|
||||
@ -1388,11 +1416,35 @@ public abstract class JTextComponent extends JComponent implements Scrollable, A
|
||||
* or -1 if the component does not yet have a positive
|
||||
* size.
|
||||
* @see TextUI#viewToModel
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #viewToModel2D(Point2D)}
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
public int viewToModel(Point pt) {
|
||||
return getUI().viewToModel(this, pt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given place in the view coordinate system
|
||||
* to the nearest representative location in the model.
|
||||
* The component must have a positive size for
|
||||
* this translation to be computed (i.e. layout cannot
|
||||
* be computed until the component has been sized). The
|
||||
* component does not have to be visible or painted.
|
||||
*
|
||||
* @param pt the location in the view to translate
|
||||
* @return the offset {@code >= 0} from the start of the document,
|
||||
* or {@code -1} if the component does not yet have a positive
|
||||
* size.
|
||||
* @see TextUI#viewToModel2D
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
public int viewToModel2D(Point2D pt) {
|
||||
return getUI().viewToModel2D(this, pt, new Position.Bias[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfers the currently selected range in the associated
|
||||
* text model to the system clipboard, removing the contents
|
||||
|
||||
@ -27,6 +27,7 @@ package javax.swing.text;
|
||||
import java.util.Arrays;
|
||||
import java.awt.*;
|
||||
import java.awt.font.TextAttribute;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import javax.swing.event.*;
|
||||
import javax.swing.SizeRequirements;
|
||||
|
||||
@ -888,10 +889,9 @@ public class ParagraphView extends FlowView implements TabExpander {
|
||||
int height = r.height;
|
||||
int y = r.y;
|
||||
Shape loc = super.modelToView(pos, a, b);
|
||||
r = loc.getBounds();
|
||||
r.height = height;
|
||||
r.y = y;
|
||||
return r;
|
||||
Rectangle2D bounds = loc.getBounds2D();
|
||||
bounds.setRect(bounds.getX(), y, bounds.getWidth(), height);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -26,7 +26,11 @@ package javax.swing.text;
|
||||
|
||||
import sun.swing.SwingUtilities2;
|
||||
import java.awt.*;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import javax.swing.JPasswordField;
|
||||
import static javax.swing.text.PlainView.isFPMethodOverriden;
|
||||
|
||||
/**
|
||||
* Implements a View suitable for use in JPasswordField
|
||||
@ -61,15 +65,40 @@ public class PasswordView extends FieldView {
|
||||
* @param p1 the ending offset in the model >= 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 @@ public class PasswordView extends FieldView {
|
||||
}
|
||||
char echoChar = f.getEchoChar();
|
||||
int n = p1 - p0;
|
||||
boolean useEchoCharFPAPI = useFPAPI
|
||||
&& drawEchoCharacterOverridden
|
||||
&& g instanceof Graphics2D;
|
||||
for (int i = 0; i < n; i++) {
|
||||
x = drawEchoCharacter(g, x, y, echoChar);
|
||||
x = (useEchoCharFPAPI)
|
||||
? drawEchoCharacter((Graphics2D) g, x, y, echoChar)
|
||||
: drawEchoCharacter(g, (int) x, (int) y, echoChar);
|
||||
}
|
||||
}
|
||||
return x;
|
||||
@ -100,20 +134,50 @@ public class PasswordView extends FieldView {
|
||||
* @param p1 the ending offset in the model >= 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 @@ public class PasswordView extends FieldView {
|
||||
* @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 @@ public class PasswordView extends FieldView {
|
||||
* object is set to the appropriate foreground color for selected
|
||||
* or unselected text.
|
||||
*
|
||||
* @implSpec This implementation calls
|
||||
* {@link #drawEchoCharacter(Graphics, int, int, char)
|
||||
* drawEchoCharacter((Graphics) g, (int) x, (int) y, c)}.
|
||||
*
|
||||
* @param g the graphics context
|
||||
* @param x the starting X coordinate {@code >= 0}
|
||||
* @param y the starting Y coordinate {@code >= 0}
|
||||
* @param c the echo character
|
||||
* @return the updated X position {@code >= 0}
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
protected float drawEchoCharacter(Graphics2D g, float x, float y, char c) {
|
||||
return drawEchoCharacter((Graphics) g, (int) x, (int) y, c);
|
||||
return drawEchoCharacterImpl(g, x, y, c, true);
|
||||
}
|
||||
|
||||
private float drawEchoCharacterImpl(Graphics g, float x, float y,
|
||||
char c, boolean useFPAPI) {
|
||||
ONE[0] = c;
|
||||
SwingUtilities2.drawChars(Utilities.getJComponent(this),
|
||||
g, ONE, 0, 1, x, y);
|
||||
if (useFPAPI) {
|
||||
return x + g.getFontMetrics().charWidth(c);
|
||||
} else {
|
||||
FontRenderContext frc = g.getFontMetrics().getFontRenderContext();
|
||||
return x + (float) g.getFont().getStringBounds(ONE, 0, 1, frc).getWidth();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -253,4 +329,23 @@ public class PasswordView extends FieldView {
|
||||
}
|
||||
|
||||
static char[] ONE = new char[1];
|
||||
|
||||
private final boolean drawEchoCharacterOverridden;
|
||||
|
||||
{
|
||||
final Class<?> CLS = getClass();
|
||||
final Class<?> INT = Integer.TYPE;
|
||||
final Class<?> FP = Float.TYPE;
|
||||
final Class<?> CHAR = Character.TYPE;
|
||||
|
||||
drawEchoCharacterOverridden = AccessController
|
||||
.doPrivileged(new PrivilegedAction<Boolean>() {
|
||||
@Override
|
||||
public Boolean run() {
|
||||
Class<?>[] intTypes = {Graphics.class, INT, INT, CHAR};
|
||||
Class<?>[] fpTypes = {Graphics2D.class, FP, FP, CHAR};
|
||||
return isFPMethodOverriden("drawEchoCharacter", CLS, intTypes, fpTypes);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,11 +24,14 @@
|
||||
*/
|
||||
package javax.swing.text;
|
||||
|
||||
import java.util.Vector;
|
||||
import java.util.Properties;
|
||||
import java.awt.*;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Objects;
|
||||
import javax.swing.event.*;
|
||||
import java.lang.reflect.Module;
|
||||
|
||||
/**
|
||||
* Implements View interface for a simple multi-line text view
|
||||
@ -60,17 +63,6 @@ public class PlainView extends View implements TabExpander {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tab size set for the document, defaulting to 8.
|
||||
*
|
||||
* @implSpec This implementation calls {@link #getTabSize() getTabSize()}.
|
||||
*
|
||||
* @return the tab size
|
||||
*/
|
||||
protected float getFractionalTabSize() {
|
||||
return getTabSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a line of text, suppressing whitespace at the end
|
||||
* and expanding any tabs. This is implemented to make calls
|
||||
@ -84,8 +76,16 @@ public class PlainView extends View implements TabExpander {
|
||||
* @param y the starting Y position >= 0
|
||||
* @see #drawUnselectedText
|
||||
* @see #drawSelectedText
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #drawLine(int, Graphics2D, float, float)}
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
protected void drawLine(int lineIndex, Graphics g, int x, int y) {
|
||||
drawLineImpl(lineIndex, g, x, y);
|
||||
}
|
||||
|
||||
private void drawLineImpl(int lineIndex, Graphics g, float x, float y) {
|
||||
Element line = getElement().getElement(lineIndex);
|
||||
Element elem;
|
||||
|
||||
@ -112,22 +112,23 @@ public class PlainView extends View implements TabExpander {
|
||||
* {@code drawSelectedText} so that the way selected and
|
||||
* unselected text are rendered can be customized.
|
||||
*
|
||||
* @implSpec This implementation calls
|
||||
* {@link #drawLine(int, Graphics, int, int)
|
||||
* drawLine(lineIndex, (Graphics)g, (int) x, (int) y)}.
|
||||
*
|
||||
* @param lineIndex the line to draw {@code >= 0}
|
||||
* @param g the {@code Graphics} context
|
||||
* @param x the starting X position {@code >= 0}
|
||||
* @param y the starting Y position {@code >= 0}
|
||||
* @see #drawUnselectedText
|
||||
* @see #drawSelectedText
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
protected void drawLine(int lineIndex, Graphics2D g, float x, float y) {
|
||||
drawLine(lineIndex, (Graphics)g, (int) x, (int) y);
|
||||
drawLineImpl(lineIndex, g, x, y);
|
||||
}
|
||||
|
||||
private int drawElement(int lineIndex, Element elem, Graphics g, int x, int y) throws BadLocationException {
|
||||
private float drawElement(int lineIndex, Element elem, Graphics g,
|
||||
float x, float y)
|
||||
throws BadLocationException
|
||||
{
|
||||
int p0 = elem.getStartOffset();
|
||||
int p1 = elem.getEndOffset();
|
||||
p1 = Math.min(getDocument().getLength(), p1);
|
||||
@ -144,23 +145,23 @@ public class PlainView extends View implements TabExpander {
|
||||
} else {
|
||||
if (sel0 == sel1 || selected == unselected) {
|
||||
// no selection, or it is invisible
|
||||
x = drawUnselectedText(g, x, y, p0, p1);
|
||||
x = callDrawUnselectedText(g, x, y, p0, p1);
|
||||
} else if ((p0 >= sel0 && p0 <= sel1) && (p1 >= sel0 && p1 <= sel1)) {
|
||||
x = drawSelectedText(g, x, y, p0, p1);
|
||||
x = callDrawSelectedText(g, x, y, p0, p1);
|
||||
} else if (sel0 >= p0 && sel0 <= p1) {
|
||||
if (sel1 >= p0 && sel1 <= p1) {
|
||||
x = drawUnselectedText(g, x, y, p0, sel0);
|
||||
x = drawSelectedText(g, x, y, sel0, sel1);
|
||||
x = drawUnselectedText(g, x, y, sel1, p1);
|
||||
x = callDrawUnselectedText(g, x, y, p0, sel0);
|
||||
x = callDrawSelectedText(g, x, y, sel0, sel1);
|
||||
x = callDrawUnselectedText(g, x, y, sel1, p1);
|
||||
} else {
|
||||
x = drawUnselectedText(g, x, y, p0, sel0);
|
||||
x = drawSelectedText(g, x, y, sel0, p1);
|
||||
x = callDrawUnselectedText(g, x, y, p0, sel0);
|
||||
x = callDrawSelectedText(g, x, y, sel0, p1);
|
||||
}
|
||||
} else if (sel1 >= p0 && sel1 <= p1) {
|
||||
x = drawSelectedText(g, x, y, p0, sel1);
|
||||
x = drawUnselectedText(g, x, y, sel1, p1);
|
||||
x = callDrawSelectedText(g, x, y, p0, sel1);
|
||||
x = callDrawUnselectedText(g, x, y, sel1, p1);
|
||||
} else {
|
||||
x = drawUnselectedText(g, x, y, p0, p1);
|
||||
x = callDrawUnselectedText(g, x, y, p0, p1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,14 +179,36 @@ public class PlainView extends View implements TabExpander {
|
||||
* @param p1 the ending position in the model >= 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 @@ public class PlainView extends View implements TabExpander {
|
||||
* Renders the given range in the model as normal unselected
|
||||
* text. Uses the foreground or disabled color to render the text.
|
||||
*
|
||||
* @implSpec This implementation calls
|
||||
* {@link #drawUnselectedText(Graphics, int, int, int, int)
|
||||
* drawUnselectedText((Graphics)g, (int) x, (int) y, p0, p1)}.
|
||||
*
|
||||
* @param g the graphics context
|
||||
* @param x the starting X coordinate {@code >= 0}
|
||||
* @param y the starting Y coordinate {@code >= 0}
|
||||
@ -205,10 +224,12 @@ public class PlainView extends View implements TabExpander {
|
||||
* @param p1 the ending position in the model {@code >= 0}
|
||||
* @return the X location of the end of the range {@code >= 0}
|
||||
* @exception BadLocationException if the range is invalid
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
protected float drawUnselectedText(Graphics2D g, float x, float y,
|
||||
int p0, int p1) throws BadLocationException {
|
||||
return drawUnselectedText((Graphics)g, (int) x, (int) y, p0, p1);
|
||||
return drawUnselectedTextImpl(g, x, y, p0, p1, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -224,14 +245,38 @@ public class PlainView extends View implements TabExpander {
|
||||
* @param p1 the ending position in the model >= 0
|
||||
* @return the location of the end of the range
|
||||
* @exception BadLocationException if the range is invalid
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #drawSelectedText(Graphics2D, float, float, int, int)}
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
protected int drawSelectedText(Graphics g, int x,
|
||||
int y, int p0, int p1) throws BadLocationException {
|
||||
int y, int p0, int p1)
|
||||
throws BadLocationException
|
||||
{
|
||||
return (int) drawSelectedTextImpl(g, x, y, p0, p1, false);
|
||||
}
|
||||
|
||||
float callDrawSelectedText(Graphics g, float x, float y,
|
||||
int p0, int p1)
|
||||
throws BadLocationException
|
||||
{
|
||||
return drawSelectedTextOverridden && g instanceof Graphics2D
|
||||
? drawSelectedText((Graphics2D) g, x, y, p0, p1)
|
||||
: drawSelectedText(g, (int) x, (int) y, p0, p1);
|
||||
}
|
||||
|
||||
private float drawSelectedTextImpl(Graphics g, float x, float y,
|
||||
int p0, int p1,
|
||||
boolean useFPAPI)
|
||||
throws BadLocationException
|
||||
{
|
||||
g.setColor(selected);
|
||||
Document doc = getDocument();
|
||||
Segment s = SegmentCache.getSharedSegment();
|
||||
doc.getText(p0, p1 - p0, s);
|
||||
int ret = Utilities.drawTabbedText(this, s, x, y, g, this, p0);
|
||||
float ret = Utilities.drawTabbedText(this, s, x, y, g, this, p0, null,
|
||||
useFPAPI);
|
||||
SegmentCache.releaseSharedSegment(s);
|
||||
return ret;
|
||||
}
|
||||
@ -242,10 +287,6 @@ public class PlainView extends View implements TabExpander {
|
||||
* the hosting component. It assumes the highlighter will render
|
||||
* the selected background.
|
||||
*
|
||||
* @implSpec This implementation calls
|
||||
* {@link #drawSelectedText(Graphics, int, int, int, int)
|
||||
* drawSelectedText((Graphics)g, (int) x, (int) y, p0, p1)}.
|
||||
*
|
||||
* @param g the graphics context
|
||||
* @param x the starting X coordinate {@code >= 0}
|
||||
* @param y the starting Y coordinate {@code >= 0}
|
||||
@ -253,11 +294,12 @@ public class PlainView extends View implements TabExpander {
|
||||
* @param p1 the ending position in the model {@code >= 0}
|
||||
* @return the location of the end of the range
|
||||
* @exception BadLocationException if the range is invalid
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
|
||||
protected float drawSelectedText(Graphics2D g, float x,
|
||||
float y, int p0, int p1) throws BadLocationException {
|
||||
return drawSelectedText((Graphics)g, (int) x, (int) y, p0, p1);
|
||||
return drawSelectedTextImpl(g, x, y, p0, p1, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -287,7 +329,13 @@ public class PlainView extends View implements TabExpander {
|
||||
// The font changed, we need to recalculate the
|
||||
// longest line.
|
||||
calculateLongestLine();
|
||||
tabSize = getTabSize() * metrics.charWidth('m');
|
||||
if (useFloatingPointAPI) {
|
||||
FontRenderContext frc = metrics.getFontRenderContext();
|
||||
float tabWidth = (float) font.getStringBounds("m", frc).getWidth();
|
||||
tabSize = getTabSize() * tabWidth;
|
||||
} else {
|
||||
tabSize = getTabSize() * metrics.charWidth('m');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -388,7 +436,11 @@ public class PlainView extends View implements TabExpander {
|
||||
originalA, host, this);
|
||||
}
|
||||
}
|
||||
drawLine(line, g, x, y);
|
||||
if (drawLineOverridden && (g instanceof Graphics2D)) {
|
||||
drawLine(line, (Graphics2D) g, (float) x, (float) y);
|
||||
} else {
|
||||
drawLine(line, g, x, y);
|
||||
}
|
||||
y += fontHeight;
|
||||
if (line == 0) {
|
||||
// This should never really happen, in so far as if
|
||||
@ -435,6 +487,13 @@ public class PlainView extends View implements TabExpander {
|
||||
int p0 = line.getStartOffset();
|
||||
Segment s = SegmentCache.getSharedSegment();
|
||||
doc.getText(p0, pos - p0, s);
|
||||
|
||||
if (useFloatingPointAPI) {
|
||||
float xOffs = Utilities.getTabbedTextWidth(s, metrics, (float) tabBase, this, p0);
|
||||
SegmentCache.releaseSharedSegment(s);
|
||||
return new Rectangle2D.Float(lineArea.x + xOffs, lineArea.y, 1, metrics.getHeight());
|
||||
}
|
||||
|
||||
int xOffs = Utilities.getTabbedTextWidth(s, metrics, tabBase, this,p0);
|
||||
SegmentCache.releaseSharedSegment(s);
|
||||
|
||||
@ -456,14 +515,13 @@ public class PlainView extends View implements TabExpander {
|
||||
* given point in the view >= 0
|
||||
* @see View#viewToModel
|
||||
*/
|
||||
public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) {
|
||||
public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
|
||||
// PENDING(prinz) properly calculate bias
|
||||
bias[0] = Position.Bias.Forward;
|
||||
|
||||
Rectangle alloc = a.getBounds();
|
||||
Document doc = getDocument();
|
||||
int x = (int) fx;
|
||||
int y = (int) fy;
|
||||
|
||||
if (y < alloc.y) {
|
||||
// above the area covered by this icon, so the position
|
||||
// is assumed to be the start of the coverage for this view.
|
||||
@ -481,7 +539,7 @@ public class PlainView extends View implements TabExpander {
|
||||
Element map = doc.getDefaultRootElement();
|
||||
int fontHeight = metrics.getHeight();
|
||||
int lineIndex = (fontHeight > 0 ?
|
||||
Math.abs((y - alloc.y) / fontHeight) :
|
||||
(int)Math.abs((y - alloc.y) / fontHeight) :
|
||||
map.getElementCount() - 1);
|
||||
if (lineIndex >= map.getElementCount()) {
|
||||
return getEndOffset() - 1;
|
||||
@ -507,7 +565,7 @@ public class PlainView extends View implements TabExpander {
|
||||
doc.getText(p0, p1 - p0, s);
|
||||
tabBase = alloc.x;
|
||||
int offs = p0 + Utilities.getTabbedTextOffset(s, metrics,
|
||||
tabBase, x, this, p0);
|
||||
tabBase, x, this, p0, true);
|
||||
SegmentCache.releaseSharedSegment(s);
|
||||
return offs;
|
||||
} catch (BadLocationException e) {
|
||||
@ -586,7 +644,7 @@ public class PlainView extends View implements TabExpander {
|
||||
if (tabSize == 0) {
|
||||
return x;
|
||||
}
|
||||
int ntabs = (((int) x) - tabBase) / tabSize;
|
||||
float ntabs = (x - tabBase) / tabSize;
|
||||
return tabBase + ((ntabs + 1) * tabSize);
|
||||
}
|
||||
|
||||
@ -758,6 +816,28 @@ public class PlainView extends View implements TabExpander {
|
||||
return w;
|
||||
}
|
||||
|
||||
static boolean isFPMethodOverriden(String method,
|
||||
Class<?> cls,
|
||||
Class<?>[] intTypes,
|
||||
Class<?>[] fpTypes)
|
||||
{
|
||||
Module thisModule = PlainView.class.getModule();
|
||||
while (!thisModule.equals(cls.getModule())) {
|
||||
try {
|
||||
cls.getDeclaredMethod(method, fpTypes);
|
||||
return true;
|
||||
} catch (Exception e1) {
|
||||
try {
|
||||
cls.getDeclaredMethod(method, intTypes);
|
||||
return false;
|
||||
} catch (Exception e2) {
|
||||
cls = cls.getSuperclass();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// --- member variables -----------------------------------------------
|
||||
|
||||
/**
|
||||
@ -780,7 +860,7 @@ public class PlainView extends View implements TabExpander {
|
||||
Font font;
|
||||
|
||||
Segment lineBuffer;
|
||||
int tabSize;
|
||||
float tabSize;
|
||||
int tabBase;
|
||||
|
||||
int sel0;
|
||||
@ -796,4 +876,46 @@ public class PlainView extends View implements TabExpander {
|
||||
*/
|
||||
int firstLineOffset;
|
||||
|
||||
final boolean drawLineOverridden;
|
||||
final boolean drawSelectedTextOverridden;
|
||||
final boolean drawUnselectedTextOverridden;
|
||||
final boolean useFloatingPointAPI;
|
||||
|
||||
{
|
||||
final Class<?> CLS = getClass();
|
||||
final Class<?> INT = Integer.TYPE;
|
||||
final Class<?> FP = Float.TYPE;
|
||||
|
||||
drawLineOverridden = AccessController
|
||||
.doPrivileged(new PrivilegedAction<Boolean>() {
|
||||
@Override
|
||||
public Boolean run() {
|
||||
Class<?>[] intTypes = {INT, Graphics.class, INT, INT};
|
||||
Class<?>[] fpTypes = {INT, Graphics2D.class, FP, FP};
|
||||
return isFPMethodOverriden("drawLine", CLS, intTypes, fpTypes);
|
||||
}
|
||||
});
|
||||
|
||||
drawUnselectedTextOverridden = AccessController
|
||||
.doPrivileged(new PrivilegedAction<Boolean>() {
|
||||
@Override
|
||||
public Boolean run() {
|
||||
Class<?>[] intTypes = {Graphics.class, INT, INT, INT, INT};
|
||||
Class<?>[] fpTypes = {Graphics2D.class, FP, FP, INT, INT};
|
||||
return isFPMethodOverriden("drawUnselectedText", CLS, intTypes, fpTypes);
|
||||
}
|
||||
});
|
||||
|
||||
drawSelectedTextOverridden = AccessController
|
||||
.doPrivileged(new PrivilegedAction<Boolean>() {
|
||||
@Override
|
||||
public Boolean run() {
|
||||
Class<?>[] intTypes = {Graphics.class, INT, INT, INT, INT};
|
||||
Class<?>[] fpTypes = {Graphics2D.class, FP, FP, INT, INT};
|
||||
return isFPMethodOverriden("drawSelectedText", CLS, intTypes, fpTypes);
|
||||
}
|
||||
});
|
||||
|
||||
useFloatingPointAPI = drawUnselectedTextOverridden || drawSelectedTextOverridden;
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,24 +24,23 @@
|
||||
*/
|
||||
package javax.swing.text;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Shape;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.font.TextLayout;
|
||||
import java.awt.font.TextAttribute;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
import java.text.*;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.text.ParagraphView.Row;
|
||||
import sun.swing.SwingUtilities2;
|
||||
import static sun.swing.SwingUtilities2.drawChars;
|
||||
import static sun.swing.SwingUtilities2.getFontCharWidth;
|
||||
import static sun.swing.SwingUtilities2.getFontCharsWidth;
|
||||
|
||||
/**
|
||||
* A collection of methods to deal with various text
|
||||
@ -78,7 +77,11 @@ public class Utilities {
|
||||
* tabs will be expanded as a space character.
|
||||
* @param startOffset starting offset of the text in the document >= 0
|
||||
* @return the X location at the end of the rendered text
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #drawTabbedText(Segment, float, float, Graphics2D, TabExpander, int)}
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
public static final int drawTabbedText(Segment s, int x, int y, Graphics g,
|
||||
TabExpander e, int startOffset) {
|
||||
return drawTabbedText(null, s, x, y, g, e, startOffset);
|
||||
@ -96,6 +99,8 @@ public class Utilities {
|
||||
* tabs will be expanded as a space character.
|
||||
* @param startOffset starting offset of the text in the document {@code >= 0}
|
||||
* @return the X location at the end of the rendered text
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
public static final float drawTabbedText(Segment s, float x, float y,
|
||||
Graphics2D g,
|
||||
@ -138,9 +143,19 @@ public class Utilities {
|
||||
Segment s, int x, int y, Graphics g,
|
||||
TabExpander e, int startOffset,
|
||||
int [] justificationData) {
|
||||
return (int) drawTabbedText(view, s, x, y, g, e, startOffset,
|
||||
justificationData, false);
|
||||
}
|
||||
|
||||
static final float drawTabbedText(View view,
|
||||
Segment s, float x, float y, Graphics g,
|
||||
TabExpander e, int startOffset,
|
||||
int [] justificationData,
|
||||
boolean useFPAPI)
|
||||
{
|
||||
JComponent component = getJComponent(view);
|
||||
FontMetrics metrics = SwingUtilities2.getFontMetrics(component, g);
|
||||
int nextX = x;
|
||||
float nextX = x;
|
||||
char[] txt = s.array;
|
||||
int txtOffset = s.offset;
|
||||
int flushLen = 0;
|
||||
@ -174,19 +189,19 @@ public class Utilities {
|
||||
&& i <= endJustifiableContent
|
||||
)) {
|
||||
if (flushLen > 0) {
|
||||
nextX = SwingUtilities2.drawChars(component, g, txt,
|
||||
flushIndex, flushLen, x, y);
|
||||
nextX = drawChars(component, g, txt, flushIndex, flushLen, x, y);
|
||||
flushLen = 0;
|
||||
}
|
||||
flushIndex = i + 1;
|
||||
if (txt[i] == '\t') {
|
||||
if (e != null) {
|
||||
nextX = (int) e.nextTabStop((float) nextX, startOffset + i - txtOffset);
|
||||
nextX = e.nextTabStop(nextX, startOffset + i - txtOffset);
|
||||
} else {
|
||||
nextX += metrics.charWidth(' ');
|
||||
nextX += getFontCharWidth(' ', metrics, useFPAPI);
|
||||
}
|
||||
} else if (txt[i] == ' ') {
|
||||
nextX += metrics.charWidth(' ') + spaceAddon;
|
||||
float spaceWidth = getFontCharWidth(' ', metrics, useFPAPI);
|
||||
nextX += spaceWidth + spaceAddon;
|
||||
if (i <= spaceAddonLeftoverEnd) {
|
||||
nextX++;
|
||||
}
|
||||
@ -194,8 +209,8 @@ public class Utilities {
|
||||
x = nextX;
|
||||
} else if ((txt[i] == '\n') || (txt[i] == '\r')) {
|
||||
if (flushLen > 0) {
|
||||
nextX = SwingUtilities2.drawChars(component, g, txt,
|
||||
flushIndex, flushLen, x, y);
|
||||
nextX = drawChars(component, g, txt, flushIndex, flushLen,
|
||||
x, y, useFPAPI);
|
||||
flushLen = 0;
|
||||
}
|
||||
flushIndex = i + 1;
|
||||
@ -205,8 +220,7 @@ public class Utilities {
|
||||
}
|
||||
}
|
||||
if (flushLen > 0) {
|
||||
nextX = SwingUtilities2.drawChars(component, g,txt, flushIndex,
|
||||
flushLen, x, y);
|
||||
nextX = drawChars(component, g,txt, flushIndex, flushLen, x, y, useFPAPI);
|
||||
}
|
||||
return nextX;
|
||||
}
|
||||
@ -223,7 +237,11 @@ public class Utilities {
|
||||
* tabs will be expanded as a space character.
|
||||
* @param startOffset starting offset of the text in the document >= 0
|
||||
* @return the width of the text
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #getTabbedTextWidth(Segment, FontMetrics, float, TabExpander, int)}
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
public static final int getTabbedTextWidth(Segment s, FontMetrics metrics, int x,
|
||||
TabExpander e, int startOffset) {
|
||||
return getTabbedTextWidth(null, s, metrics, x, e, startOffset, null);
|
||||
@ -240,11 +258,13 @@ public class Utilities {
|
||||
* tabs will be expanded as a space character.
|
||||
* @param startOffset starting offset of the text in the document {@code >= 0}
|
||||
* @return the width of the text
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
public static final float getTabbedTextWidth(Segment s, FontMetrics metrics,
|
||||
float x, TabExpander e,
|
||||
int startOffset) {
|
||||
return getTabbedTextWidth(s, metrics, (int) x, e, startOffset);
|
||||
return getTabbedTextWidth(null, s, metrics, x, e, startOffset, null);
|
||||
}
|
||||
|
||||
// In addition to the previous method it can extend spaces for
|
||||
@ -254,10 +274,32 @@ public class Utilities {
|
||||
// one:
|
||||
// @param justificationData justificationData for the row.
|
||||
// if null not justification is needed
|
||||
static final int getTabbedTextWidth(View view, Segment s, FontMetrics metrics, int x,
|
||||
static final int getTabbedTextWidth(View view, Segment s,
|
||||
FontMetrics metrics, int x,
|
||||
TabExpander e, int startOffset,
|
||||
int[] justificationData) {
|
||||
int nextX = x;
|
||||
int[] justificationData)
|
||||
{
|
||||
return (int) getTabbedTextWidth(view, s, metrics, x, e, startOffset,
|
||||
justificationData, false);
|
||||
|
||||
}
|
||||
|
||||
static final float getTabbedTextWidth(View view, Segment s,
|
||||
FontMetrics metrics, float x,
|
||||
TabExpander e, int startOffset,
|
||||
int[] justificationData)
|
||||
{
|
||||
return getTabbedTextWidth(view, s, metrics, x, e, startOffset,
|
||||
justificationData, true);
|
||||
|
||||
}
|
||||
|
||||
static final float getTabbedTextWidth(View view, Segment s,
|
||||
FontMetrics metrics, float x,
|
||||
TabExpander e, int startOffset,
|
||||
int[] justificationData,
|
||||
boolean useFPAPI) {
|
||||
float nextX = x;
|
||||
char[] txt = s.array;
|
||||
int txtOffset = s.offset;
|
||||
int n = s.offset + s.count;
|
||||
@ -294,13 +336,13 @@ public class Utilities {
|
||||
charCount = 0;
|
||||
if (txt[i] == '\t') {
|
||||
if (e != null) {
|
||||
nextX = (int) e.nextTabStop((float) nextX,
|
||||
startOffset + i - txtOffset);
|
||||
nextX = e.nextTabStop(nextX, startOffset + i - txtOffset);
|
||||
} else {
|
||||
nextX += metrics.charWidth(' ');
|
||||
nextX += getFontCharWidth(' ', metrics, useFPAPI);
|
||||
}
|
||||
} else if (txt[i] == ' ') {
|
||||
nextX += metrics.charWidth(' ') + spaceAddon;
|
||||
float spaceWidth = getFontCharWidth(' ', metrics, useFPAPI);
|
||||
nextX += spaceWidth + spaceAddon;
|
||||
if (i <= spaceAddonLeftoverEnd) {
|
||||
nextX++;
|
||||
}
|
||||
@ -308,13 +350,15 @@ public class Utilities {
|
||||
} else if(txt[i] == '\n') {
|
||||
// Ignore newlines, they take up space and we shouldn't be
|
||||
// counting them.
|
||||
nextX += metrics.charsWidth(txt, i - charCount, charCount);
|
||||
nextX += getFontCharsWidth(txt, i - charCount, charCount,
|
||||
metrics, useFPAPI);
|
||||
charCount = 0;
|
||||
} else {
|
||||
charCount++;
|
||||
}
|
||||
}
|
||||
nextX += metrics.charsWidth(txt, n - charCount, charCount);
|
||||
nextX += getFontCharsWidth(txt, n - charCount, charCount,
|
||||
metrics, useFPAPI);
|
||||
return nextX - x;
|
||||
}
|
||||
|
||||
@ -334,7 +378,12 @@ public class Utilities {
|
||||
* tabs will be expanded as a space character.
|
||||
* @param startOffset starting offset of the text in the document >= 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 @@ public class Utilities {
|
||||
int startOffset,
|
||||
int[] justificationData) {
|
||||
return getTabbedTextOffset(view, s, metrics, x0, x, e, startOffset, true,
|
||||
justificationData);
|
||||
justificationData, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -365,13 +414,19 @@ public class Utilities {
|
||||
* @param startOffset starting offset of the text in the document >= 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 @@ public class Utilities {
|
||||
* @param startOffset starting offset of the text in the document {@code >= 0}
|
||||
* @param round whether or not to round
|
||||
* @return the offset into the text {@code >= 0}
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
public static final int getTabbedTextOffset(Segment s,
|
||||
FontMetrics metrics,
|
||||
@ -398,8 +455,8 @@ public class Utilities {
|
||||
int startOffset,
|
||||
boolean round)
|
||||
{
|
||||
return getTabbedTextOffset(null, s, metrics, (int) x0, (int) x, e,
|
||||
startOffset, round, null);
|
||||
return getTabbedTextOffset(null, s, metrics, x0, x, e,
|
||||
startOffset, round, null, true);
|
||||
}
|
||||
|
||||
// In addition to the previous method it can extend spaces for
|
||||
@ -412,15 +469,16 @@ public class Utilities {
|
||||
static final int getTabbedTextOffset(View view,
|
||||
Segment s,
|
||||
FontMetrics metrics,
|
||||
int x0, int x, TabExpander e,
|
||||
float x0, float x, TabExpander e,
|
||||
int startOffset,
|
||||
boolean round,
|
||||
int[] justificationData) {
|
||||
int[] justificationData,
|
||||
boolean useFPAPI) {
|
||||
if (x0 >= x) {
|
||||
// x before x0, return.
|
||||
return 0;
|
||||
}
|
||||
int nextX = x0;
|
||||
float nextX = x0;
|
||||
// s may be a shared segment, so it is copied prior to calling
|
||||
// the tab expander
|
||||
char[] txt = s.array;
|
||||
@ -456,19 +514,19 @@ public class Utilities {
|
||||
)){
|
||||
if (txt[i] == '\t') {
|
||||
if (e != null) {
|
||||
nextX = (int) e.nextTabStop((float) nextX,
|
||||
startOffset + i - txtOffset);
|
||||
nextX = e.nextTabStop(nextX, startOffset + i - txtOffset);
|
||||
} else {
|
||||
nextX += metrics.charWidth(' ');
|
||||
nextX += getFontCharWidth(' ', metrics, useFPAPI);
|
||||
}
|
||||
} else if (txt[i] == ' ') {
|
||||
nextX += metrics.charWidth(' ') + spaceAddon;
|
||||
nextX += getFontCharWidth(' ', metrics, useFPAPI);
|
||||
nextX += spaceAddon;
|
||||
if (i <= spaceAddonLeftoverEnd) {
|
||||
nextX++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nextX += metrics.charWidth(txt[i]);
|
||||
nextX += getFontCharWidth(txt[i], metrics, useFPAPI);
|
||||
}
|
||||
if (x < nextX) {
|
||||
// found the hit position... return the appropriate side
|
||||
@ -480,12 +538,15 @@ public class Utilities {
|
||||
if (round) {
|
||||
offset = i + 1 - txtOffset;
|
||||
|
||||
int width = metrics.charsWidth(txt, txtOffset, offset);
|
||||
int span = x - x0;
|
||||
float width = getFontCharsWidth(txt, txtOffset, offset,
|
||||
metrics, useFPAPI);
|
||||
float span = x - x0;
|
||||
|
||||
if (span < width) {
|
||||
while (offset > 0) {
|
||||
int nextWidth = offset > 1 ? metrics.charsWidth(txt, txtOffset, offset - 1) : 0;
|
||||
float charsWidth = getFontCharsWidth(txt, txtOffset,
|
||||
offset - 1, metrics, useFPAPI);
|
||||
float nextWidth = offset > 1 ? charsWidth : 0;
|
||||
|
||||
if (span >= nextWidth) {
|
||||
if (span - nextWidth < width - span) {
|
||||
@ -502,7 +563,9 @@ public class Utilities {
|
||||
} else {
|
||||
offset = i - txtOffset;
|
||||
|
||||
while (offset > 0 && metrics.charsWidth(txt, txtOffset, offset) > (x - x0)) {
|
||||
while (offset > 0 && getFontCharsWidth(txt, txtOffset, offset,
|
||||
metrics, useFPAPI)
|
||||
> (x - x0)) {
|
||||
offset--;
|
||||
}
|
||||
}
|
||||
@ -528,15 +591,26 @@ public class Utilities {
|
||||
* tabs will be expanded as a space character.
|
||||
* @param startOffset starting offset in the document of the text
|
||||
* @return the offset into the given text
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #getBreakLocation(Segment, FontMetrics, float, float,
|
||||
* TabExpander, int)}
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
public static final int getBreakLocation(Segment s, FontMetrics metrics,
|
||||
int x0, int x, TabExpander e,
|
||||
int startOffset) {
|
||||
return getBreakLocation(s, metrics, x0, x, e, startOffset, false);
|
||||
}
|
||||
|
||||
static final int getBreakLocation(Segment s, FontMetrics metrics,
|
||||
float x0, float x, TabExpander e,
|
||||
int startOffset, boolean useFPIAPI) {
|
||||
char[] txt = s.array;
|
||||
int txtOffset = s.offset;
|
||||
int txtCount = s.count;
|
||||
int index = Utilities.getTabbedTextOffset(s, metrics, x0, x,
|
||||
e, startOffset, false);
|
||||
int index = getTabbedTextOffset(null, s, metrics, x0, x, e, startOffset,
|
||||
false, null, useFPIAPI);
|
||||
|
||||
if (index >= txtCount - 1) {
|
||||
return txtCount;
|
||||
@ -577,11 +651,13 @@ public class Utilities {
|
||||
* tabs will be expanded as a space character.
|
||||
* @param startOffset starting offset in the document of the text
|
||||
* @return the offset into the given text
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
public static final int getBreakLocation(Segment s, FontMetrics metrics,
|
||||
float x0, float x, TabExpander e,
|
||||
int startOffset) {
|
||||
return getBreakLocation(s, metrics, (int) x0, (int) x, e, startOffset);
|
||||
return getBreakLocation(s, metrics, x0, x, e, startOffset, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -627,16 +703,16 @@ public class Utilities {
|
||||
* @exception BadLocationException if the offset is out of range
|
||||
*/
|
||||
public static final int getRowEnd(JTextComponent c, int offs) throws BadLocationException {
|
||||
Rectangle r = c.modelToView(offs);
|
||||
Rectangle2D r = c.modelToView2D(offs);
|
||||
if (r == null) {
|
||||
return -1;
|
||||
}
|
||||
int n = c.getDocument().getLength();
|
||||
int lastOffs = offs;
|
||||
int y = r.y;
|
||||
while ((r != null) && (y == r.y)) {
|
||||
double y = r.getY();
|
||||
while ((r != null) && (y == r.getY())) {
|
||||
// Skip invisible elements
|
||||
if (r.height !=0) {
|
||||
if (r.getHeight() !=0) {
|
||||
offs = lastOffs;
|
||||
}
|
||||
lastOffs += 1;
|
||||
@ -657,27 +733,44 @@ public class Utilities {
|
||||
* @return the position >= 0 if the request can be computed, otherwise
|
||||
* a value of -1 will be returned.
|
||||
* @exception BadLocationException if the offset is out of range
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #getPositionAbove(JTextComponent, int, float)}
|
||||
*/
|
||||
public static final int getPositionAbove(JTextComponent c, int offs, int x) throws BadLocationException {
|
||||
@Deprecated(since = "9")
|
||||
public static final int getPositionAbove(JTextComponent c, int offs, int x)
|
||||
throws BadLocationException
|
||||
{
|
||||
return getPositionAbove(c, offs, x, false);
|
||||
}
|
||||
|
||||
static final int getPositionAbove(JTextComponent c, int offs, float x,
|
||||
boolean useFPAPI) throws BadLocationException
|
||||
{
|
||||
int lastOffs = getRowStart(c, offs) - 1;
|
||||
if (lastOffs < 0) {
|
||||
return -1;
|
||||
}
|
||||
int bestSpan = Integer.MAX_VALUE;
|
||||
int y = 0;
|
||||
Rectangle r = null;
|
||||
double bestSpan = Integer.MAX_VALUE;
|
||||
double y = 0;
|
||||
Rectangle2D r = null;
|
||||
if (lastOffs >= 0) {
|
||||
r = c.modelToView(lastOffs);
|
||||
y = r.y;
|
||||
r = useFPAPI ? c.modelToView2D(lastOffs) : c.modelToView(lastOffs);
|
||||
y = r.getY();
|
||||
}
|
||||
while ((r != null) && (y == r.y)) {
|
||||
int span = Math.abs(r.x - x);
|
||||
while ((r != null) && (y == r.getY())) {
|
||||
double span = Math.abs(r.getX() - x);
|
||||
if (span < bestSpan) {
|
||||
offs = lastOffs;
|
||||
bestSpan = span;
|
||||
}
|
||||
lastOffs -= 1;
|
||||
r = (lastOffs >= 0) ? c.modelToView(lastOffs) : null;
|
||||
|
||||
if ((lastOffs >= 0)) {
|
||||
r = useFPAPI ? c.modelToView2D(lastOffs) : c.modelToView(lastOffs);
|
||||
} else {
|
||||
r = null;
|
||||
}
|
||||
}
|
||||
return offs;
|
||||
}
|
||||
@ -694,10 +787,12 @@ public class Utilities {
|
||||
* @return the position {@code >= 0} if the request can be computed, otherwise
|
||||
* a value of -1 will be returned.
|
||||
* @exception BadLocationException if the offset is out of range
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
public static final int getPositionAbove(JTextComponent c, int offs, float x)
|
||||
throws BadLocationException {
|
||||
return getPositionAbove(c, offs, (int) x);
|
||||
return getPositionAbove(c, offs, x, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -712,28 +807,45 @@ public class Utilities {
|
||||
* @return the position >= 0 if the request can be computed, otherwise
|
||||
* a value of -1 will be returned.
|
||||
* @exception BadLocationException if the offset is out of range
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #getPositionBelow(JTextComponent, int, float)}
|
||||
*/
|
||||
public static final int getPositionBelow(JTextComponent c, int offs, int x) throws BadLocationException {
|
||||
@Deprecated(since = "9")
|
||||
public static final int getPositionBelow(JTextComponent c, int offs, int x)
|
||||
throws BadLocationException
|
||||
{
|
||||
return getPositionBelow(c, offs, x, false);
|
||||
}
|
||||
|
||||
static final int getPositionBelow(JTextComponent c, int offs, float x,
|
||||
boolean useFPAPI) throws BadLocationException
|
||||
{
|
||||
int lastOffs = getRowEnd(c, offs) + 1;
|
||||
if (lastOffs <= 0) {
|
||||
return -1;
|
||||
}
|
||||
int bestSpan = Integer.MAX_VALUE;
|
||||
double bestSpan = Integer.MAX_VALUE;
|
||||
int n = c.getDocument().getLength();
|
||||
int y = 0;
|
||||
Rectangle r = null;
|
||||
double y = 0;
|
||||
Rectangle2D r = null;
|
||||
if (lastOffs <= n) {
|
||||
r = c.modelToView(lastOffs);
|
||||
y = r.y;
|
||||
r = useFPAPI ? c.modelToView2D(lastOffs) : c.modelToView(lastOffs);
|
||||
y = r.getY();
|
||||
}
|
||||
while ((r != null) && (y == r.y)) {
|
||||
int span = Math.abs(x - r.x);
|
||||
while ((r != null) && (y == r.getY())) {
|
||||
double span = Math.abs(x - r.getX());
|
||||
if (span < bestSpan) {
|
||||
offs = lastOffs;
|
||||
bestSpan = span;
|
||||
}
|
||||
lastOffs += 1;
|
||||
r = (lastOffs <= n) ? c.modelToView(lastOffs) : null;
|
||||
|
||||
if (lastOffs <= n) {
|
||||
r = useFPAPI ? c.modelToView2D(lastOffs) : c.modelToView(lastOffs);
|
||||
} else {
|
||||
r = null;
|
||||
}
|
||||
}
|
||||
return offs;
|
||||
}
|
||||
@ -750,10 +862,12 @@ public class Utilities {
|
||||
* @return the position {@code >= 0} if the request can be computed, otherwise
|
||||
* a value of -1 will be returned.
|
||||
* @exception BadLocationException if the offset is out of range
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
public static final int getPositionBelow(JTextComponent c, int offs, float x)
|
||||
throws BadLocationException {
|
||||
return getPositionBelow(c, offs, (int) x);
|
||||
return getPositionBelow(c, offs, x, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1029,7 +1143,23 @@ public class Utilities {
|
||||
*/
|
||||
static int drawComposedText(View view, AttributeSet attr, Graphics g,
|
||||
int x, int y, int p0, int p1)
|
||||
throws BadLocationException {
|
||||
throws BadLocationException
|
||||
{
|
||||
return (int) drawComposedText(view, attr, g, x, y, p0, p1, false);
|
||||
}
|
||||
|
||||
static float drawComposedText(View view, AttributeSet attr, Graphics g,
|
||||
float x, float y, int p0, int p1)
|
||||
throws BadLocationException
|
||||
{
|
||||
return drawComposedText(view, attr, g, x, y, p0, p1, true);
|
||||
}
|
||||
|
||||
static float drawComposedText(View view, AttributeSet attr, Graphics g,
|
||||
float x, float y, int p0, int p1,
|
||||
boolean useFPAPI)
|
||||
throws BadLocationException
|
||||
{
|
||||
Graphics2D g2d = (Graphics2D)g;
|
||||
AttributedString as = (AttributedString)attr.getAttribute(
|
||||
StyleConstants.ComposedTextAttribute);
|
||||
@ -1039,8 +1169,7 @@ public class Utilities {
|
||||
return x;
|
||||
|
||||
AttributedCharacterIterator aci = as.getIterator(null, p0, p1);
|
||||
return x + (int)SwingUtilities2.drawString(
|
||||
getJComponent(view), g2d,aci,x,y);
|
||||
return x + SwingUtilities2.drawString(getJComponent(view), g2d, aci, x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -25,8 +25,12 @@
|
||||
package javax.swing.text;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import javax.swing.event.*;
|
||||
import static javax.swing.text.PlainView.isFPMethodOverriden;
|
||||
|
||||
/**
|
||||
* View of plain text (text with only one font and color)
|
||||
@ -86,17 +90,6 @@ public class WrappedPlainView extends BoxView implements TabExpander {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tab size set for the document, defaulting to 8.
|
||||
*
|
||||
* @implSpec This implementation calls {@link #getTabSize() getTabSize()}.
|
||||
*
|
||||
* @return the tab size
|
||||
*/
|
||||
protected float getFractionalTabSize() {
|
||||
return getTabSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a line of text, suppressing whitespace at the end
|
||||
* and expanding any tabs. This is implemented to make calls
|
||||
@ -111,8 +104,17 @@ public class WrappedPlainView extends BoxView implements TabExpander {
|
||||
* @param y the starting Y position >= 0
|
||||
* @see #drawUnselectedText
|
||||
* @see #drawSelectedText
|
||||
*
|
||||
* @deprecated replaced by
|
||||
* {@link #drawLine(int, int, Graphics2D, float, float)}
|
||||
*/
|
||||
@Deprecated(since = "9")
|
||||
protected void drawLine(int p0, int p1, Graphics g, int x, int y) {
|
||||
drawLineImpl(p0, p1, g, x, y, false);
|
||||
}
|
||||
|
||||
private void drawLineImpl(int p0, int p1, Graphics g, float x, float y,
|
||||
boolean useFPAPI) {
|
||||
Element lineMap = getElement();
|
||||
Element line = lineMap.getElement(lineMap.getElementIndex(p0));
|
||||
Element elem;
|
||||
@ -143,10 +145,6 @@ public class WrappedPlainView extends BoxView implements TabExpander {
|
||||
* <code>drawSelectedText</code> so that the way selected and
|
||||
* unselected text are rendered can be customized.
|
||||
*
|
||||
* @implSpec This implementation calls
|
||||
* {@link #drawLine(int, int, Graphics, int, int)
|
||||
* drawLine(p0, p1, (Graphics) g, (int) x, (int) y)}.
|
||||
*
|
||||
* @param p0 the starting document location to use >= 0
|
||||
* @param p1 the ending document location to use >= p1
|
||||
* @param g the graphics context
|
||||
@ -154,12 +152,17 @@ public class WrappedPlainView extends BoxView implements TabExpander {
|
||||
* @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 @@ public class WrappedPlainView extends BoxView implements TabExpander {
|
||||
} else {
|
||||
if (sel0 == sel1 || selected == unselected) {
|
||||
// no selection, or it is invisible
|
||||
x = drawUnselectedText(g, x, y, p0, p1);
|
||||
x = callDrawUnselectedText(g, x, y, p0, p1);
|
||||
} else if ((p0 >= sel0 && p0 <= sel1) && (p1 >= sel0 && p1 <= sel1)) {
|
||||
x = drawSelectedText(g, x, y, p0, p1);
|
||||
x = callDrawSelectedText(g, x, y, p0, p1);
|
||||
} else if (sel0 >= p0 && sel0 <= p1) {
|
||||
if (sel1 >= p0 && sel1 <= p1) {
|
||||
x = drawUnselectedText(g, x, y, p0, sel0);
|
||||
x = drawSelectedText(g, x, y, sel0, sel1);
|
||||
x = drawUnselectedText(g, x, y, sel1, p1);
|
||||
x = callDrawUnselectedText(g, x, y, p0, sel0);
|
||||
x = callDrawSelectedText(g, x, y, sel0, sel1);
|
||||
x = callDrawUnselectedText(g, x, y, sel1, p1);
|
||||
} else {
|
||||
x = drawUnselectedText(g, x, y, p0, sel0);
|
||||
x = drawSelectedText(g, x, y, sel0, p1);
|
||||
x = callDrawUnselectedText(g, x, y, p0, sel0);
|
||||
x = callDrawSelectedText(g, x, y, sel0, p1);
|
||||
}
|
||||
} else if (sel1 >= p0 && sel1 <= p1) {
|
||||
x = drawSelectedText(g, x, y, p0, sel1);
|
||||
x = drawUnselectedText(g, x, y, sel1, p1);
|
||||
x = callDrawSelectedText(g, x, y, p0, sel1);
|
||||
x = callDrawUnselectedText(g, x, y, sel1, p1);
|
||||
} else {
|
||||
x = drawUnselectedText(g, x, y, p0, p1);
|
||||
x = callDrawUnselectedText(g, x, y, p0, p1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,14 +208,36 @@ public class WrappedPlainView extends BoxView implements TabExpander {
|
||||
* @param p1 the ending position in the model >= 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 @@ public class WrappedPlainView extends BoxView implements TabExpander {
|
||||
* Renders the given range in the model as normal unselected
|
||||
* text.
|
||||
*
|
||||
* @implSpec This implementation calls
|
||||
* {@link #drawUnselectedText(Graphics, int, int, int, int)
|
||||
* drawUnselectedText((Graphics)g, (int) x, (int) y, p0, p1)}.
|
||||
*
|
||||
* @param g the graphics context
|
||||
* @param x the starting X coordinate >= 0
|
||||
* @param y the starting Y coordinate >= 0
|
||||
@ -232,10 +253,12 @@ public class WrappedPlainView extends BoxView implements TabExpander {
|
||||
* @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 @@ public class WrappedPlainView extends BoxView implements TabExpander {
|
||||
* @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 @@ public class WrappedPlainView extends BoxView implements TabExpander {
|
||||
* the hosting component. It assumes the highlighter will render
|
||||
* the selected background.
|
||||
*
|
||||
* @implSpec This implementation calls
|
||||
* {@link #drawSelectedText(Graphics, int, int, int, int)
|
||||
* drawSelectedText((Graphics)g, (int) x, (int) y, p0, p1)}.
|
||||
*
|
||||
* @param g the graphics context
|
||||
* @param x the starting X coordinate >= 0
|
||||
* @param y the starting Y coordinate >= 0
|
||||
@ -279,10 +321,12 @@ public class WrappedPlainView extends BoxView implements TabExpander {
|
||||
* @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 @@ public class WrappedPlainView extends BoxView implements TabExpander {
|
||||
Component host = getContainer();
|
||||
Font f = host.getFont();
|
||||
metrics = host.getFontMetrics(f);
|
||||
tabSize = getTabSize() * metrics.charWidth('m');
|
||||
if (useFloatingPointAPI) {
|
||||
FontRenderContext frc = metrics.getFontRenderContext();
|
||||
float tabWidth = (float) f.getStringBounds("m", frc).getWidth();
|
||||
tabSize = getTabSize() * tabWidth;
|
||||
} else {
|
||||
tabSize = getTabSize() * metrics.charWidth('m');
|
||||
}
|
||||
}
|
||||
|
||||
// --- TabExpander methods ------------------------------------------
|
||||
@ -413,7 +463,7 @@ public class WrappedPlainView extends BoxView implements TabExpander {
|
||||
public float nextTabStop(float x, int tabOffset) {
|
||||
if (tabSize == 0)
|
||||
return x;
|
||||
int ntabs = ((int) x - tabBase) / tabSize;
|
||||
float ntabs = (x - tabBase) / tabSize;
|
||||
return tabBase + ((ntabs + 1) * tabSize);
|
||||
}
|
||||
|
||||
@ -591,7 +641,7 @@ public class WrappedPlainView extends BoxView implements TabExpander {
|
||||
Segment lineBuffer;
|
||||
boolean widthChanging;
|
||||
int tabBase;
|
||||
int tabSize;
|
||||
float tabSize;
|
||||
boolean wordWrap;
|
||||
|
||||
int sel0;
|
||||
@ -668,6 +718,7 @@ public class WrappedPlainView extends BoxView implements TabExpander {
|
||||
int end = getEndOffset();
|
||||
int p0 = start;
|
||||
int[] lineEnds = getLineEnds();
|
||||
boolean useDrawLineFP = drawLineOverridden && g instanceof Graphics2D;
|
||||
for (int i = 0; i < lineCount; i++) {
|
||||
int p1 = (lineEnds == null) ? end :
|
||||
start + lineEnds[i];
|
||||
@ -677,8 +728,11 @@ public class WrappedPlainView extends BoxView implements TabExpander {
|
||||
: p1;
|
||||
dh.paintLayeredHighlights(g, p0, hOffset, a, host, this);
|
||||
}
|
||||
drawLine(p0, p1, g, x, y);
|
||||
|
||||
if (useDrawLineFP) {
|
||||
drawLine(p0, p1, (Graphics2D) g, (float) x, (float) y);
|
||||
} else {
|
||||
drawLine(p0, p1, g, x, y);
|
||||
}
|
||||
p0 = p1;
|
||||
y += metrics.getHeight();
|
||||
}
|
||||
@ -929,4 +983,47 @@ public class WrappedPlainView extends BoxView implements TabExpander {
|
||||
int lineCount;
|
||||
SoftReference<int[]> lineCache = null;
|
||||
}
|
||||
|
||||
private final boolean drawLineOverridden;
|
||||
private final boolean drawSelectedTextOverridden;
|
||||
private final boolean drawUnselectedTextOverridden;
|
||||
private final boolean useFloatingPointAPI;
|
||||
|
||||
{
|
||||
final Class<?> CLS = getClass();
|
||||
final Class<?> INT = Integer.TYPE;
|
||||
final Class<?> FP = Float.TYPE;
|
||||
|
||||
drawLineOverridden = AccessController
|
||||
.doPrivileged(new PrivilegedAction<Boolean>() {
|
||||
@Override
|
||||
public Boolean run() {
|
||||
Class<?>[] intTypes = {INT, INT, Graphics.class, INT, INT};
|
||||
Class<?>[] fpTypes = {INT, INT, Graphics2D.class, FP, FP};
|
||||
return isFPMethodOverriden("drawLine", CLS, intTypes, fpTypes);
|
||||
}
|
||||
});
|
||||
|
||||
drawUnselectedTextOverridden = AccessController
|
||||
.doPrivileged(new PrivilegedAction<Boolean>() {
|
||||
@Override
|
||||
public Boolean run() {
|
||||
Class<?>[] intTypes = {Graphics.class, INT, INT, INT, INT};
|
||||
Class<?>[] fpTypes = {Graphics2D.class, FP, FP, INT, INT};
|
||||
return isFPMethodOverriden("drawUnselectedText", CLS, intTypes, fpTypes);
|
||||
}
|
||||
});
|
||||
|
||||
drawSelectedTextOverridden = AccessController
|
||||
.doPrivileged(new PrivilegedAction<Boolean>() {
|
||||
@Override
|
||||
public Boolean run() {
|
||||
Class<?>[] intTypes = {Graphics.class, INT, INT, INT, INT};
|
||||
Class<?>[] fpTypes = {Graphics2D.class, FP, FP, INT, INT};
|
||||
return isFPMethodOverriden("drawSelectedText", CLS, intTypes, fpTypes);
|
||||
}
|
||||
});
|
||||
|
||||
useFloatingPointAPI = drawUnselectedTextOverridden || drawSelectedTextOverridden;
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,6 +30,7 @@ import java.awt.*;
|
||||
import static java.awt.RenderingHints.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.font.*;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import static java.awt.geom.AffineTransform.TYPE_FLIP;
|
||||
import static java.awt.geom.AffineTransform.TYPE_TRANSLATION;
|
||||
@ -723,10 +724,31 @@ public class SwingUtilities2 {
|
||||
int length,
|
||||
int x,
|
||||
int y) {
|
||||
return (int) drawChars(c, g, data, offset, length, x, y, false);
|
||||
}
|
||||
|
||||
public static float drawChars(JComponent c, Graphics g,
|
||||
char[] data,
|
||||
int offset,
|
||||
int length,
|
||||
float x,
|
||||
float y) {
|
||||
return drawChars(c, g, data, offset, length, x, y, true);
|
||||
}
|
||||
|
||||
public static float drawChars(JComponent c, Graphics g,
|
||||
char[] data,
|
||||
int offset,
|
||||
int length,
|
||||
float x,
|
||||
float y,
|
||||
boolean useFPAPI) {
|
||||
if ( length <= 0 ) { //no need to paint empty strings
|
||||
return x;
|
||||
}
|
||||
int nextX = x + getFontMetrics(c, g).charsWidth(data, offset, length);
|
||||
float nextX = x + getFontCharsWidth(data, offset, length,
|
||||
getFontMetrics(c, g),
|
||||
useFPAPI);
|
||||
if (isPrinting(g)) {
|
||||
Graphics2D g2d = getGraphics2D(g);
|
||||
if (g2d != null) {
|
||||
@ -766,8 +788,14 @@ public class SwingUtilities2 {
|
||||
Object aaHint = (c == null)
|
||||
? null
|
||||
: c.getClientProperty(KEY_TEXT_ANTIALIASING);
|
||||
if (aaHint != null && (g instanceof Graphics2D)) {
|
||||
Graphics2D g2 = (Graphics2D)g;
|
||||
|
||||
if (!(g instanceof Graphics2D)) {
|
||||
g.drawChars(data, offset, length, (int) x, (int) y);
|
||||
return nextX;
|
||||
}
|
||||
|
||||
Graphics2D g2 = (Graphics2D) g;
|
||||
if (aaHint != null) {
|
||||
|
||||
Object oldContrast = null;
|
||||
Object oldAAValue = g2.getRenderingHint(KEY_TEXT_ANTIALIASING);
|
||||
@ -788,7 +816,7 @@ public class SwingUtilities2 {
|
||||
}
|
||||
}
|
||||
|
||||
g.drawChars(data, offset, length, x, y);
|
||||
g2.drawString(new String(data, offset, length), x, y);
|
||||
|
||||
if (oldAAValue != null) {
|
||||
g2.setRenderingHint(KEY_TEXT_ANTIALIASING, oldAAValue);
|
||||
@ -798,19 +826,59 @@ public class SwingUtilities2 {
|
||||
}
|
||||
}
|
||||
else {
|
||||
g.drawChars(data, offset, length, x, y);
|
||||
g2.drawString(new String(data, offset, length), x, y);
|
||||
}
|
||||
return nextX;
|
||||
}
|
||||
|
||||
public static float getFontCharWidth(char c, FontMetrics fm,
|
||||
boolean useFPAPI)
|
||||
{
|
||||
return getFontCharsWidth(new char[]{c}, 0, 1, fm, useFPAPI);
|
||||
}
|
||||
|
||||
public static float getFontCharsWidth(char[] data, int offset, int len,
|
||||
FontMetrics fm,
|
||||
boolean useFPAPI)
|
||||
{
|
||||
return len == 0 ? 0 : getFontStringWidth(new String(data, offset, len),
|
||||
fm, useFPAPI);
|
||||
}
|
||||
|
||||
public static float getFontStringWidth(String data, FontMetrics fm,
|
||||
boolean useFPAPI)
|
||||
{
|
||||
if (useFPAPI) {
|
||||
Rectangle2D bounds = fm.getFont()
|
||||
.getStringBounds(data, fm.getFontRenderContext());
|
||||
return (float) bounds.getWidth();
|
||||
} else {
|
||||
return fm.stringWidth(data);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* see documentation for drawChars
|
||||
* returns the advance
|
||||
*/
|
||||
public static float drawString(JComponent c, Graphics g,
|
||||
AttributedCharacterIterator iterator,
|
||||
int x,
|
||||
int y) {
|
||||
int x, int y)
|
||||
{
|
||||
return drawStringImpl(c, g, iterator, x, y);
|
||||
}
|
||||
|
||||
public static float drawString(JComponent c, Graphics g,
|
||||
AttributedCharacterIterator iterator,
|
||||
float x, float y)
|
||||
{
|
||||
return drawStringImpl(c, g, iterator, x, y);
|
||||
}
|
||||
|
||||
private static float drawStringImpl(JComponent c, Graphics g,
|
||||
AttributedCharacterIterator iterator,
|
||||
float x, float y)
|
||||
{
|
||||
|
||||
float retVal;
|
||||
boolean isPrinting = isPrinting(g);
|
||||
@ -825,8 +893,8 @@ public class SwingUtilities2 {
|
||||
|
||||
Graphics2D g2d = getGraphics2D(g);
|
||||
if (g2d == null) {
|
||||
g.drawString(iterator,x,y); //for the cases where advance
|
||||
//matters it should not happen
|
||||
g.drawString(iterator, (int)x, (int)y); //for the cases where advance
|
||||
//matters it should not happen
|
||||
retVal = x;
|
||||
|
||||
} else {
|
||||
|
||||
@ -41,13 +41,13 @@ import javax.swing.plaf.TreeUI;
|
||||
|
||||
import javax.accessibility.*;
|
||||
import com.sun.java.accessibility.util.*;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.AppContext;
|
||||
import sun.awt.SunToolkit;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
/*
|
||||
* Note: This class has to be public. It's loaded from the VM like this:
|
||||
@ -1754,7 +1754,7 @@ final public class AccessBridge {
|
||||
if (child instanceof JTextComponent) {
|
||||
JTextComponent text = (JTextComponent) child;
|
||||
try {
|
||||
r = text.modelToView(text.getCaretPosition());
|
||||
r = text.modelToView2D(text.getCaretPosition()).getBounds();
|
||||
if (r != null) {
|
||||
Point p = text.getLocationOnScreen();
|
||||
r.translate(p.x, p.y);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
499
jdk/test/javax/swing/text/View/8156217/FPMethodCalledTest.java
Normal file
499
jdk/test/javax/swing/text/View/8156217/FPMethodCalledTest.java
Normal file
@ -0,0 +1,499 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Robot;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPasswordField;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.metal.MetalLookAndFeel;
|
||||
import javax.swing.plaf.metal.MetalTextFieldUI;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Element;
|
||||
import javax.swing.text.PasswordView;
|
||||
import javax.swing.text.PlainView;
|
||||
import javax.swing.text.View;
|
||||
import javax.swing.text.WrappedPlainView;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8156217
|
||||
* @key headful
|
||||
* @summary Selected text is shifted on HiDPI display
|
||||
* @run main FPMethodCalledTest
|
||||
*/
|
||||
public class FPMethodCalledTest {
|
||||
|
||||
private static JFrame frame;
|
||||
private static JTextField textField;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
for (Test test : TESTS) {
|
||||
test(test);
|
||||
}
|
||||
}
|
||||
|
||||
static void test(final Test test) throws Exception {
|
||||
try {
|
||||
Robot robot = new Robot();
|
||||
robot.setAutoDelay(50);
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
createAndShowGUI(test);
|
||||
});
|
||||
|
||||
robot.waitForIdle();
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
textField.select(1, 3);
|
||||
});
|
||||
|
||||
robot.waitForIdle();
|
||||
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
Resultable resultable = test.resultable;
|
||||
if (!resultable.getResult()) {
|
||||
throw new RuntimeException("Test fails for: " + resultable);
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
SwingUtilities.invokeAndWait(() -> {
|
||||
if (frame != null) {
|
||||
frame.dispose();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void createAndShowGUI(Test test) {
|
||||
|
||||
try {
|
||||
UIManager.setLookAndFeel(new MetalLookAndFeel());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
frame = new JFrame();
|
||||
frame.setSize(300, 300);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
|
||||
JPanel panel = new JPanel(new FlowLayout());
|
||||
|
||||
String text = "AAAAAAA";
|
||||
textField = test.isPasswordField()
|
||||
? new JPasswordField(text)
|
||||
: new JTextField(text);
|
||||
|
||||
textField.setUI(new MetalTextFieldUI() {
|
||||
|
||||
@Override
|
||||
public View create(Element elem) {
|
||||
return test.createView(elem);
|
||||
}
|
||||
});
|
||||
|
||||
panel.add(textField);
|
||||
frame.getContentPane().add(panel);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
private static final Test[] TESTS = {
|
||||
new Test() {
|
||||
@Override
|
||||
View createView(Element elem) {
|
||||
PlainViewINTAPI view = new PlainViewINTAPI(elem);
|
||||
resultable = view;
|
||||
return view;
|
||||
}
|
||||
},
|
||||
new Test() {
|
||||
@Override
|
||||
View createView(Element elem) {
|
||||
PlainViewFPAPI view = new PlainViewFPAPI(elem);
|
||||
resultable = view;
|
||||
return view;
|
||||
}
|
||||
},
|
||||
new Test() {
|
||||
@Override
|
||||
View createView(Element elem) {
|
||||
PlainViewMixedAPI view = new PlainViewMixedAPI(elem);
|
||||
resultable = view;
|
||||
return view;
|
||||
}
|
||||
},
|
||||
new Test() {
|
||||
@Override
|
||||
View createView(Element elem) {
|
||||
WrappedPlainViewINTAPI view = new WrappedPlainViewINTAPI(elem);
|
||||
resultable = view;
|
||||
return view;
|
||||
}
|
||||
},
|
||||
new Test() {
|
||||
@Override
|
||||
View createView(Element elem) {
|
||||
WrappedPlainViewFPAPI view = new WrappedPlainViewFPAPI(elem);
|
||||
resultable = view;
|
||||
return view;
|
||||
}
|
||||
},
|
||||
new Test() {
|
||||
@Override
|
||||
View createView(Element elem) {
|
||||
WrappedPlainViewMixedAPI view = new WrappedPlainViewMixedAPI(elem);
|
||||
resultable = view;
|
||||
return view;
|
||||
}
|
||||
},
|
||||
new Test(true) {
|
||||
|
||||
@Override
|
||||
View createView(Element elem) {
|
||||
PasswordViewINTAPI view = new PasswordViewINTAPI(elem);
|
||||
resultable = view;
|
||||
return view;
|
||||
}
|
||||
},
|
||||
new Test(true) {
|
||||
|
||||
@Override
|
||||
View createView(Element elem) {
|
||||
PasswordViewFPAPI view = new PasswordViewFPAPI(elem);
|
||||
resultable = view;
|
||||
return view;
|
||||
}
|
||||
},
|
||||
new Test(true) {
|
||||
|
||||
@Override
|
||||
View createView(Element elem) {
|
||||
PasswordViewMixedAPI view = new PasswordViewMixedAPI(elem);
|
||||
resultable = view;
|
||||
return view;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static interface Resultable {
|
||||
|
||||
boolean getResult();
|
||||
}
|
||||
|
||||
static abstract class Test {
|
||||
|
||||
Resultable resultable;
|
||||
final boolean isPasswordField;
|
||||
|
||||
public Test() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public Test(boolean isPasswordField) {
|
||||
this.isPasswordField = isPasswordField;
|
||||
}
|
||||
|
||||
boolean isPasswordField() {
|
||||
return isPasswordField;
|
||||
}
|
||||
|
||||
abstract View createView(Element elem);
|
||||
}
|
||||
|
||||
static class PlainViewINTAPI extends PlainView implements Resultable {
|
||||
|
||||
boolean drawLine = false;
|
||||
boolean drawSelected = false;
|
||||
boolean drawUnselected = false;
|
||||
|
||||
public PlainViewINTAPI(Element elem) {
|
||||
super(elem);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawLine(int lineIndex, Graphics g, int x, int y) {
|
||||
drawLine = true;
|
||||
super.drawLine(lineIndex, g, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int drawSelectedText(Graphics g, int x, int y,
|
||||
int p0, int p1) throws BadLocationException {
|
||||
drawSelected = true;
|
||||
return super.drawSelectedText(g, x, y, p0, p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int drawUnselectedText(Graphics g, int x, int y,
|
||||
int p0, int p1) throws BadLocationException {
|
||||
drawUnselected = true;
|
||||
return super.drawUnselectedText(g, x, y, p0, p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getResult() {
|
||||
return drawLine && drawSelected && drawUnselected;
|
||||
}
|
||||
}
|
||||
|
||||
static class PlainViewFPAPI extends PlainView implements Resultable {
|
||||
|
||||
boolean drawLine = false;
|
||||
boolean drawSelected = false;
|
||||
boolean drawUnselected = false;
|
||||
|
||||
public PlainViewFPAPI(Element elem) {
|
||||
super(elem);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawLine(int lineIndex, Graphics2D g, float x, float y) {
|
||||
drawLine = true;
|
||||
super.drawLine(lineIndex, g, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float drawSelectedText(Graphics2D g, float x, float y,
|
||||
int p0, int p1) throws BadLocationException {
|
||||
drawSelected = true;
|
||||
return super.drawSelectedText(g, x, y, p0, p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float drawUnselectedText(Graphics2D g, float x, float y,
|
||||
int p0, int p1) throws BadLocationException {
|
||||
drawUnselected = true;
|
||||
return super.drawUnselectedText(g, x, y, p0, p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getResult() {
|
||||
return drawSelected;
|
||||
}
|
||||
}
|
||||
|
||||
static class PlainViewMixedAPI extends PlainView implements Resultable {
|
||||
|
||||
boolean isIntMethodCalled = false;
|
||||
boolean isFPMethodCalled = false;
|
||||
|
||||
public PlainViewMixedAPI(Element elem) {
|
||||
super(elem);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int drawSelectedText(Graphics g, int x, int y,
|
||||
int p0, int p1) throws BadLocationException {
|
||||
isIntMethodCalled = true;
|
||||
return super.drawSelectedText(g, x, y, p0, p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float drawSelectedText(Graphics2D g, float x, float y,
|
||||
int p0, int p1) throws BadLocationException {
|
||||
isFPMethodCalled = true;
|
||||
return super.drawSelectedText(g, x, y, p0, p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getResult() {
|
||||
return !isIntMethodCalled && isFPMethodCalled;
|
||||
}
|
||||
}
|
||||
|
||||
static class WrappedPlainViewINTAPI extends WrappedPlainView implements Resultable {
|
||||
|
||||
boolean drawLine = false;
|
||||
boolean drawSelected = false;
|
||||
boolean drawUnselected = false;
|
||||
|
||||
public WrappedPlainViewINTAPI(Element elem) {
|
||||
super(elem);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawLine(int p0, int p1, Graphics g, int x, int y) {
|
||||
drawLine = true;
|
||||
super.drawLine(p0, p1, g, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int drawSelectedText(Graphics g, int x, int y,
|
||||
int p0, int p1) throws BadLocationException {
|
||||
drawSelected = true;
|
||||
return super.drawSelectedText(g, x, y, p0, p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int drawUnselectedText(Graphics g, int x, int y,
|
||||
int p0, int p1) throws BadLocationException {
|
||||
drawUnselected = true;
|
||||
return super.drawUnselectedText(g, x, y, p0, p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getResult() {
|
||||
return drawLine && drawSelected && drawUnselected;
|
||||
}
|
||||
}
|
||||
|
||||
static class WrappedPlainViewFPAPI extends WrappedPlainView implements Resultable {
|
||||
|
||||
boolean drawLine = false;
|
||||
boolean drawSelected = false;
|
||||
boolean drawUnselected = false;
|
||||
|
||||
public WrappedPlainViewFPAPI(Element elem) {
|
||||
super(elem);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawLine(int p0, int p1, Graphics2D g, float x, float y) {
|
||||
drawLine = true;
|
||||
super.drawLine(p0, p1, g, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float drawSelectedText(Graphics2D g, float x, float y,
|
||||
int p0, int p1) throws BadLocationException {
|
||||
drawSelected = true;
|
||||
return super.drawSelectedText(g, x, y, p0, p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float drawUnselectedText(Graphics2D g, float x, float y,
|
||||
int p0, int p1) throws BadLocationException {
|
||||
drawUnselected = true;
|
||||
return super.drawUnselectedText(g, x, y, p0, p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getResult() {
|
||||
return drawLine && drawSelected && drawUnselected;
|
||||
}
|
||||
}
|
||||
|
||||
static class WrappedPlainViewMixedAPI extends WrappedPlainView implements Resultable {
|
||||
|
||||
boolean isIntMethodCalled = false;
|
||||
boolean isFPMethodCalled = false;
|
||||
|
||||
public WrappedPlainViewMixedAPI(Element elem) {
|
||||
super(elem);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int drawUnselectedText(Graphics g, int x, int y,
|
||||
int p0, int p1) throws BadLocationException {
|
||||
isIntMethodCalled = true;
|
||||
return super.drawUnselectedText(g, x, y, p0, p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float drawUnselectedText(Graphics2D g, float x, float y,
|
||||
int p0, int p1) throws BadLocationException {
|
||||
isFPMethodCalled = true;
|
||||
return super.drawUnselectedText(g, x, y, p0, p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getResult() {
|
||||
return !isIntMethodCalled && isFPMethodCalled;
|
||||
}
|
||||
}
|
||||
|
||||
static class PasswordViewINTAPI extends PasswordView implements Resultable {
|
||||
|
||||
boolean isIntMethodCalled = false;
|
||||
|
||||
public PasswordViewINTAPI(Element elem) {
|
||||
super(elem);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int drawEchoCharacter(Graphics g, int x, int y, char c) {
|
||||
isIntMethodCalled = true;
|
||||
return super.drawEchoCharacter(g, x, y, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getResult() {
|
||||
return isIntMethodCalled;
|
||||
}
|
||||
}
|
||||
|
||||
static class PasswordViewFPAPI extends PasswordView implements Resultable {
|
||||
|
||||
boolean isFPMethodCalled = false;
|
||||
|
||||
public PasswordViewFPAPI(Element elem) {
|
||||
super(elem);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float drawEchoCharacter(Graphics2D g, float x, float y, char c) {
|
||||
isFPMethodCalled = true;
|
||||
return super.drawEchoCharacter(g, x, y, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getResult() {
|
||||
return isFPMethodCalled;
|
||||
}
|
||||
}
|
||||
|
||||
static class PasswordViewMixedAPI extends PasswordView implements Resultable {
|
||||
|
||||
boolean isIntMethodCalled = false;
|
||||
boolean isFPMethodCalled = false;
|
||||
|
||||
public PasswordViewMixedAPI(Element elem) {
|
||||
super(elem);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int drawEchoCharacter(Graphics g, int x, int y, char c) {
|
||||
isIntMethodCalled = true;
|
||||
return super.drawEchoCharacter(g, x, y, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float drawEchoCharacter(Graphics2D g, float x, float y, char c) {
|
||||
isFPMethodCalled = true;
|
||||
return super.drawEchoCharacter(g, x, y, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getResult() {
|
||||
return !isIntMethodCalled && isFPMethodCalled;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user