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