1 /*
2  * Copyright (c) 2016, 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.FlowLayout;
25 import java.awt.Graphics;
26 import java.awt.Graphics2D;
27 import java.awt.Robot;
28 import javax.swing.JFrame;
29 import javax.swing.JPanel;
30 import javax.swing.JPasswordField;
31 import javax.swing.JTextField;
32 import javax.swing.SwingUtilities;
33 import javax.swing.UIManager;
34 import javax.swing.plaf.metal.MetalLookAndFeel;
35 import javax.swing.plaf.metal.MetalTextFieldUI;
36 import javax.swing.text.BadLocationException;
37 import javax.swing.text.Element;
38 import javax.swing.text.PasswordView;
39 import javax.swing.text.PlainView;
40 import javax.swing.text.View;
41 import javax.swing.text.WrappedPlainView;
42 
43 /**
44  * @test
45  * @bug 8156217 8169922
46  * @key headful
47  * @summary Selected text is shifted on HiDPI display
48  * @run main FPMethodCalledTest
49  */
50 public class FPMethodCalledTest {
51 
52     private static JFrame frame;
53     private static JTextField textField;
54 
main(String[] args)55     public static void main(String[] args) throws Exception {
56 
57         for (Test test : TESTS) {
58             test(test);
59         }
60     }
61 
test(final Test test)62     static void test(final Test test) throws Exception {
63         try {
64             Robot robot = new Robot();
65             robot.setAutoDelay(50);
66             SwingUtilities.invokeAndWait(() -> {
67                 createAndShowGUI(test);
68             });
69 
70             robot.waitForIdle();
71 
72             SwingUtilities.invokeAndWait(() -> {
73                 textField.select(1, 3);
74             });
75 
76             robot.waitForIdle();
77 
78             SwingUtilities.invokeAndWait(() -> {
79                 Resultable resultable = test.resultable;
80                 if (!resultable.getResult()) {
81                     throw new RuntimeException("Test fails for: " + resultable);
82                 }
83             });
84         } finally {
85             SwingUtilities.invokeAndWait(() -> {
86                 if (frame != null) {
87                     frame.dispose();
88                 }
89             });
90         }
91     }
92 
createAndShowGUI(Test test)93     static void createAndShowGUI(Test test) {
94 
95         try {
96             UIManager.setLookAndFeel(new MetalLookAndFeel());
97         } catch (Exception e) {
98             throw new RuntimeException(e);
99         }
100 
101         frame = new JFrame();
102         frame.setSize(300, 300);
103         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
104 
105         JPanel panel = new JPanel(new FlowLayout());
106 
107         String text = "AAAAAAA";
108         textField = test.isPasswordField()
109                 ? new JPasswordField(text)
110                 : new JTextField(text);
111 
112         textField.setUI(new MetalTextFieldUI() {
113 
114             @Override
115             public View create(Element elem) {
116                 return test.createView(elem);
117             }
118         });
119 
120         panel.add(textField);
121         frame.getContentPane().add(panel);
122         frame.setVisible(true);
123     }
124 
125     private static final Test[] TESTS = {
126         new Test() {
127             @Override
128             View createView(Element elem) {
129                 PlainViewINTAPI view = new PlainViewINTAPI(elem);
130                 resultable = view;
131                 return view;
132             }
133         },
134         new Test() {
135             @Override
136             View createView(Element elem) {
137                 PlainViewFPAPI view = new PlainViewFPAPI(elem);
138                 resultable = view;
139                 return view;
140             }
141         },
142         new Test() {
143             @Override
144             View createView(Element elem) {
145                 PlainViewMixedAPI view = new PlainViewMixedAPI(elem);
146                 resultable = view;
147                 return view;
148             }
149         },
150         new Test() {
151             @Override
152             View createView(Element elem) {
153                 WrappedPlainViewINTAPI view = new WrappedPlainViewINTAPI(elem);
154                 resultable = view;
155                 return view;
156             }
157         },
158         new Test() {
159             @Override
160             View createView(Element elem) {
161                 WrappedPlainViewFPAPI view = new WrappedPlainViewFPAPI(elem);
162                 resultable = view;
163                 return view;
164             }
165         },
166         new Test() {
167             @Override
168             View createView(Element elem) {
169                 WrappedPlainViewMixedAPI view = new WrappedPlainViewMixedAPI(elem);
170                 resultable = view;
171                 return view;
172             }
173         },
174         new Test(true) {
175 
176             @Override
177             View createView(Element elem) {
178                 PasswordViewINTAPI view = new PasswordViewINTAPI(elem);
179                 resultable = view;
180                 return view;
181             }
182         },
183         new Test(true) {
184 
185             @Override
186             View createView(Element elem) {
187                 PasswordViewFPAPI view = new PasswordViewFPAPI(elem);
188                 resultable = view;
189                 return view;
190             }
191         },
192         new Test(true) {
193 
194             @Override
195             View createView(Element elem) {
196                 PasswordViewMixedAPI view = new PasswordViewMixedAPI(elem);
197                 resultable = view;
198                 return view;
199             }
200         }
201     };
202 
203     static interface Resultable {
204 
getResult()205         boolean getResult();
206     }
207 
208     static abstract class Test {
209 
210         Resultable resultable;
211         final boolean isPasswordField;
212 
Test()213         public Test() {
214             this(false);
215         }
216 
Test(boolean isPasswordField)217         public Test(boolean isPasswordField) {
218             this.isPasswordField = isPasswordField;
219         }
220 
isPasswordField()221         boolean isPasswordField() {
222             return isPasswordField;
223         }
224 
createView(Element elem)225         abstract View createView(Element elem);
226     }
227 
228     static class PlainViewINTAPI extends PlainView implements Resultable {
229 
230         boolean drawLine = false;
231         boolean drawSelected = false;
232         boolean drawUnselected = false;
233 
PlainViewINTAPI(Element elem)234         public PlainViewINTAPI(Element elem) {
235             super(elem);
236         }
237 
238         @Override
drawLine(int lineIndex, Graphics g, int x, int y)239         protected void drawLine(int lineIndex, Graphics g, int x, int y) {
240             drawLine = true;
241             super.drawLine(lineIndex, g, x, y);
242         }
243 
244         @Override
drawSelectedText(Graphics g, int x, int y, int p0, int p1)245         protected int drawSelectedText(Graphics g, int x, int y,
246                 int p0, int p1) throws BadLocationException {
247             drawSelected = true;
248             return super.drawSelectedText(g, x, y, p0, p1);
249         }
250 
251         @Override
drawUnselectedText(Graphics g, int x, int y, int p0, int p1)252         protected int drawUnselectedText(Graphics g, int x, int y,
253                 int p0, int p1) throws BadLocationException {
254             drawUnselected = true;
255             return super.drawUnselectedText(g, x, y, p0, p1);
256         }
257 
258         @Override
getResult()259         public boolean getResult() {
260             return drawLine && drawSelected && drawUnselected;
261         }
262     }
263 
264     static class PlainViewFPAPI extends PlainView implements Resultable {
265 
266         boolean drawLine = false;
267         boolean drawSelected = false;
268         boolean drawUnselected = false;
269 
PlainViewFPAPI(Element elem)270         public PlainViewFPAPI(Element elem) {
271             super(elem);
272         }
273 
274         @Override
drawLine(int lineIndex, Graphics2D g, float x, float y)275         protected void drawLine(int lineIndex, Graphics2D g, float x, float y) {
276             drawLine = true;
277             super.drawLine(lineIndex, g, x, y);
278         }
279 
280         @Override
drawSelectedText(Graphics2D g, float x, float y, int p0, int p1)281         protected float drawSelectedText(Graphics2D g, float x, float y,
282                 int p0, int p1) throws BadLocationException {
283             drawSelected = true;
284             return super.drawSelectedText(g, x, y, p0, p1);
285         }
286 
287         @Override
drawUnselectedText(Graphics2D g, float x, float y, int p0, int p1)288         protected float drawUnselectedText(Graphics2D g, float x, float y,
289                 int p0, int p1) throws BadLocationException {
290             drawUnselected = true;
291             return super.drawUnselectedText(g, x, y, p0, p1);
292         }
293 
294         @Override
getResult()295         public boolean getResult() {
296             return drawSelected;
297         }
298     }
299 
300     static class PlainViewMixedAPI extends PlainView implements Resultable {
301 
302         boolean isIntMethodCalled = false;
303         boolean isFPMethodCalled = false;
304 
PlainViewMixedAPI(Element elem)305         public PlainViewMixedAPI(Element elem) {
306             super(elem);
307         }
308 
309         @Override
drawSelectedText(Graphics g, int x, int y, int p0, int p1)310         protected int drawSelectedText(Graphics g, int x, int y,
311                 int p0, int p1) throws BadLocationException {
312             isIntMethodCalled = true;
313             return super.drawSelectedText(g, x, y, p0, p1);
314         }
315 
316         @Override
drawSelectedText(Graphics2D g, float x, float y, int p0, int p1)317         protected float drawSelectedText(Graphics2D g, float x, float y,
318                 int p0, int p1) throws BadLocationException {
319             isFPMethodCalled = true;
320             return super.drawSelectedText(g, x, y, p0, p1);
321         }
322 
323         @Override
getResult()324         public boolean getResult() {
325             return !isIntMethodCalled && isFPMethodCalled;
326         }
327     }
328 
329     static class WrappedPlainViewINTAPI extends WrappedPlainView implements Resultable {
330 
331         boolean drawLine = false;
332         boolean drawSelected = false;
333         boolean drawUnselected = false;
334 
WrappedPlainViewINTAPI(Element elem)335         public WrappedPlainViewINTAPI(Element elem) {
336             super(elem);
337         }
338 
339         @Override
drawLine(int p0, int p1, Graphics g, int x, int y)340         protected void drawLine(int p0, int p1, Graphics g, int x, int y) {
341             drawLine = true;
342             super.drawLine(p0, p1, g, x, y);
343         }
344 
345         @Override
drawSelectedText(Graphics g, int x, int y, int p0, int p1)346         protected int drawSelectedText(Graphics g, int x, int y,
347                 int p0, int p1) throws BadLocationException {
348             drawSelected = true;
349             return super.drawSelectedText(g, x, y, p0, p1);
350         }
351 
352         @Override
drawUnselectedText(Graphics g, int x, int y, int p0, int p1)353         protected int drawUnselectedText(Graphics g, int x, int y,
354                 int p0, int p1) throws BadLocationException {
355             drawUnselected = true;
356             return super.drawUnselectedText(g, x, y, p0, p1);
357         }
358 
359         @Override
getResult()360         public boolean getResult() {
361             return drawLine && drawSelected && drawUnselected;
362         }
363     }
364 
365     static class WrappedPlainViewFPAPI extends WrappedPlainView implements Resultable {
366 
367         boolean drawLine = false;
368         boolean drawSelected = false;
369         boolean drawUnselected = false;
370 
WrappedPlainViewFPAPI(Element elem)371         public WrappedPlainViewFPAPI(Element elem) {
372             super(elem);
373         }
374 
375         @Override
drawLine(int p0, int p1, Graphics2D g, float x, float y)376         protected void drawLine(int p0, int p1, Graphics2D g, float x, float y) {
377             drawLine = true;
378             super.drawLine(p0, p1, g, x, y);
379         }
380 
381         @Override
drawSelectedText(Graphics2D g, float x, float y, int p0, int p1)382         protected float drawSelectedText(Graphics2D g, float x, float y,
383                 int p0, int p1) throws BadLocationException {
384             drawSelected = true;
385             return super.drawSelectedText(g, x, y, p0, p1);
386         }
387 
388         @Override
drawUnselectedText(Graphics2D g, float x, float y, int p0, int p1)389         protected float drawUnselectedText(Graphics2D g, float x, float y,
390                 int p0, int p1) throws BadLocationException {
391             drawUnselected = true;
392             return super.drawUnselectedText(g, x, y, p0, p1);
393         }
394 
395         @Override
getResult()396         public boolean getResult() {
397             return drawLine && drawSelected && drawUnselected;
398         }
399     }
400 
401     static class WrappedPlainViewMixedAPI extends WrappedPlainView implements Resultable {
402 
403         boolean isIntMethodCalled = false;
404         boolean isFPMethodCalled = false;
405 
WrappedPlainViewMixedAPI(Element elem)406         public WrappedPlainViewMixedAPI(Element elem) {
407             super(elem);
408         }
409 
410         @Override
drawUnselectedText(Graphics g, int x, int y, int p0, int p1)411         protected int drawUnselectedText(Graphics g, int x, int y,
412                 int p0, int p1) throws BadLocationException {
413             isIntMethodCalled = true;
414             return super.drawUnselectedText(g, x, y, p0, p1);
415         }
416 
417         @Override
drawUnselectedText(Graphics2D g, float x, float y, int p0, int p1)418         protected float drawUnselectedText(Graphics2D g, float x, float y,
419                 int p0, int p1) throws BadLocationException {
420             isFPMethodCalled = true;
421             return super.drawUnselectedText(g, x, y, p0, p1);
422         }
423 
424         @Override
getResult()425         public boolean getResult() {
426             return !isIntMethodCalled && isFPMethodCalled;
427         }
428     }
429 
430     static class PasswordViewINTAPI extends PasswordView implements Resultable {
431 
432         boolean isIntMethodCalled = false;
433 
PasswordViewINTAPI(Element elem)434         public PasswordViewINTAPI(Element elem) {
435             super(elem);
436 
437         }
438 
439         @Override
drawEchoCharacter(Graphics g, int x, int y, char c)440         protected int drawEchoCharacter(Graphics g, int x, int y, char c) {
441             isIntMethodCalled = true;
442             return super.drawEchoCharacter(g, x, y, c);
443         }
444 
445         @Override
getResult()446         public boolean getResult() {
447             return isIntMethodCalled;
448         }
449     }
450 
451     static class PasswordViewFPAPI extends PasswordView implements Resultable {
452 
453         boolean isFPMethodCalled = false;
454 
PasswordViewFPAPI(Element elem)455         public PasswordViewFPAPI(Element elem) {
456             super(elem);
457 
458         }
459 
460         @Override
drawEchoCharacter(Graphics2D g, float x, float y, char c)461         protected float drawEchoCharacter(Graphics2D g, float x, float y, char c) {
462             isFPMethodCalled = true;
463             return super.drawEchoCharacter(g, x, y, c);
464         }
465 
466         @Override
getResult()467         public boolean getResult() {
468             return isFPMethodCalled;
469         }
470     }
471 
472     static class PasswordViewMixedAPI extends PasswordView implements Resultable {
473 
474         boolean isIntMethodCalled = false;
475         boolean isFPMethodCalled = false;
476 
PasswordViewMixedAPI(Element elem)477         public PasswordViewMixedAPI(Element elem) {
478             super(elem);
479 
480         }
481 
482         @Override
drawEchoCharacter(Graphics g, int x, int y, char c)483         protected int drawEchoCharacter(Graphics g, int x, int y, char c) {
484             isIntMethodCalled = true;
485             return super.drawEchoCharacter(g, x, y, c);
486         }
487 
488         @Override
drawEchoCharacter(Graphics2D g, float x, float y, char c)489         protected float drawEchoCharacter(Graphics2D g, float x, float y, char c) {
490             isFPMethodCalled = true;
491             return super.drawEchoCharacter(g, x, y, c);
492         }
493 
494         @Override
getResult()495         public boolean getResult() {
496             return !isIntMethodCalled && isFPMethodCalled;
497         }
498     }
499 }
500