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