| /* |
| * Copyright (c) 2007, 2017, 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. |
| */ |
| |
| /* |
| * @test |
| * @key headful |
| * @bug 8133864 8158209 |
| * @summary Wrong display, when the document I18n properties is true. |
| */ |
| import javax.swing.*; |
| import javax.swing.text.*; |
| import java.awt.*; |
| import java.awt.event.KeyEvent; |
| import java.util.ArrayList; |
| import javax.swing.event.CaretEvent; |
| import javax.swing.event.CaretListener; |
| |
| public class TableViewLayoutTest extends JFrame { |
| |
| private static double yCaret; |
| private static double xCaret; |
| |
| // Number of iteration to verify the stability of the test with different robot delays : |
| // Work well with robot.delay(50) in hitKey method. |
| // But if the robot delay is too low, the test is not stable. |
| // Put this to 100, and reduce robot delay sometimes answers may be different. |
| private static int tn = 2; |
| |
| // The four caret positions to test. |
| private static double yCarFLTab; |
| private static double yCarLLTab; |
| private static double xCarBTab; |
| private static double xCarETab; |
| |
| // The caret coordonate differences along axis after the insertion and the removing cycle. |
| // 0 if the table layout is right. |
| private static double dyCarFLTab; |
| private static double dyCarLLTab; |
| private static double dxCarBTab; |
| private static double dxCarETab; |
| |
| private static JEditorPane edit = new JEditorPane(); |
| private static TableViewLayoutTest frame; |
| |
| private static String Prop = "\n"; |
| private static boolean isTabWrong = Boolean.FALSE; |
| |
| private static Boolean isI18n = false; |
| |
| public TableViewLayoutTest() { |
| |
| super("Code example for a TableView bug"); |
| setUndecorated(true); |
| setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); |
| edit.setEditorKit(new CodeBugEditorKit()); |
| initCodeBug(); |
| this.getContentPane().add(new JScrollPane(edit)); |
| this.pack(); |
| this.setLocationRelativeTo(null); |
| |
| edit.addCaretListener(new CaretListener() { |
| public void caretUpdate(CaretEvent e) { |
| JTextComponent textComp = (JTextComponent) e.getSource(); |
| try { |
| Rectangle rect = textComp.getUI().modelToView(textComp, e.getDot()); |
| yCaret = rect.getY(); |
| xCaret = rect.getX(); |
| } catch (BadLocationException ex) { |
| throw new RuntimeException("Failed to get pixel position of caret", ex); |
| } |
| } |
| }); |
| } |
| |
| private void initCodeBug() { |
| CodeBugDocument doc = (CodeBugDocument) edit.getDocument(); |
| try { |
| doc.insertString(0, "TextB TextE", null); |
| } catch (BadLocationException ex) { |
| } |
| doc.insertTable(6, 4, 3); |
| try { |
| doc.insertString(7, "Cell11", null); |
| doc.insertString(14, "Cell12", null); |
| doc.insertString(21, "Cell13", null); |
| doc.insertString(28, "Cell21", null); |
| doc.insertString(35, "Cell22", null); |
| doc.insertString(42, "Cell23", null); |
| doc.insertString(49, "Cell31", null); |
| doc.insertString(56, "Cell32", null); |
| doc.insertString(63, "Cell33", null); |
| doc.insertString(70, "Cell41", null); |
| doc.insertString(77, "Cell42", null); |
| doc.insertString(84, "Cell43", null); |
| } catch (BadLocationException ex) { |
| } |
| } |
| |
| public static void main(String[] args) throws Exception { |
| |
| for (int i = 0; i < tn; i++) { |
| Robot rob = new Robot(); |
| |
| SwingUtilities.invokeAndWait(new Runnable() { |
| @Override |
| public void run() { |
| frame = new TableViewLayoutTest(); |
| frame.setVisible(true); |
| } |
| }); |
| |
| SwingUtilities.invokeAndWait(new Runnable() { |
| @Override |
| public void run() { |
| //Enable or disable i18n. |
| isI18n = !isI18n; |
| edit.getDocument().putProperty("i18n", isI18n); |
| |
| //Made a change to update table layout. |
| //Without any change the table i18n property change is not take in account. |
| edit.select(11, 12); |
| edit.replaceSelection("1"); |
| |
| //Catch the four caret positions to test before insertions. |
| edit.setCaretPosition(6); |
| xCarBTab = xCaret; |
| edit.setCaretPosition(91); |
| xCarETab = xCaret; |
| |
| edit.setCaretPosition(74); |
| yCarLLTab = yCaret; |
| edit.setCaretPosition(11); |
| yCarFLTab = yCaret; |
| } |
| }); |
| |
| hitKey(rob, KeyEvent.VK_T); |
| hitKey(rob, KeyEvent.VK_E); |
| hitKey(rob, KeyEvent.VK_S); |
| hitKey(rob, KeyEvent.VK_T); |
| hitKey(rob, KeyEvent.VK_BACK_SPACE); |
| hitKey(rob, KeyEvent.VK_BACK_SPACE); |
| hitKey(rob, KeyEvent.VK_BACK_SPACE); |
| hitKey(rob, KeyEvent.VK_BACK_SPACE); |
| |
| rob.waitForIdle(); |
| |
| SwingUtilities.invokeAndWait(new Runnable() { |
| @Override |
| public void run() { |
| //Calculate caret coordinate differences and catch caret positions after insertions. |
| edit.setCaretPosition(6); |
| dxCarBTab = Math.abs(xCarBTab - xCaret); |
| edit.setCaretPosition(91); |
| dxCarETab = Math.abs(xCarETab - xCaret); |
| |
| edit.setCaretPosition(74); |
| dyCarLLTab = Math.abs(yCarLLTab - yCaret); |
| edit.setCaretPosition(11); |
| dyCarFLTab = Math.abs(yCarFLTab - yCaret); |
| |
| edit.setCaretPosition(74); |
| yCarLLTab = yCaret; |
| edit.setCaretPosition(11); |
| yCarFLTab = yCaret; |
| } |
| }); |
| |
| Object dp = edit.getDocument().getProperty("i18n"); |
| Boolean isI18n = dp instanceof Boolean ? (Boolean) dp : Boolean.FALSE; |
| String i18n = isI18n ? "\nWhen i18n enable, " : "\nWhen i18n disable, "; |
| |
| if (Math.abs(yCarFLTab - yCarLLTab) < 10) { |
| isTabWrong = Boolean.TRUE; |
| Prop = Prop + i18n + "test can't be completed : TableView layout wrong, lines overlap, see JDK-8133864."; |
| } else { |
| if (dyCarFLTab != 0 || dyCarLLTab != 0) { |
| isTabWrong = Boolean.TRUE; |
| Prop = Prop + i18n + "TableView layout wrong : Table high change when inserts and removes caracters, bug never reported yet. First Line dy=" + dyCarFLTab + " Last Line dy=" + dyCarLLTab; |
| } |
| if (dxCarBTab != 0 || dxCarETab != 0) { |
| isTabWrong = Boolean.TRUE; |
| Prop = Prop + i18n + "TableView layout wrong : Table width change when inserts and removes caracters, see JDK-8158209 and JDK-7169915. Before Table dx=" + dxCarBTab + " After Table dx=" + dxCarETab; |
| } |
| } |
| rob.waitForIdle(); |
| |
| SwingUtilities.invokeAndWait(new Runnable() { |
| @Override |
| public void run() { |
| frame.dispose(); |
| } |
| }); |
| } |
| if (isTabWrong) { |
| throw new RuntimeException(Prop); |
| } |
| |
| System.out.println("ok"); |
| } |
| |
| private static void hitKey(Robot robot, int k) throws Exception { |
| robot.delay(50); |
| robot.keyPress(k); |
| robot.keyRelease(k); |
| robot.delay(50); |
| } |
| } |
| |
| //------------------------------------------------------------------------------ |
| class CodeBugEditorKit extends StyledEditorKit { |
| |
| ViewFactory defaultFactory = new TableFactory(); |
| |
| @Override |
| public ViewFactory getViewFactory() { |
| return defaultFactory; |
| } |
| |
| @Override |
| public Document createDefaultDocument() { |
| return new CodeBugDocument(); |
| } |
| } |
| //------------------------------------------------------------------------------ |
| |
| class TableFactory implements ViewFactory { |
| |
| @Override |
| public View create(Element elem) { |
| String kind = elem.getName(); |
| if (kind != null) { |
| if (kind.equals(AbstractDocument.ContentElementName)) { |
| return new LabelView(elem); |
| } else if (kind.equals(AbstractDocument.ParagraphElementName)) { |
| return new ParagraphView(elem); |
| } else if (kind.equals(AbstractDocument.SectionElementName)) { |
| return new BoxView(elem, View.Y_AXIS); |
| } else if (kind.equals(StyleConstants.ComponentElementName)) { |
| return new ComponentView(elem); |
| } else if (kind.equals(CodeBugDocument.ELEMENT_TABLE)) { |
| return new tableView(elem); |
| } else if (kind.equals(StyleConstants.IconElementName)) { |
| return new IconView(elem); |
| } |
| } |
| // default to text display |
| return new LabelView(elem); |
| |
| } |
| } |
| //------------------------------------------------------------------------------ |
| |
| //------------------------------------------------------------------------------ |
| class tableView extends TableView implements ViewFactory { |
| |
| public tableView(Element elem) { |
| super(elem); |
| } |
| |
| @Override |
| public void setParent(View parent) { |
| super.setParent(parent); |
| } |
| |
| @Override |
| public void setSize(float width, float height) { |
| super.setSize(width, height); |
| } |
| |
| @Override |
| public ViewFactory getViewFactory() { |
| return this; |
| } |
| |
| @Override |
| public float getMinimumSpan(int axis) { |
| return getPreferredSpan(axis); |
| } |
| |
| @Override |
| public float getMaximumSpan(int axis) { |
| return getPreferredSpan(axis); |
| } |
| |
| @Override |
| public float getAlignment(int axis) { |
| return 0.5f; |
| } |
| |
| @Override |
| public float getPreferredSpan(int axis) { |
| if (axis == 0) { |
| return super.getPreferredSpan(0); |
| } |
| float preferredSpan = super.getPreferredSpan(axis); |
| return preferredSpan; |
| } |
| |
| @Override |
| public void paint(Graphics g, Shape allocation) { |
| super.paint(g, allocation); |
| Rectangle alloc = allocation.getBounds(); |
| int lastY = alloc.y + alloc.height - 1; |
| g.drawLine(alloc.x, lastY, alloc.x + alloc.width, lastY); |
| } |
| |
| @Override |
| protected void paintChild(Graphics g, Rectangle alloc, int index) { |
| super.paintChild(g, alloc, index); |
| int lastX = alloc.x + alloc.width; |
| g.drawLine(alloc.x, alloc.y, lastX, alloc.y); |
| } |
| |
| @Override |
| public View create(Element elem) { |
| String kind = elem.getName(); |
| if (kind != null) { |
| if (kind.equals(CodeBugDocument.ELEMENT_TR)) { |
| return new trView(elem); |
| } else if (kind.equals(CodeBugDocument.ELEMENT_TD)) { |
| return new BoxView(elem, View.Y_AXIS); |
| |
| } |
| } |
| |
| // default is to delegate to the normal factory |
| View p = getParent(); |
| if (p != null) { |
| ViewFactory f = p.getViewFactory(); |
| if (f != null) { |
| return f.create(elem); |
| } |
| } |
| |
| return null; |
| } |
| |
| public class trView extends TableRow { |
| |
| @Override |
| public void setParent(View parent) { |
| super.setParent(parent); |
| } |
| |
| public trView(Element elem) { |
| super(elem); |
| } |
| |
| public float getMinimumSpan(int axis) { |
| return getPreferredSpan(axis); |
| } |
| |
| public float getMaximumSpan(int axis) { |
| return getPreferredSpan(axis); |
| } |
| |
| public float getAlignment(int axis) { |
| return 0f; |
| } |
| |
| @Override |
| protected void paintChild(Graphics g, Rectangle alloc, int index) { |
| super.paintChild(g, alloc, index); |
| int lastY = alloc.y + alloc.height - 1; |
| g.drawLine(alloc.x, alloc.y, alloc.x, lastY); |
| int lastX = alloc.x + alloc.width; |
| g.drawLine(lastX, alloc.y, lastX, lastY); |
| } |
| }; |
| } |
| |
| //------------------------------------------------------------------------------ |
| class CodeBugDocument extends DefaultStyledDocument { |
| |
| public static final String ELEMENT_TABLE = "table"; |
| public static final String ELEMENT_TR = "table cells row"; |
| public static final String ELEMENT_TD = "table data cell"; |
| |
| public CodeBugDocument() { |
| //putProperty("i18n", Boolean.TRUE); |
| } |
| |
| protected void insertTable(int offset, int rowCount, int colCount) { |
| try { |
| ArrayList Specs = new ArrayList(); |
| ElementSpec gapTag = new ElementSpec(new SimpleAttributeSet(), |
| ElementSpec.ContentType, "\n".toCharArray(), 0, 1); |
| Specs.add(gapTag); |
| |
| SimpleAttributeSet tableAttrs = new SimpleAttributeSet(); |
| tableAttrs.addAttribute(ElementNameAttribute, ELEMENT_TABLE); |
| ElementSpec tableStart |
| = new ElementSpec(tableAttrs, ElementSpec.StartTagType); |
| Specs.add(tableStart); //start table tag |
| |
| fillRowSpecs(Specs, rowCount, colCount); |
| |
| ElementSpec[] spec = new ElementSpec[Specs.size()]; |
| Specs.toArray(spec); |
| |
| this.insert(offset, spec); |
| } catch (BadLocationException ex) { |
| } |
| } |
| |
| protected void fillRowSpecs(ArrayList Specs, int rowCount, int colCount) { |
| SimpleAttributeSet rowAttrs = new SimpleAttributeSet(); |
| rowAttrs.addAttribute(ElementNameAttribute, ELEMENT_TR); |
| for (int i = 0; i < rowCount; i++) { |
| ElementSpec rowStart |
| = new ElementSpec(rowAttrs, ElementSpec.StartTagType); |
| Specs.add(rowStart); |
| |
| fillCellSpecs(Specs, colCount); |
| |
| ElementSpec rowEnd |
| = new ElementSpec(rowAttrs, ElementSpec.EndTagType); |
| Specs.add(rowEnd); |
| } |
| |
| } |
| |
| protected void fillCellSpecs(ArrayList Specs, int colCount) { |
| for (int i = 0; i < colCount; i++) { |
| SimpleAttributeSet cellAttrs = new SimpleAttributeSet(); |
| cellAttrs.addAttribute(ElementNameAttribute, ELEMENT_TD); |
| |
| ElementSpec cellStart |
| = new ElementSpec(cellAttrs, ElementSpec.StartTagType); |
| Specs.add(cellStart); |
| |
| ElementSpec parStart = new ElementSpec(new SimpleAttributeSet(), |
| ElementSpec.StartTagType); |
| Specs.add(parStart); |
| ElementSpec parContent = new ElementSpec(new SimpleAttributeSet(), |
| ElementSpec.ContentType, "\n".toCharArray(), 0, 1); |
| Specs.add(parContent); |
| ElementSpec parEnd = new ElementSpec(new SimpleAttributeSet(), |
| ElementSpec.EndTagType); |
| Specs.add(parEnd); |
| ElementSpec cellEnd |
| = new ElementSpec(cellAttrs, ElementSpec.EndTagType); |
| Specs.add(cellEnd); |
| } |
| } |
| } |