1 /* 2 * Copyright (c) 2011, 2012, 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 /* 25 * @test 26 * @key headful 27 * @bug 6462008 28 * @summary Tests that mouse/keyboard work properly on JList with lead < 0 or > list.getModel().getSize() 29 * @author Shannon Hickey 30 * @run main bug6462008 31 */ 32 import java.awt.*; 33 import java.awt.event.*; 34 import javax.swing.*; 35 import java.util.*; 36 import sun.awt.SunToolkit; 37 38 public class bug6462008 { 39 40 private static final int DONT_CARE = -2; 41 private static int anchorLead; 42 private static boolean isAquaLAF; 43 private static int controlKey; 44 private static JList list; 45 private static SunToolkit toolkit; 46 private static Robot robot; 47 48 public static void main(String[] args) throws Exception { 49 toolkit = (SunToolkit) Toolkit.getDefaultToolkit(); 50 robot = new Robot(); 51 robot.setAutoDelay(100); 52 53 isAquaLAF = "Aqua".equals(UIManager.getLookAndFeel().getID()); 54 controlKey = isAquaLAF ? KeyEvent.VK_META : KeyEvent.VK_CONTROL; 55 56 SwingUtilities.invokeAndWait(new Runnable() { 57 58 @Override 59 public void run() { 60 createAndShowGUI(); 61 } 62 }); 63 64 toolkit.realSync(); 65 66 setAnchorLead(-1); 67 toolkit.realSync(); 68 69 testListSelection(); 70 71 setAnchorLead(100); 72 toolkit.realSync(); 73 74 testListSelection(); 75 } 76 77 public static void testListSelection() throws Exception { 78 79 // Space 80 robot.keyPress(KeyEvent.VK_SPACE); 81 robot.keyRelease(KeyEvent.VK_SPACE); 82 83 toolkit.realSync(); 84 checkSelection(); 85 resetList(); 86 toolkit.realSync(); 87 88 // Control + Space 89 robot.keyPress(KeyEvent.VK_CONTROL); 90 robot.keyPress(KeyEvent.VK_SPACE); 91 robot.keyRelease(KeyEvent.VK_SPACE); 92 robot.keyRelease(KeyEvent.VK_CONTROL); 93 94 toolkit.realSync(); 95 checkSelection(); 96 resetList(); 97 toolkit.realSync(); 98 99 // Shift + Space 100 robot.keyPress(KeyEvent.VK_SHIFT); 101 robot.keyPress(KeyEvent.VK_SPACE); 102 robot.keyRelease(KeyEvent.VK_SPACE); 103 robot.keyRelease(KeyEvent.VK_SHIFT); 104 105 toolkit.realSync(); 106 checkSelection(); 107 resetList(); 108 toolkit.realSync(); 109 110 // Control + Shift + Space 111 robot.keyPress(KeyEvent.VK_CONTROL); 112 robot.keyPress(KeyEvent.VK_SHIFT); 113 robot.keyPress(KeyEvent.VK_SPACE); 114 robot.keyRelease(KeyEvent.VK_SPACE); 115 robot.keyRelease(KeyEvent.VK_SHIFT); 116 robot.keyRelease(KeyEvent.VK_CONTROL); 117 118 toolkit.realSync(); 119 checkSelection(); 120 resetList(); 121 toolkit.realSync(); 122 123 124 // Control + A Multiple Selection 125 126 robot.keyPress(controlKey); 127 robot.keyPress(KeyEvent.VK_A); 128 robot.keyRelease(KeyEvent.VK_A); 129 robot.keyRelease(controlKey); 130 131 toolkit.realSync(); 132 checkSelectionAL(-1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9); 133 resetList(); 134 setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 135 toolkit.realSync(); 136 137 // Control + A Single Selection 138 robot.keyPress(controlKey); 139 robot.keyPress(KeyEvent.VK_A); 140 robot.keyRelease(KeyEvent.VK_A); 141 robot.keyRelease(controlKey); 142 143 toolkit.realSync(); 144 checkSelectionAL(0, 0, 0); 145 resetList(); 146 setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 147 setSelectionInterval(5, 5); 148 toolkit.realSync(); 149 150 151 // Control + A Selection interval (5, 5) 152 robot.keyPress(controlKey); 153 robot.keyPress(KeyEvent.VK_A); 154 robot.keyRelease(KeyEvent.VK_A); 155 robot.keyRelease(controlKey); 156 157 toolkit.realSync(); 158 checkSelection(5); 159 resetList(); 160 toolkit.realSync(); 161 162 // Page Down 163 // Not applicable for the Aqua L&F 164 if (!isAquaLAF) { 165 robot.keyPress(KeyEvent.VK_PAGE_DOWN); 166 robot.keyRelease(KeyEvent.VK_PAGE_DOWN); 167 168 toolkit.realSync(); 169 checkSelection(9, 9, 9); 170 resetList(); 171 toolkit.realSync(); 172 } 173 174 // Shift + Page Down 175 /* 176 * We really want to use robot here, but there seems to be a bug in AWT's 177 * robot implementation (see 6463168). For now, we'll invoke the action 178 * directly instead. When the bug is fixed, we'll use the following four 179 * lines instead: 180 * robot.keyPress(KeyEvent.VK_SHIFT); 181 * robot.keyPress(KeyEvent.VK_PAGE_DOWN); 182 * robot.keyRelease(KeyEvent.VK_PAGE_DOWN); 183 * robot.keyRelease(KeyEvent.VK_SHIFT); 184 */ 185 186 scrollDownExtendSelection(); 187 188 toolkit.realSync(); 189 checkSelection(0, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9); 190 resetList(); 191 toolkit.realSync(); 192 193 // Down 194 robot.keyPress(KeyEvent.VK_DOWN); 195 robot.keyRelease(KeyEvent.VK_DOWN); 196 197 toolkit.realSync(); 198 checkSelectionAL(0, 0, 0); 199 resetList(); 200 toolkit.realSync(); 201 202 // L 203 robot.keyPress(KeyEvent.VK_L); 204 robot.keyRelease(KeyEvent.VK_L); 205 206 toolkit.realSync(); 207 checkSelectionAL(0, 0, 0); 208 resetList(); 209 toolkit.realSync(); 210 211 // Click item 4 212 Point p = clickItem4(); 213 robot.mouseMove(p.x, p.y); 214 robot.mousePress(InputEvent.BUTTON1_MASK); 215 robot.mouseRelease(InputEvent.BUTTON1_MASK); 216 217 218 toolkit.realSync(); 219 checkSelectionAL(4, 4, 4); 220 resetList(); 221 toolkit.realSync(); 222 223 224 // Control + Click item 4 225 robot.keyPress(controlKey); 226 p = clickItem4(); 227 robot.mouseMove(p.x, p.y); 228 robot.mousePress(InputEvent.BUTTON1_MASK); 229 robot.mouseRelease(InputEvent.BUTTON1_MASK); 230 robot.keyRelease(controlKey); 231 232 233 toolkit.realSync(); 234 checkSelectionAL(4, 4, 4); 235 resetList(); 236 toolkit.realSync(); 237 238 // Shift + Click item 4 239 robot.keyPress(KeyEvent.VK_SHIFT); 240 p = clickItem4(); 241 robot.mouseMove(p.x, p.y); 242 robot.mousePress(InputEvent.BUTTON1_MASK); 243 robot.mouseRelease(InputEvent.BUTTON1_MASK); 244 robot.keyRelease(KeyEvent.VK_SHIFT); 245 246 247 toolkit.realSync(); 248 checkSelectionAL(0, 4, 0, 1, 2, 3, 4); 249 resetList(); 250 toolkit.realSync(); 251 252 253 // Control + Shift + Click item 4 254 robot.keyPress(controlKey); 255 robot.keyPress(KeyEvent.VK_SHIFT); 256 p = clickItem4(); 257 robot.mouseMove(p.x, p.y); 258 robot.mousePress(InputEvent.BUTTON1_MASK); 259 robot.mouseRelease(InputEvent.BUTTON1_MASK); 260 robot.keyRelease(KeyEvent.VK_SHIFT); 261 robot.keyRelease(controlKey); 262 263 toolkit.realSync(); 264 checkSelectionAL(0, 4); 265 resetList(); 266 toolkit.realSync(); 267 } 268 269 private static DefaultListModel getModel() { 270 DefaultListModel listModel = new DefaultListModel(); 271 for (int i = 0; i < 10; i++) { 272 listModel.addElement("List Item " + i); 273 } 274 return listModel; 275 } 276 277 private static Point clickItem4() throws Exception { 278 279 final Point[] result = new Point[1]; 280 SwingUtilities.invokeAndWait(new Runnable() { 281 282 @Override 283 public void run() { 284 Rectangle r = list.getCellBounds(4, 4); 285 Point p = new Point(r.x + r.width / 2, r.y + r.height / 2); 286 SwingUtilities.convertPointToScreen(p, list); 287 result[0] = p; 288 } 289 }); 290 291 return result[0]; 292 } 293 294 private static void resetList() throws Exception { 295 SwingUtilities.invokeAndWait(new Runnable() { 296 297 @Override 298 public void run() { 299 list.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); 300 list.getSelectionModel().clearSelection(); 301 setAnchorLeadNonThreadSafe(); 302 } 303 }); 304 } 305 306 private static void scrollDownExtendSelection() throws Exception { 307 SwingUtilities.invokeAndWait(new Runnable() { 308 309 @Override 310 public void run() { 311 list.getActionMap().get("scrollDownExtendSelection"). 312 actionPerformed(new ActionEvent(list, 313 ActionEvent.ACTION_PERFORMED, null)); 314 } 315 }); 316 } 317 318 private static void setSelectionMode(final int selectionMode) throws Exception { 319 SwingUtilities.invokeAndWait(new Runnable() { 320 321 @Override 322 public void run() { 323 list.getSelectionModel().setSelectionMode(selectionMode); 324 setAnchorLeadNonThreadSafe(); 325 } 326 }); 327 } 328 329 private static void setSelectionInterval(final int index0, final int index1) throws Exception { 330 SwingUtilities.invokeAndWait(new Runnable() { 331 332 @Override 333 public void run() { 334 list.getSelectionModel().setSelectionInterval(index0, index1); 335 setAnchorLeadNonThreadSafe(); 336 } 337 }); 338 } 339 340 private static void setAnchorLead(final int anchorLeadValue) throws Exception { 341 SwingUtilities.invokeAndWait(new Runnable() { 342 343 @Override 344 public void run() { 345 anchorLead = anchorLeadValue; 346 setAnchorLeadNonThreadSafe(); 347 } 348 }); 349 } 350 351 private static void setAnchorLeadNonThreadSafe() { 352 list.getSelectionModel().setAnchorSelectionIndex(anchorLead); 353 ((DefaultListSelectionModel) list.getSelectionModel()).moveLeadSelectionIndex(anchorLead); 354 } 355 356 private static void createAndShowGUI() { 357 JFrame frame = new JFrame("bug6462008"); 358 frame.setSize(200, 500); 359 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 360 361 list = new JList(getModel()); 362 JPanel panel = new JPanel(new BorderLayout()); 363 panel.add(list); 364 frame.getContentPane().add(panel); 365 366 frame.setVisible(true); 367 } 368 369 private static void checkSelection(int... sels) throws Exception { 370 checkSelectionAL(DONT_CARE, DONT_CARE, sels); 371 } 372 373 private static void checkSelectionAL(final int anchor, final int lead, final int... sels) throws Exception { 374 SwingUtilities.invokeAndWait(new Runnable() { 375 376 @Override 377 public void run() { 378 checkSelectionNonThreadSafe(anchor, lead, sels); 379 } 380 }); 381 } 382 383 private static void checkSelectionNonThreadSafe(int anchor, int lead, int... sels) { 384 ListSelectionModel lsm = list.getSelectionModel(); 385 386 int actualAnchor = lsm.getAnchorSelectionIndex(); 387 int actualLead = lsm.getLeadSelectionIndex(); 388 389 if (anchor != DONT_CARE && actualAnchor != anchor) { 390 throw new RuntimeException("anchor is " + actualAnchor + ", should be " + anchor); 391 } 392 393 if (lead != DONT_CARE && actualLead != lead) { 394 throw new RuntimeException("lead is " + actualLead + ", should be " + lead); 395 } 396 397 Arrays.sort(sels); 398 boolean[] checks = new boolean[list.getModel().getSize()]; 399 for (int i : sels) { 400 checks[i] = true; 401 } 402 403 int index0 = Math.min(lsm.getMinSelectionIndex(), 0); 404 int index1 = Math.max(lsm.getMaxSelectionIndex(), list.getModel().getSize() - 1); 405 406 for (int i = index0; i <= index1; i++) { 407 if (lsm.isSelectedIndex(i)) { 408 if (i < 0 || i >= list.getModel().getSize() || !checks[i]) { 409 throw new RuntimeException(i + " is selected when it should not be"); 410 } 411 } else if (i >= 0 && i < list.getModel().getSize() && checks[i]) { 412 throw new RuntimeException(i + " is supposed to be selected"); 413 } 414 } 415 } 416 }