1 /* 2 * Copyright (c) 2014, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javafx.scene.control.test.util; 26 27 import java.util.ArrayList; 28 import java.util.Collection; 29 import java.util.Collections; 30 import java.util.HashSet; 31 import java.util.Set; 32 import org.jemmy.Point; 33 import org.jemmy.interfaces.Keyboard.KeyboardButtons; 34 import org.jemmy.interfaces.Keyboard.KeyboardModifiers; 35 import org.junit.Assert; 36 37 /** 38 * Helps to track actual state of selection in controls like TreeView, listView, 39 * TableView and TreeTableView. 40 * 41 * @author Oleg Barbashov, Alexander Kirov 42 */ 43 public class MultipleSelectionHelper { 44 45 public Point focus = new Point(-1, 0); 46 public Point anchor = null; 47 public boolean multiple = false; 48 public boolean singleCell = false; 49 public Collection<Point> selectedSet = new ArrayList<Point>(); 50 public int rows; 51 public int columns; 52 public int pageHeight = -1; 53 public int pageWidth = -1; 54 public boolean ctrlA = false; 55 public int topVisible = -1; 56 public int bottomVisible = -1; 57 58 public MultipleSelectionHelper(int columns, int rows) { 59 this.rows = rows; 60 this.columns = columns; 61 } 62 63 /** 64 * ALARM: must be called before pushing key combination, from code, which 65 * knows, how to understand, which area is visible now. And this visible 66 * area must be passed as argument. 67 * 68 * @param area - range [begin, end] - current fully visible range of lines. 69 * Checks, that begin <= end. 70 */ 71 public void setVisibleRange(Range area) { 72 Assert.assertTrue(area.begin <= area.end); 73 this.topVisible = area.begin; 74 this.bottomVisible = area.end; 75 this.pageHeight = this.bottomVisible - this.topVisible + 1; 76 } 77 78 public void setPageHeight(int height) { 79 this.pageHeight = height; 80 } 81 82 public void setPageWidth(int width) { 83 this.pageWidth = width; 84 } 85 86 public void setMultiple(boolean multiple) { 87 this.multiple = multiple; 88 } 89 90 public void setSingleCell(boolean singleCell) { 91 if (this.singleCell != singleCell) { 92 if (singleCell) { 93 focus = new Point(0, 0); 94 } else { 95 focus = new Point(-1, 0); 96 } 97 this.singleCell = singleCell; 98 selectedSet.clear(); 99 anchor = null; 100 } 101 } 102 103 public void moveFocusTo(int row, int column) { 104 focus = new Point(row, column); 105 } 106 107 public void moveAnchorTo(int row, int column) { 108 anchor = new Point(row, column); 109 } 110 111 public Collection<Point> getSelected() { 112 return new HashSet<Point>(selectedSet); 113 } 114 115 public void push(KeyboardButtons btn, KeyboardModifiers... modifiers) { 116 ctrlA = false; 117 switch (modifiers.length) { 118 case 0: 119 push(btn); 120 break; 121 case 1: 122 switch (modifiers[0]) { 123 case META_DOWN_MASK: 124 case CTRL_DOWN_MASK: 125 ctrl(btn); 126 break; 127 case SHIFT_DOWN_MASK: 128 shift(btn); 129 break; 130 default: 131 throw new UnsupportedOperationException(this.getClass().getSimpleName() + ": unexpected button is pressed"); 132 } 133 break; 134 case 2: 135 Set<KeyboardModifiers> set = new HashSet<KeyboardModifiers>(); 136 Collections.addAll(set, modifiers); 137 if (set.contains(KeyboardModifiers.SHIFT_DOWN_MASK) 138 && (set.contains(KeyboardModifiers.CTRL_DOWN_MASK) 139 || set.contains(KeyboardModifiers.META_DOWN_MASK))) { 140 ctrlShift(btn); 141 break; 142 } 143 if (set.contains(KeyboardModifiers.CTRL_DOWN_MASK) 144 && set.contains(KeyboardModifiers.META_DOWN_MASK)) { 145 metaCtrl(btn); 146 break; 147 } 148 throw new UnsupportedOperationException(this.getClass().getSimpleName() + ": unexpected button is pressed"); 149 default: 150 throw new UnsupportedOperationException(this.getClass().getSimpleName() + ": unexpected button is pressed"); 151 } 152 } 153 154 public void push(KeyboardButtons btn) { 155 switch (btn) { 156 case PAGE_DOWN: 157 checkPageHeight(); 158 moveFocusOnePageDown(); 159 anchor = new Point(focus); 160 select(selectedSet, focus, focus, true); 161 return; 162 case PAGE_UP: 163 checkPageHeight(); 164 moveFocusOnePageUp(); 165 anchor = new Point(focus); 166 select(selectedSet, focus, focus, true); 167 return; 168 case SPACE: 169 anchor = new Point(focus); 170 select(selectedSet, focus, false, false); 171 return; 172 } 173 switch (btn) { 174 case HOME: 175 focus.y = 0; 176 anchor = new Point(focus); 177 break; 178 case END: 179 focus.y = rows - 1; 180 anchor = new Point(focus); 181 break; 182 case UP: 183 if (focus.y > 0) { 184 focus.y--; 185 } 186 anchor = new Point(focus); 187 break; 188 case DOWN: 189 if (focus.y < rows - 1) { 190 focus.y++; 191 } 192 anchor = new Point(focus); 193 break; 194 case LEFT: 195 if (singleCell && (focus.x > 0)) { 196 focus.x--; 197 } 198 anchor = new Point(focus); 199 break; 200 case RIGHT: 201 if (singleCell && (focus.x < columns - 1)) { 202 focus.x++; 203 } 204 anchor = new Point(focus); 205 break; 206 default: 207 throw new UnsupportedOperationException(this.getClass().getSimpleName() + ": unexpected button is pressed"); 208 } 209 select(selectedSet, focus, true, false); 210 } 211 212 public void ctrl(KeyboardButtons btn) { 213 switch (btn) { 214 case A: 215 select(selectedSet, new Point(0, 0), new Point(columns - 1, rows - 1), true); 216 //focus.y = rows - 1;//We dont change focus, it stays at the same place. 217 ctrlA = true; 218 break; 219 case HOME: 220 focus.y = 0; 221 break; 222 case END: 223 focus.y = rows - 1; 224 break; 225 case PAGE_DOWN: 226 checkPageHeight(); 227 moveFocusOnePageDown(); 228 break; 229 case PAGE_UP: 230 checkPageHeight(); 231 moveFocusOnePageUp(); 232 break; 233 case SPACE: 234 anchor = new Point(focus); 235 select(selectedSet, focus, false, true); 236 return; 237 case UP: 238 if (focus.y > 0) { 239 focus.y--; 240 } 241 break; 242 case DOWN: 243 if (focus.y < rows - 1) { 244 focus.y++; 245 } 246 break; 247 case LEFT: 248 if (focus.x > 0) { 249 focus.x--; 250 } 251 break; 252 case RIGHT: 253 if (focus.x < columns - 1) { 254 focus.x++; 255 } 256 break; 257 } 258 } 259 260 //See also RT-34619 261 public void shift(KeyboardButtons btn) { 262 if (anchor == null) { 263 anchor = new Point(focus); 264 } 265 266 switch (btn) { 267 case HOME: 268 focus.y = 0; 269 select(selectedSet, anchor, focus, true); 270 break; 271 case END: 272 focus.y = rows - 1; 273 select(selectedSet, anchor, focus, true); 274 break; 275 case UP: 276 if (focus.y > 0) { 277 focus.y--; 278 if (multiple) { 279 select(selectedSet, anchor, focus, true); 280 } else { 281 select(selectedSet, focus, focus, true); 282 } 283 } 284 break; 285 case DOWN: 286 if (focus.y < rows - 1) { 287 focus.y++; 288 if (multiple) { 289 select(selectedSet, anchor, focus, true); 290 } else { 291 select(selectedSet, focus, focus, true); 292 } 293 } 294 break; 295 case PAGE_UP: 296 if (!singleCell) { 297 checkPageHeight(); 298 moveFocusOnePageUp(); 299 select(selectedSet, anchor, focus, true); 300 } 301 break; 302 case PAGE_DOWN: 303 if (!singleCell) { 304 checkPageHeight(); 305 moveFocusOnePageDown(); 306 select(selectedSet, anchor, focus, true); 307 } 308 break; 309 case LEFT: 310 if (focus.x > 0) { 311 focus.x--; 312 } 313 if (multiple) { 314 select(selectedSet, anchor, focus, true); 315 } else { 316 select(selectedSet, focus, focus, true); 317 } 318 break; 319 case RIGHT: 320 if (focus.x < columns - 1) { 321 focus.x++; 322 } 323 if (multiple) { 324 select(selectedSet, anchor, focus, true); 325 } else { 326 select(selectedSet, focus, focus, true); 327 } 328 break; 329 case SPACE: 330 if (multiple) { 331 select(selectedSet, anchor, focus, true); 332 } else { 333 select(selectedSet, focus, focus, true); 334 } 335 break; 336 } 337 } 338 339 public void ctrlShift(KeyboardButtons btn) { 340 if (anchor == null) { 341 anchor = new Point(focus); 342 } 343 switch (btn) { 344 case HOME: 345 focus.y = 0; 346 if (multiple) { 347 select(selectedSet, anchor, focus, false); 348 } else { 349 select(selectedSet, focus, focus, true); 350 } 351 break; 352 case END: 353 focus.y = rows - 1; 354 if (multiple) { 355 select(selectedSet, anchor, focus, false); 356 } else { 357 select(selectedSet, focus, focus, true); 358 } 359 break; 360 case UP: 361 if (focus.y > 0) { 362 focus.y--; 363 if (multiple) { 364 select(selectedSet, anchor, focus, false); 365 } else { 366 select(selectedSet, focus, focus, true); 367 } 368 } 369 break; 370 case DOWN: 371 if (focus.y < rows - 1) { 372 focus.y++; 373 if (multiple) { 374 select(selectedSet, anchor, focus, false); 375 } else { 376 select(selectedSet, focus, focus, true); 377 } 378 } 379 break; 380 case PAGE_UP: 381 if (focus.y > 0) { 382 moveFocusOnePageUp(); 383 if (multiple) { 384 select(selectedSet, anchor, focus, false); 385 } else { 386 select(selectedSet, focus, focus, true); 387 } 388 } 389 break; 390 case PAGE_DOWN: 391 if (focus.y < rows - 1) { 392 moveFocusOnePageDown(); 393 if (multiple) { 394 select(selectedSet, anchor, focus, false); 395 } else { 396 select(selectedSet, focus, focus, true); 397 } 398 } 399 break; 400 case LEFT: 401 if (focus.x > 0) { 402 focus.x--; 403 if (multiple) { 404 select(selectedSet, anchor, focus, false); 405 } else { 406 select(selectedSet, focus, focus, true); 407 } 408 } 409 break; 410 case RIGHT: 411 if (focus.x < columns - 1) { 412 focus.x++; 413 if (multiple) { 414 select(selectedSet, anchor, focus, false); 415 } else { 416 select(selectedSet, focus, focus, true); 417 } 418 } 419 break; 420 case SPACE: 421 if (multiple) { 422 select(selectedSet, anchor, focus, false); 423 } else { 424 select(selectedSet, focus, focus, true); 425 } 426 break; 427 } 428 } 429 430 public void metaCtrl(KeyboardButtons btn) { 431 if (anchor == null) { 432 anchor = new Point(focus); 433 } 434 switch (btn) { 435 case SPACE: 436 anchor = new Point(focus); 437 select(selectedSet, focus, false, true); 438 return; 439 default: 440 throw new UnsupportedOperationException("Unsupported : Meta + Ctrl + " + btn); 441 } 442 } 443 444 public void click(int column, int row, KeyboardButtons modifier) { 445 if (modifier != null) { 446 switch (modifier) { 447 case CONTROL: 448 case META: 449 ctrlClick(column, row); 450 return; 451 case SHIFT: 452 shiftClick(column, row); 453 return; 454 } 455 } 456 click(column, row); 457 } 458 459 public void click(int column, int row) { 460 if (!singleCell) { 461 column = -1; 462 } 463 focus = new Point(column, row); 464 anchor = new Point(focus); 465 selectedSet.clear(); 466 selectedSet.add(new Point(column, row)); 467 } 468 469 protected void shiftClick(int column, int row) { 470 if (multiple) { 471 if (anchor == null) { 472 anchor = new Point(focus); 473 } 474 if (!singleCell) { 475 column = -1; 476 } 477 focus.y = row; 478 focus.x = column; 479 select(selectedSet, anchor, focus, true); 480 } else { 481 click(column, row); 482 } 483 } 484 485 protected void ctrlClick(int column, int row) { 486 //ctrl creates anchor at (column,row). 487 if (multiple) { 488 if (!singleCell) { 489 column = -1; 490 } 491 focus.y = row; 492 focus.x = column; 493 anchor = new Point(focus); 494 select(selectedSet, focus, false, true); 495 } else { 496 if (!singleCell) { 497 column = -1; 498 } 499 focus = new Point(column, row); 500 anchor = new Point(focus); 501 //See RT-34649 502 if (selectedSet.contains(focus)) { 503 selectedSet.clear(); 504 } else { 505 selectedSet.clear(); 506 selectedSet.add(new Point(column, row)); 507 } 508 } 509 } 510 511 protected void select(Collection<Point> set, Point p, boolean clear, boolean invert) { 512 if (clear) { 513 set.clear(); 514 } 515 if (invert && selectedSet.contains(p)) { 516 selectedSet.remove(p); 517 } else { 518 if (!selectedSet.contains(p)) { 519 selectedSet.add(new Point(p)); 520 } 521 } 522 } 523 524 protected void select(Collection<Point> set, Point p1, Point p2, boolean clear) { 525 if (clear) { 526 set.clear(); 527 } 528 select(set, p1, p2); 529 } 530 531 protected void select(Collection<Point> set, Point p1, Point p2) { 532 if (singleCell) { 533 for (int j = Math.min(p1.x, p2.x); j <= Math.max(p1.x, p2.x); j++) { 534 for (int i = Math.min(p1.y, p2.y); i <= Math.max(p1.y, p2.y); i++) { 535 set.add(new Point(j, i)); 536 } 537 } 538 } else { 539 for (int i = Math.min(p1.y, p2.y); i <= Math.max(p1.y, p2.y); i++) { 540 set.add(new Point(-1, i)); 541 } 542 } 543 } 544 545 protected void checkPageHeight() { 546 if (pageHeight < 0 || bottomVisible < 0 || topVisible < 0) { 547 throw new IndexOutOfBoundsException(this.getClass().getSimpleName() + ": incorrect page parameters are set"); 548 } 549 } 550 551 protected void checkPageWidth() { 552 if (pageWidth < 0) { 553 throw new IndexOutOfBoundsException(this.getClass().getSimpleName() + ": incorrect page height is set"); 554 } 555 } 556 557 private void moveFocusOnePageDown() { 558 if (focus.y < bottomVisible) { 559 focus.y = bottomVisible; 560 } else { 561 focus.y += (pageHeight - 1); 562 if (focus.y > rows - 1) { 563 focus.y = rows - 1; 564 } 565 } 566 } 567 568 private void moveFocusOnePageUp() { 569 if (focus.y > topVisible) { 570 focus.y = topVisible; 571 } else { 572 focus.y -= (pageHeight - 1); 573 if (focus.y < 0) { 574 focus.y = 0; 575 } 576 } 577 } 578 579 public static class Range { 580 581 public final int begin; 582 public final int end; 583 584 public Range(int begin, int end) { 585 this.begin = begin; 586 this.end = end; 587 } 588 589 @Override 590 public String toString() { 591 return "Range : begin <" + begin + ">, end <" + end + ">."; 592 } 593 } 594 595 public static class ListViewMultipleSelectionHelper extends MultipleSelectionHelper { 596 597 public ListViewMultipleSelectionHelper(int columns, int rows) { 598 super(columns, rows); 599 } 600 601 /* Due to the https://javafx-jira.kenai.com/browse/RT-34204 602 * we don't need to remove the focused cell from selection 603 * in single cell selection mode. 604 */ 605 @Override 606 protected void ctrlClick(int column, int row) { 607 //ctrl creates anchor at (column,row). 608 column = -1; 609 if (multiple) { 610 focus.y = row; 611 focus.x = column; 612 anchor = new Point(focus); 613 select(selectedSet, focus, false, true); 614 } else { 615 focus = new Point(column, row); 616 anchor = new Point(focus); 617 if (!selectedSet.contains(focus)) { 618 619 selectedSet.clear(); 620 selectedSet.add(new Point(column, row)); 621 } 622 } 623 } 624 } 625 }