1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.awt.Component;
  25 import java.awt.Dimension;
  26 import java.awt.EventQueue;
  27 import java.awt.FlowLayout;
  28 import java.awt.Font;
  29 import java.util.Objects;
  30 import java.util.concurrent.Callable;
  31 
  32 import javax.swing.JButton;
  33 import javax.swing.JCheckBox;
  34 import javax.swing.JComboBox;
  35 import javax.swing.JEditorPane;
  36 import javax.swing.JFormattedTextField;
  37 import javax.swing.JFrame;
  38 import javax.swing.JLabel;
  39 import javax.swing.JList;
  40 import javax.swing.JMenu;
  41 import javax.swing.JMenuItem;
  42 import javax.swing.JRadioButton;
  43 import javax.swing.JScrollPane;
  44 import javax.swing.JTable;
  45 import javax.swing.JTextArea;
  46 import javax.swing.JTextField;
  47 import javax.swing.JToolTip;
  48 import javax.swing.JTree;
  49 import javax.swing.SwingUtilities;
  50 import javax.swing.UIManager;
  51 import javax.swing.UnsupportedLookAndFeelException;
  52 import javax.swing.tree.DefaultMutableTreeNode;
  53 
  54 import static javax.swing.UIManager.getInstalledLookAndFeels;
  55 
  56 /**
  57  * @test
  58  * @key headful
  59  * @bug 8201552
  60  * @summary Initial layout of the component should use correct graphics config.
  61  *          It is checked by SwingUtilities.updateComponentTreeUI(), if layout
  62  *          was correct the call to updateComponentTreeUI() will be no-op.
  63  * @compile -encoding utf-8 StalePreferredSize.java
  64  * @run main StalePreferredSize
  65  * @run main/othervm -Dsun.java2d.uiScale=2.25 StalePreferredSize
  66  */
  67 public final class StalePreferredSize {
  68 
  69     static final String HTML =
  70             "<html><span>A few words to get started before the "
  71                     + "bug</span><span>overlapping text</span></html>";
  72 
  73     // Some text to be tested
  74     static String TEXT1 = "A quick brown fox jumps over the lazy dog";
  75     static String TEXT2 = "El veloz murciélago hindú comía feliz cardillo y "
  76             + "kiwi. La cigüeña tocaba el saxofón detrás del palenque de paja";
  77     static String TEXT3 = "Voix ambiguë d’un cœur qui au zéphyr préfère les "
  78             + "jattes de kiwis";
  79     static String TEXT4 = "다람쥐 헌 쳇바퀴에 타고파";
  80     static String TEXT5 = "Съешь ещё этих мягких французских булок да выпей же чаю";
  81 
  82 
  83     static JFrame frame;
  84     static Component component;
  85     static int typeFont = 0; // 0 - default, 1 - bold, 2 - italic
  86 
  87     public static void main(final String[] args) throws Exception {
  88         for (final UIManager.LookAndFeelInfo laf : getInstalledLookAndFeels()) {
  89             EventQueue.invokeAndWait(() -> setLookAndFeel(laf));
  90             for (; typeFont < 3; typeFont++) {
  91                 System.err.println("typeFont = " + typeFont);
  92                 test(HTML);
  93                 test(TEXT1);
  94                 test(TEXT2);
  95                 test(TEXT3);
  96                 test(TEXT4);
  97                 test(TEXT5);
  98             }
  99         }
 100     }
 101 
 102     private static void test(String text) throws Exception {
 103         System.err.println("text = " + text);
 104         // Each Callable create a component to be tested
 105         Callable[] comps = {
 106                 () -> new JLabel(text),
 107                 () -> new JButton(text),
 108                 () -> new JMenuItem(text),
 109                 () -> new JMenu(text),
 110                 () -> new JList<>(new String[]{text}),
 111                 () -> new JComboBox<>(new String[]{text}),
 112                 () -> new JTextField(text),
 113                 () -> new JTextArea(text),
 114                 () -> new JCheckBox(text),
 115                 () -> new JFormattedTextField(text),
 116                 () -> new JRadioButton(text),
 117                 () -> new JTree(new DefaultMutableTreeNode(text)),
 118 //TODO            () -> new JSpinner(new SpinnerListModel(new String[]{HTML})),
 119                 () -> {
 120                     JToolTip tip = new JToolTip();
 121                     tip.setTipText(text);
 122                     return tip;
 123                     },
 124                 () -> {
 125                     JEditorPane pane = new JEditorPane();
 126                     pane.setText(text);
 127                     return pane;
 128                     },
 129 
 130                 () -> {
 131                     JTable table = new JTable(1, 1);
 132                     table.getModel().setValueAt(text, 0, 0);
 133                     return table;
 134                     }
 135                 };
 136 
 137             for (Callable<Component> creator : comps) {
 138                 checkComponent(creator);
 139             }
 140     }
 141 
 142     static void checkComponent(Callable<Component> creator) throws Exception {
 143         EventQueue.invokeAndWait(() -> {
 144 
 145             try {
 146                 component = creator.call();
 147             } catch (Exception e) {
 148                 throw new RuntimeException(e);
 149             }
 150 
 151             if (typeFont == 1) {
 152                 component.setFont(component.getFont().deriveFont(Font.BOLD));
 153             }
 154             if (typeFont == 2) {
 155                 component.setFont(component.getFont().deriveFont(Font.ITALIC));
 156             }
 157 
 158             frame = new JFrame();
 159             frame.setLayout(new FlowLayout());
 160             frame.add(new JScrollPane(component));
 161             frame.setSize(300, 100);
 162             frame.setLocationRelativeTo(null);
 163             frame.setVisible(true);
 164         });
 165 
 166         EventQueue.invokeAndWait(() -> {
 167 
 168             // After the frame was shown we change nothing, so current layout
 169             // should be optimal and updateComponentTreeUI() should be no-op
 170             Dimension before = component.getPreferredSize();
 171             SwingUtilities.updateComponentTreeUI(frame);
 172             Dimension after = component.getPreferredSize();
 173 
 174             frame.dispose();
 175 
 176             if (!Objects.equals(before, after)) {
 177                 System.err.println("Component: " + component);
 178                 System.err.println("Before: " + before);
 179                 System.err.println("After: " + after);
 180                 throw new RuntimeException("Wrong PreferredSize");
 181             }
 182         });
 183     }
 184 
 185     private static void setLookAndFeel(final UIManager.LookAndFeelInfo laf) {
 186         try {
 187             UIManager.setLookAndFeel(laf.getClassName());
 188             System.err.println("LookAndFeel: " + laf.getClassName());
 189         } catch (final UnsupportedLookAndFeelException ignored) {
 190             System.err.println(
 191                     "Unsupported LookAndFeel: " + laf.getClassName());
 192         } catch (ClassNotFoundException | InstantiationException |
 193                 IllegalAccessException e) {
 194             throw new RuntimeException(e);
 195         }
 196     }
 197 }