1 /* 2 * Copyright (c) 2011, 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 26 package com.sun.javafx.scene.control.behavior; 27 28 import com.sun.javafx.scene.control.SizeLimitedList; 29 import javafx.collections.ListChangeListener; 30 import javafx.collections.ObservableList; 31 import javafx.collections.WeakListChangeListener; 32 import javafx.geometry.NodeOrientation; 33 import javafx.scene.control.*; 34 import javafx.scene.input.KeyEvent; 35 import javafx.scene.input.MouseEvent; 36 import javafx.util.Callback; 37 import java.util.ArrayList; 38 import java.util.List; 39 import com.sun.javafx.PlatformUtil; 40 import static javafx.scene.input.KeyCode.A; 41 import static javafx.scene.input.KeyCode.DOWN; 42 import static javafx.scene.input.KeyCode.END; 43 import static javafx.scene.input.KeyCode.ENTER; 44 import static javafx.scene.input.KeyCode.ESCAPE; 45 import static javafx.scene.input.KeyCode.F2; 46 import static javafx.scene.input.KeyCode.HOME; 47 import static javafx.scene.input.KeyCode.KP_DOWN; 48 import static javafx.scene.input.KeyCode.KP_LEFT; 49 import static javafx.scene.input.KeyCode.KP_RIGHT; 50 import static javafx.scene.input.KeyCode.KP_UP; 51 import static javafx.scene.input.KeyCode.LEFT; 52 import static javafx.scene.input.KeyCode.PAGE_DOWN; 53 import static javafx.scene.input.KeyCode.PAGE_UP; 54 import static javafx.scene.input.KeyCode.RIGHT; 55 import static javafx.scene.input.KeyCode.SPACE; 56 import static javafx.scene.input.KeyCode.TAB; 57 import static javafx.scene.input.KeyCode.UP; 58 59 public abstract class TableViewBehaviorBase<C extends Control, T, TC extends TableColumnBase<T,?>> extends BehaviorBase<C> { 60 61 /************************************************************************** 62 * * 63 * Setup key bindings * 64 * * 65 *************************************************************************/ 66 protected static final List<KeyBinding> TABLE_VIEW_BINDINGS = new ArrayList<KeyBinding>(); 67 68 static { 69 TABLE_VIEW_BINDINGS.add(new KeyBinding(TAB, "TraverseNext")); 70 TABLE_VIEW_BINDINGS.add(new KeyBinding(TAB, "TraversePrevious").shift()); 71 72 TABLE_VIEW_BINDINGS.add(new KeyBinding(HOME, "SelectFirstRow")); 73 TABLE_VIEW_BINDINGS.add(new KeyBinding(END, "SelectLastRow")); 74 75 TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "ScrollUp")); 76 TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "ScrollDown")); 77 78 TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "SelectLeftCell")); 79 TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_LEFT, "SelectLeftCell")); 80 TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "SelectRightCell")); 81 TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_RIGHT, "SelectRightCell")); 82 83 TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "SelectPreviousRow")); 84 TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_UP, "SelectPreviousRow")); 85 TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "SelectNextRow")); 86 TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_DOWN, "SelectNextRow")); 87 88 TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "TraverseLeft")); 89 TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_LEFT, "TraverseLeft")); 90 TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "SelectNextRow")); 91 TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_RIGHT, "SelectNextRow")); 92 TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "TraverseUp")); 93 TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_UP, "TraverseUp")); 94 TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "TraverseDown")); 95 TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_DOWN, "TraverseDown")); 96 97 TABLE_VIEW_BINDINGS.add(new KeyBinding(HOME, "SelectAllToFirstRow").shift()); 98 TABLE_VIEW_BINDINGS.add(new KeyBinding(END, "SelectAllToLastRow").shift()); 99 TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "SelectAllPageUp").shift()); 100 TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "SelectAllPageDown").shift()); 101 102 TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "AlsoSelectPrevious").shift()); 103 TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_UP, "AlsoSelectPrevious").shift()); 104 TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "AlsoSelectNext").shift()); 105 TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_DOWN, "AlsoSelectNext").shift()); 106 107 TABLE_VIEW_BINDINGS.add(new KeyBinding(SPACE, "SelectAllToFocus").shift()); 108 TABLE_VIEW_BINDINGS.add(new KeyBinding(SPACE, "SelectAllToFocusAndSetAnchor").shortcut().shift()); 109 110 // TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "AlsoSelectPreviousCell").shift()); 111 // TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_UP, "AlsoSelectPreviousCell").shift()); 112 // TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "AlsoSelectNextCell").shift()); 113 // TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_DOWN, "AlsoSelectNextCell").shift()); 114 TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "AlsoSelectLeftCell").shift()); 115 TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_LEFT, "AlsoSelectLeftCell").shift()); 116 TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "AlsoSelectRightCell").shift()); 117 TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_RIGHT, "AlsoSelectRightCell").shift()); 118 119 TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "FocusPreviousRow").shortcut()); 120 TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "FocusNextRow").shortcut()); 121 TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "FocusRightCell").shortcut()); 122 TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_RIGHT, "FocusRightCell").shortcut()); 123 TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "FocusLeftCell").shortcut()); 124 TABLE_VIEW_BINDINGS.add(new KeyBinding(KP_LEFT, "FocusLeftCell").shortcut()); 125 TABLE_VIEW_BINDINGS.add(new KeyBinding(A, "SelectAll").shortcut()); 126 TABLE_VIEW_BINDINGS.add(new KeyBinding(HOME, "FocusFirstRow").shortcut()); 127 TABLE_VIEW_BINDINGS.add(new KeyBinding(END, "FocusLastRow").shortcut()); 128 TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "FocusPageUp").shortcut()); 129 TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "FocusPageDown").shortcut()); 130 131 TABLE_VIEW_BINDINGS.add(new KeyBinding(UP, "DiscontinuousSelectPreviousRow").shortcut().shift()); 132 TABLE_VIEW_BINDINGS.add(new KeyBinding(DOWN, "DiscontinuousSelectNextRow").shortcut().shift()); 133 TABLE_VIEW_BINDINGS.add(new KeyBinding(LEFT, "DiscontinuousSelectPreviousColumn").shortcut().shift()); 134 TABLE_VIEW_BINDINGS.add(new KeyBinding(RIGHT, "DiscontinuousSelectNextColumn").shortcut().shift()); 135 TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_UP, "DiscontinuousSelectPageUp").shortcut().shift()); 136 TABLE_VIEW_BINDINGS.add(new KeyBinding(PAGE_DOWN, "DiscontinuousSelectPageDown").shortcut().shift()); 137 TABLE_VIEW_BINDINGS.add(new KeyBinding(HOME, "DiscontinuousSelectAllToFirstRow").shortcut().shift()); 138 TABLE_VIEW_BINDINGS.add(new KeyBinding(END, "DiscontinuousSelectAllToLastRow").shortcut().shift()); 139 140 if (PlatformUtil.isMac()) { 141 TABLE_VIEW_BINDINGS.add(new KeyBinding(SPACE, "toggleFocusOwnerSelection").ctrl().shortcut()); 142 } else { 143 TABLE_VIEW_BINDINGS.add(new KeyBinding(SPACE, "toggleFocusOwnerSelection").ctrl()); 144 } 145 146 TABLE_VIEW_BINDINGS.add(new KeyBinding(ENTER, "Activate")); 147 TABLE_VIEW_BINDINGS.add(new KeyBinding(SPACE, "Activate")); 148 TABLE_VIEW_BINDINGS.add(new KeyBinding(F2, "Activate")); 149 // TABLE_VIEW_BINDINGS.add(new KeyBinding(SPACE, "Activate").ctrl()); 150 151 TABLE_VIEW_BINDINGS.add(new KeyBinding(ESCAPE, "CancelEdit")); 152 } 153 154 @Override protected void callAction(String name) { 155 boolean rtl = (getControl().getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT); 156 157 if ("SelectPreviousRow".equals(name)) selectPreviousRow(); 158 else if ("SelectNextRow".equals(name)) selectNextRow(); 159 else if ("SelectLeftCell".equals(name)) { if (rtl) selectRightCell(); else selectLeftCell(); } 160 else if ("SelectRightCell".equals(name)) { if (rtl) selectLeftCell(); else selectRightCell(); } 161 else if ("SelectFirstRow".equals(name)) selectFirstRow(); 162 else if ("SelectLastRow".equals(name)) selectLastRow(); 163 else if ("SelectAll".equals(name)) selectAll(); 164 else if ("SelectAllPageUp".equals(name)) selectAllPageUp(); 165 else if ("SelectAllPageDown".equals(name)) selectAllPageDown(); 166 else if ("SelectAllToFirstRow".equals(name)) selectAllToFirstRow(); 167 else if ("SelectAllToLastRow".equals(name)) selectAllToLastRow(); 168 else if ("AlsoSelectNext".equals(name)) alsoSelectNext(); 169 else if ("AlsoSelectPrevious".equals(name)) alsoSelectPrevious(); 170 else if ("AlsoSelectLeftCell".equals(name)) { if (rtl) alsoSelectRightCell(); else alsoSelectLeftCell(); } 171 else if ("AlsoSelectRightCell".equals(name)) { if (rtl) alsoSelectLeftCell(); else alsoSelectRightCell(); } 172 else if ("ClearSelection".equals(name)) clearSelection(); 173 else if ("ScrollUp".equals(name)) scrollUp(); 174 else if ("ScrollDown".equals(name)) scrollDown(); 175 else if ("FocusPreviousRow".equals(name)) focusPreviousRow(); 176 else if ("FocusNextRow".equals(name)) focusNextRow(); 177 else if ("FocusLeftCell".equals(name)) { if (rtl) focusRightCell(); else focusLeftCell(); } 178 else if ("FocusRightCell".equals(name)) { if (rtl) focusLeftCell(); else focusRightCell(); } 179 else if ("Activate".equals(name)) activate(); 180 else if ("CancelEdit".equals(name)) cancelEdit(); 181 else if ("FocusFirstRow".equals(name)) focusFirstRow(); 182 else if ("FocusLastRow".equals(name)) focusLastRow(); 183 else if ("toggleFocusOwnerSelection".equals(name)) toggleFocusOwnerSelection(); 184 185 else if ("SelectAllToFocus".equals(name)) selectAllToFocus(false); 186 else if ("SelectAllToFocusAndSetAnchor".equals(name)) selectAllToFocus(true); 187 188 else if ("FocusPageUp".equals(name)) focusPageUp(); 189 else if ("FocusPageDown".equals(name)) focusPageDown(); 190 else if ("DiscontinuousSelectNextRow".equals(name)) discontinuousSelectNextRow(); 191 else if ("DiscontinuousSelectPreviousRow".equals(name)) discontinuousSelectPreviousRow(); 192 else if ("DiscontinuousSelectNextColumn".equals(name)) { if (rtl) discontinuousSelectPreviousColumn(); else discontinuousSelectNextColumn(); } 193 else if ("DiscontinuousSelectPreviousColumn".equals(name)) { if (rtl) discontinuousSelectNextColumn(); else discontinuousSelectPreviousColumn(); } 194 else if ("DiscontinuousSelectPageUp".equals(name)) discontinuousSelectPageUp(); 195 else if ("DiscontinuousSelectPageDown".equals(name)) discontinuousSelectPageDown(); 196 else if ("DiscontinuousSelectAllToLastRow".equals(name)) discontinuousSelectAllToLastRow(); 197 else if ("DiscontinuousSelectAllToFirstRow".equals(name)) discontinuousSelectAllToFirstRow(); 198 else super.callAction(name); 199 } 200 201 @Override protected void callActionForEvent(KeyEvent e) { 202 // RT-12751: we want to keep an eye on the user holding down the shift key, 203 // so that we know when they enter/leave multiple selection mode. This 204 // changes what happens when certain key combinations are pressed. 205 isShiftDown = e.getEventType() == KeyEvent.KEY_PRESSED && e.isShiftDown(); 206 isShortcutDown = e.getEventType() == KeyEvent.KEY_PRESSED && e.isShortcutDown(); 207 208 super.callActionForEvent(e); 209 } 210 211 212 213 /************************************************************************** 214 * * 215 * Internal fields * 216 * * 217 *************************************************************************/ 218 219 protected boolean isShortcutDown = false; 220 protected boolean isShiftDown = false; 221 private boolean selectionPathDeviated = false; 222 protected boolean selectionChanging = false; 223 224 private final SizeLimitedList<TablePositionBase> selectionHistory = new SizeLimitedList<>(10); 225 226 protected final ListChangeListener<TablePositionBase> selectedCellsListener = c -> { 227 while (c.next()) { 228 if (c.wasReplaced()) { 229 if (TreeTableCellBehavior.hasDefaultAnchor(getControl())) { 230 TreeTableCellBehavior.removeAnchor(getControl()); 231 } 232 } 233 234 if (! c.wasAdded()) { 235 continue; 236 } 237 238 TableSelectionModel sm = getSelectionModel(); 239 if (sm == null) return; 240 241 TablePositionBase anchor = getAnchor(); 242 boolean cellSelectionEnabled = sm.isCellSelectionEnabled(); 243 244 int addedSize = c.getAddedSize(); 245 List<TablePositionBase> addedSubList = (List<TablePositionBase>) c.getAddedSubList(); 246 247 for (TablePositionBase tpb : addedSubList) { 248 if (! selectionHistory.contains(tpb)) { 249 selectionHistory.add(tpb); 250 } 251 } 252 253 // newest selection 254 if (addedSize > 0 && ! hasAnchor()) { 255 TablePositionBase tp = addedSubList.get(addedSize - 1); 256 setAnchor(tp); 257 } 258 259 if (anchor != null && cellSelectionEnabled && ! selectionPathDeviated) { 260 // check if the selection is on the same row or column, 261 // otherwise set selectionPathDeviated to true 262 for (int i = 0; i < addedSize; i++) { 263 TablePositionBase tp = addedSubList.get(i); 264 if (anchor.getRow() != -1 && tp.getRow() != anchor.getRow() && tp.getColumn() != anchor.getColumn()) { 265 setSelectionPathDeviated(true); 266 break; 267 } 268 } 269 } 270 } 271 }; 272 273 protected final WeakListChangeListener<TablePositionBase> weakSelectedCellsListener = 274 new WeakListChangeListener<TablePositionBase>(selectedCellsListener); 275 276 277 278 /************************************************************************** 279 * * 280 * Constructors * 281 * * 282 *************************************************************************/ 283 284 public TableViewBehaviorBase(C control) { 285 this(control, null); 286 } 287 288 public TableViewBehaviorBase(C control, List<KeyBinding> bindings) { 289 super(control, bindings == null ? TABLE_VIEW_BINDINGS : bindings); 290 } 291 292 293 294 /************************************************************************** 295 * * 296 * Abstract API * 297 * * 298 *************************************************************************/ 299 300 /** 301 * Call to record the current anchor position 302 */ 303 protected void setAnchor(TablePositionBase tp) { 304 TableCellBehaviorBase.setAnchor(getControl(), tp, false); 305 setSelectionPathDeviated(false); 306 } 307 308 /** 309 * Will return the current anchor position. 310 */ 311 protected TablePositionBase getAnchor() { 312 return TableCellBehaviorBase.getAnchor(getControl(), getFocusedCell()); 313 } 314 315 /** 316 * Returns true if there is an anchor set, and false if not anchor is set. 317 */ 318 protected boolean hasAnchor() { 319 return TableCellBehaviorBase.hasNonDefaultAnchor(getControl()); 320 } 321 322 /** 323 * Returns the number of items in the underlying data model. 324 */ 325 protected abstract int getItemCount(); 326 327 /** 328 * Returns the focus model for the underlying UI control (which must extend 329 * from TableFocusModel). 330 */ 331 protected abstract TableFocusModel getFocusModel(); 332 333 /** 334 * Returns the selection model for the underlying UI control (which must extend 335 * from TableSelectionModel). 336 */ 337 protected abstract TableSelectionModel<T> getSelectionModel(); 338 339 /** 340 * Returns an observable list of all cells that are currently selected in 341 * the selection model of the underlying control. 342 */ 343 protected abstract ObservableList<? extends TablePositionBase/*<C,TC>*/> getSelectedCells(); 344 345 /** 346 * Returns the focused cell from the focus model of the underlying control. 347 */ 348 protected abstract TablePositionBase getFocusedCell(); 349 350 /** 351 * Returns the position of the given table column in the visible leaf columns 352 * list of the underlying control. 353 */ 354 protected abstract int getVisibleLeafIndex(TableColumnBase tc); 355 356 /** 357 * Returns the column at the given index in the visible leaf columns list of 358 * the underlying control. 359 */ 360 protected abstract TableColumnBase getVisibleLeafColumn(int index); 361 362 /** 363 * Begins the edit process in the underlying control for the given row/column 364 * position. 365 */ 366 protected abstract void editCell(int row, TableColumnBase tc); 367 368 /** 369 * Returns an observable list of all visible leaf columns in the underlying 370 * control. 371 */ 372 protected abstract ObservableList<? extends TableColumnBase> getVisibleLeafColumns(); 373 374 /** 375 * Creates a TablePositionBase instance using the underlying controls 376 * concrete implementation for the given row/column intersection. 377 */ 378 protected abstract TablePositionBase<TC> getTablePosition(int row, TableColumnBase<T,?> tc); 379 380 381 382 /************************************************************************** 383 * * 384 * Public API * 385 * * 386 *************************************************************************/ 387 388 /* 389 * Anchor is created upon 390 * - initial selection of an item (by mouse or keyboard) 391 * 392 * Anchor is changed when you 393 * - move the selection to an item by UP/DOWN/LEFT/RIGHT arrow keys 394 * - select an item by mouse click 395 * - add/remove an item to/from an existing selection by CTRL+SPACE shortcut 396 * - add/remove an items to/from an existing selection by CTRL+mouse click 397 * 398 * Note that if an item is removed from an existing selection by 399 * CTRL+SPACE/CTRL+mouse click, anchor still remains on this item even 400 * though it is not selected. 401 * 402 * Anchor is NOT changed when you 403 * - create linear multi-selection by SHIFT+UP/DOWN/LEFT/RIGHT arrow keys 404 * - create linear multi-selection by SHIFT+SPACE arrow keys 405 * - create linear multi-selection by SHIFT+mouse click 406 * 407 * In case there is a discontinuous selection in the list, creating linear 408 * multi-selection between anchor and focused item will cancel the 409 * discontinuous selection. It means that only items that are located between 410 * anchor and focused item will be selected. 411 */ 412 protected void setAnchor(int row, TableColumnBase col) { 413 setAnchor(row == -1 && col == null ? null : getTablePosition(row, col)); 414 } 415 416 private Callback<Boolean, Integer> onScrollPageUp; 417 public void setOnScrollPageUp(Callback<Boolean, Integer> c) { onScrollPageUp = c; } 418 419 private Callback<Boolean, Integer> onScrollPageDown; 420 public void setOnScrollPageDown(Callback<Boolean, Integer> c) { onScrollPageDown = c; } 421 422 private Runnable onFocusPreviousRow; 423 public void setOnFocusPreviousRow(Runnable r) { onFocusPreviousRow = r; } 424 425 private Runnable onFocusNextRow; 426 public void setOnFocusNextRow(Runnable r) { onFocusNextRow = r; } 427 428 private Runnable onSelectPreviousRow; 429 public void setOnSelectPreviousRow(Runnable r) { onSelectPreviousRow = r; } 430 431 private Runnable onSelectNextRow; 432 public void setOnSelectNextRow(Runnable r) { onSelectNextRow = r; } 433 434 private Runnable onMoveToFirstCell; 435 public void setOnMoveToFirstCell(Runnable r) { onMoveToFirstCell = r; } 436 437 private Runnable onMoveToLastCell; 438 public void setOnMoveToLastCell(Runnable r) { onMoveToLastCell = r; } 439 440 private Runnable onSelectRightCell; 441 public void setOnSelectRightCell(Runnable r) { onSelectRightCell = r; } 442 443 private Runnable onSelectLeftCell; 444 public void setOnSelectLeftCell(Runnable r) { onSelectLeftCell = r; } 445 446 @Override public void mousePressed(MouseEvent e) { 447 super.mousePressed(e); 448 449 // // FIXME can't assume (yet) cells.get(0) is necessarily the lead cell 450 // ObservableList<? extends TablePositionBase> cells = getSelectedCells(); 451 // setAnchor(cells.isEmpty() ? null : cells.get(0)); 452 453 if (!getControl().isFocused() && getControl().isFocusTraversable()) { 454 getControl().requestFocus(); 455 } 456 } 457 458 protected boolean isRTL() { 459 return (getControl().getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT); 460 } 461 462 463 /************************************************************************** 464 * * 465 * Private implementation * 466 * * 467 *************************************************************************/ 468 469 private void setSelectionPathDeviated(boolean selectionPathDeviated) { 470 this.selectionPathDeviated = selectionPathDeviated; 471 } 472 473 protected void scrollUp() { 474 TableSelectionModel<T> sm = getSelectionModel(); 475 if (sm == null || getSelectedCells().isEmpty()) return; 476 477 TablePositionBase<TC> selectedCell = getSelectedCells().get(0); 478 479 int newSelectedIndex = -1; 480 if (onScrollPageUp != null) { 481 newSelectedIndex = onScrollPageUp.call(false); 482 } 483 if (newSelectedIndex == -1) return; 484 485 sm.clearAndSelect(newSelectedIndex, selectedCell.getTableColumn()); 486 } 487 488 protected void scrollDown() { 489 TableSelectionModel<T> sm = getSelectionModel(); 490 if (sm == null || getSelectedCells().isEmpty()) return; 491 492 TablePositionBase<TC> selectedCell = getSelectedCells().get(0); 493 494 int newSelectedIndex = -1; 495 if (onScrollPageDown != null) { 496 newSelectedIndex = onScrollPageDown.call(false); 497 } 498 if (newSelectedIndex == -1) return; 499 500 sm.clearAndSelect(newSelectedIndex, selectedCell.getTableColumn()); 501 } 502 503 protected void focusFirstRow() { 504 TableFocusModel fm = getFocusModel(); 505 if (fm == null) return; 506 507 TableColumnBase tc = getFocusedCell() == null ? null : getFocusedCell().getTableColumn(); 508 fm.focus(0, tc); 509 510 if (onMoveToFirstCell != null) onMoveToFirstCell.run(); 511 } 512 513 protected void focusLastRow() { 514 TableFocusModel fm = getFocusModel(); 515 if (fm == null) return; 516 517 TableColumnBase tc = getFocusedCell() == null ? null : getFocusedCell().getTableColumn(); 518 fm.focus(getItemCount() - 1, tc); 519 520 if (onMoveToLastCell != null) onMoveToLastCell.run(); 521 } 522 523 protected void focusPreviousRow() { 524 TableSelectionModel sm = getSelectionModel(); 525 if (sm == null) return; 526 527 TableFocusModel fm = getFocusModel(); 528 if (fm == null) return; 529 530 if (sm.isCellSelectionEnabled()) { 531 fm.focusAboveCell(); 532 } else { 533 fm.focusPrevious(); 534 } 535 536 if (! isShortcutDown || getAnchor() == null) { 537 setAnchor(fm.getFocusedIndex(), null); 538 } 539 540 if (onFocusPreviousRow != null) onFocusPreviousRow.run(); 541 } 542 543 protected void focusNextRow() { 544 TableSelectionModel sm = getSelectionModel(); 545 if (sm == null) return; 546 547 TableFocusModel fm = getFocusModel(); 548 if (fm == null) return; 549 550 if (sm.isCellSelectionEnabled()) { 551 fm.focusBelowCell(); 552 } else { 553 fm.focusNext(); 554 } 555 556 if (! isShortcutDown || getAnchor() == null) { 557 setAnchor(fm.getFocusedIndex(), null); 558 } 559 560 if (onFocusNextRow != null) onFocusNextRow.run(); 561 } 562 563 protected void focusLeftCell() { 564 TableSelectionModel sm = getSelectionModel(); 565 if (sm == null) return; 566 567 TableFocusModel fm = getFocusModel(); 568 if (fm == null) return; 569 570 fm.focusLeftCell(); 571 if (onFocusPreviousRow != null) onFocusPreviousRow.run(); 572 } 573 574 protected void focusRightCell() { 575 TableSelectionModel sm = getSelectionModel(); 576 if (sm == null) return; 577 578 TableFocusModel fm = getFocusModel(); 579 if (fm == null) return; 580 581 fm.focusRightCell(); 582 if (onFocusNextRow != null) onFocusNextRow.run(); 583 } 584 585 protected void focusPageUp() { 586 int newFocusIndex = onScrollPageUp.call(true); 587 588 TableFocusModel fm = getFocusModel(); 589 if (fm == null) return; 590 TableColumnBase tc = getFocusedCell() == null ? null : getFocusedCell().getTableColumn(); 591 fm.focus(newFocusIndex, tc); 592 } 593 594 protected void focusPageDown() { 595 int newFocusIndex = onScrollPageDown.call(true); 596 597 TableFocusModel fm = getFocusModel(); 598 if (fm == null) return; 599 TableColumnBase tc = getFocusedCell() == null ? null : getFocusedCell().getTableColumn(); 600 fm.focus(newFocusIndex, tc); 601 } 602 603 protected void clearSelection() { 604 TableSelectionModel sm = getSelectionModel(); 605 if (sm == null) return; 606 607 sm.clearSelection(); 608 } 609 610 protected void clearSelectionOutsideRange(int start, int end, TableColumnBase<T,?> column) { 611 TableSelectionModel<T> sm = getSelectionModel(); 612 if (sm == null) return; 613 614 int min = Math.min(start, end); 615 int max = Math.max(start, end); 616 617 List<Integer> indices = new ArrayList<Integer>(sm.getSelectedIndices()); 618 619 selectionChanging = true; 620 for (int i = 0; i < indices.size(); i++) { 621 int index = indices.get(i); 622 if (index < min || index > max) { 623 sm.clearSelection(index, column); 624 } 625 } 626 selectionChanging = false; 627 } 628 629 protected void alsoSelectPrevious() { 630 TableSelectionModel sm = getSelectionModel(); 631 if (sm == null) return; 632 633 if (sm.getSelectionMode() == SelectionMode.SINGLE) { 634 selectPreviousRow(); 635 return; 636 } 637 638 TableFocusModel fm = getFocusModel(); 639 if (fm == null) return; 640 641 if (sm.isCellSelectionEnabled()) { 642 updateCellVerticalSelection(-1, () -> { 643 getSelectionModel().selectAboveCell(); 644 }); 645 } else { 646 if (isShiftDown && hasAnchor()) { 647 updateRowSelection(-1); 648 } else { 649 sm.selectPrevious(); 650 } 651 } 652 onSelectPreviousRow.run(); 653 } 654 655 protected void alsoSelectNext() { 656 TableSelectionModel sm = getSelectionModel(); 657 if (sm == null) return; 658 659 if (sm.getSelectionMode() == SelectionMode.SINGLE) { 660 selectNextRow(); 661 return; 662 } 663 664 TableFocusModel fm = getFocusModel(); 665 if (fm == null) return; 666 667 if (sm.isCellSelectionEnabled()) { 668 updateCellVerticalSelection(1, () -> { 669 getSelectionModel().selectBelowCell(); 670 }); 671 } else { 672 if (isShiftDown && hasAnchor()) { 673 updateRowSelection(1); 674 } else { 675 sm.selectNext(); 676 } 677 } 678 onSelectNextRow.run(); 679 } 680 681 protected void alsoSelectLeftCell() { 682 updateCellHorizontalSelection(-1, () -> { 683 getSelectionModel().selectLeftCell(); 684 }); 685 686 onSelectLeftCell.run(); 687 } 688 689 protected void alsoSelectRightCell() { 690 updateCellHorizontalSelection(1, () -> { 691 getSelectionModel().selectRightCell(); 692 }); 693 694 onSelectRightCell.run(); 695 } 696 697 protected void updateRowSelection(int delta) { 698 TableSelectionModel sm = getSelectionModel(); 699 if (sm == null || sm.getSelectionMode() == SelectionMode.SINGLE) return; 700 701 TableFocusModel fm = getFocusModel(); 702 if (fm == null) return; 703 704 int newRow = fm.getFocusedIndex() + delta; 705 TablePositionBase anchor = getAnchor(); 706 707 if (! hasAnchor()) { 708 setAnchor(getFocusedCell()); 709 } 710 711 if (sm.getSelectedIndices().size() > 1) { 712 clearSelectionOutsideRange(anchor.getRow(), newRow, null); 713 } 714 715 if (anchor.getRow() > newRow) { 716 sm.selectRange(anchor.getRow(), newRow - 1); 717 } else { 718 sm.selectRange(anchor.getRow(), newRow + 1); 719 } 720 } 721 722 protected void updateCellVerticalSelection(int delta, Runnable defaultAction) { 723 TableSelectionModel sm = getSelectionModel(); 724 if (sm == null || sm.getSelectionMode() == SelectionMode.SINGLE) return; 725 726 TableFocusModel fm = getFocusModel(); 727 if (fm == null) return; 728 729 final TablePositionBase focusedCell = getFocusedCell(); 730 final int focusedCellRow = focusedCell.getRow(); 731 732 if (isShiftDown && sm.isSelected(focusedCellRow + delta, focusedCell.getTableColumn())) { 733 int newFocusOwner = focusedCellRow + delta; 734 735 // work out if we're backtracking 736 boolean backtracking = false; 737 if (selectionHistory.size() >= 2) { 738 TablePositionBase<TC> secondToLastSelectedCell = selectionHistory.get(1); 739 backtracking = secondToLastSelectedCell.getRow() == newFocusOwner && 740 secondToLastSelectedCell.getColumn() == focusedCell.getColumn(); 741 } 742 743 // if the selection path has deviated from the anchor row / column, then we need to see if we're moving 744 // backwards to the previous selection or not (as it determines what cell row we clear out) 745 int cellRowToClear = selectionPathDeviated ? 746 (backtracking ? focusedCellRow : newFocusOwner) : 747 focusedCellRow; 748 749 sm.clearSelection(cellRowToClear, focusedCell.getTableColumn()); 750 fm.focus(newFocusOwner, focusedCell.getTableColumn()); 751 } else if (isShiftDown && getAnchor() != null && ! selectionPathDeviated) { 752 int newRow = fm.getFocusedIndex() + delta; 753 754 // we don't let the newRow go outside the bounds of the data 755 newRow = Math.max(Math.min(getItemCount() - 1, newRow), 0); 756 757 int start = Math.min(getAnchor().getRow(), newRow); 758 int end = Math.max(getAnchor().getRow(), newRow); 759 760 if (sm.getSelectedIndices().size() > 1) { 761 clearSelectionOutsideRange(start, end, focusedCell.getTableColumn()); 762 } 763 764 for (int _row = start; _row <= end; _row++) { 765 if (sm.isSelected(_row, focusedCell.getTableColumn())) { 766 continue; 767 } 768 sm.select(_row, focusedCell.getTableColumn()); 769 } 770 fm.focus(newRow, focusedCell.getTableColumn()); 771 } else { 772 final int focusIndex = fm.getFocusedIndex(); 773 if (! sm.isSelected(focusIndex, focusedCell.getTableColumn())) { 774 sm.select(focusIndex, focusedCell.getTableColumn()); 775 } 776 defaultAction.run(); 777 } 778 } 779 780 protected void updateCellHorizontalSelection(int delta, Runnable defaultAction) { 781 TableSelectionModel sm = getSelectionModel(); 782 if (sm == null || sm.getSelectionMode() == SelectionMode.SINGLE) return; 783 784 TableFocusModel fm = getFocusModel(); 785 if (fm == null) return; 786 787 final TablePositionBase focusedCell = getFocusedCell(); 788 if (focusedCell == null || focusedCell.getTableColumn() == null) return; 789 790 boolean atEnd = false; 791 TableColumnBase adjacentColumn = getColumn(focusedCell.getTableColumn(), delta); 792 if (adjacentColumn == null) { 793 // if adjacentColumn is null, we use the focusedCell column, as we are 794 // most probably at the very beginning or end of the row 795 adjacentColumn = focusedCell.getTableColumn(); 796 atEnd = true; 797 } 798 799 final int focusedCellRow = focusedCell.getRow(); 800 801 if (isShiftDown && sm.isSelected(focusedCellRow, adjacentColumn)) { 802 if (atEnd) { 803 return; 804 } 805 806 // work out if we're backtracking 807 boolean backtracking = false; 808 ObservableList<? extends TablePositionBase> selectedCells = getSelectedCells(); 809 if (selectedCells.size() >= 2) { 810 TablePositionBase<TC> secondToLastSelectedCell = selectedCells.get(selectedCells.size() - 2); 811 backtracking = secondToLastSelectedCell.getRow() == focusedCellRow && 812 secondToLastSelectedCell.getTableColumn().equals(adjacentColumn); 813 } 814 815 // if the selection path has deviated from the anchor row / column, then we need to see if we're moving 816 // backwards to the previous selection or not (as it determines what cell column we clear out) 817 TableColumnBase<?,?> cellColumnToClear = selectionPathDeviated ? 818 (backtracking ? focusedCell.getTableColumn() : adjacentColumn) : 819 focusedCell.getTableColumn(); 820 821 sm.clearSelection(focusedCellRow, cellColumnToClear); 822 fm.focus(focusedCellRow, adjacentColumn); 823 } else if (isShiftDown && getAnchor() != null && ! selectionPathDeviated) { 824 final int anchorColumn = getAnchor().getColumn(); 825 826 // we don't let the newColumn go outside the bounds of the data 827 int newColumn = getVisibleLeafIndex(focusedCell.getTableColumn()) + delta; 828 newColumn = Math.max(Math.min(getVisibleLeafColumns().size() - 1, newColumn), 0); 829 830 int start = Math.min(anchorColumn, newColumn); 831 int end = Math.max(anchorColumn, newColumn); 832 833 for (int _col = start; _col <= end; _col++) { 834 sm.select(focusedCell.getRow(), getColumn(_col)); 835 } 836 fm.focus(focusedCell.getRow(), getColumn(newColumn)); 837 } else { 838 defaultAction.run(); 839 } 840 } 841 842 protected TableColumnBase getColumn(int index) { 843 return getVisibleLeafColumn(index); 844 } 845 846 protected TableColumnBase getColumn(TableColumnBase tc, int delta) { 847 return getVisibleLeafColumn(getVisibleLeafIndex(tc) + delta); 848 } 849 850 protected void selectFirstRow() { 851 TableSelectionModel sm = getSelectionModel(); 852 if (sm == null) return; 853 854 ObservableList<? extends TablePositionBase> selection = getSelectedCells(); 855 TableColumnBase<?,?> selectedColumn = selection.size() == 0 ? null : selection.get(0).getTableColumn(); 856 sm.clearAndSelect(0, selectedColumn); 857 858 if (onMoveToFirstCell != null) onMoveToFirstCell.run(); 859 } 860 861 protected void selectLastRow() { 862 TableSelectionModel sm = getSelectionModel(); 863 if (sm == null) return; 864 865 ObservableList<? extends TablePositionBase> selection = getSelectedCells(); 866 TableColumnBase<?,?> selectedColumn = selection.size() == 0 ? null : selection.get(0).getTableColumn(); 867 sm.clearAndSelect(getItemCount() - 1, selectedColumn); 868 869 if (onMoveToLastCell != null) onMoveToLastCell.run(); 870 } 871 872 protected void selectPreviousRow() { 873 selectCell(-1, 0); 874 if (onSelectPreviousRow != null) onSelectPreviousRow.run(); 875 } 876 877 protected void selectNextRow() { 878 selectCell(1, 0); 879 if (onSelectNextRow != null) onSelectNextRow.run(); 880 } 881 882 protected void selectLeftCell() { 883 selectCell(0, -1); 884 if (onSelectLeftCell != null) onSelectLeftCell.run(); 885 } 886 887 protected void selectRightCell() { 888 selectCell(0, 1); 889 if (onSelectRightCell != null) onSelectRightCell.run(); 890 } 891 892 protected void selectCell(int rowDiff, int columnDiff) { 893 TableSelectionModel sm = getSelectionModel(); 894 if (sm == null) return; 895 896 TableFocusModel fm = getFocusModel(); 897 if (fm == null) return; 898 899 TablePositionBase<TC> focusedCell = getFocusedCell(); 900 int currentRow = focusedCell.getRow(); 901 int currentColumn = getVisibleLeafIndex(focusedCell.getTableColumn()); 902 903 if (rowDiff < 0 && currentRow <= 0) return; 904 else if (rowDiff > 0 && currentRow >= getItemCount() - 1) return; 905 else if (columnDiff < 0 && currentColumn <= 0) return; 906 else if (columnDiff > 0 && currentColumn >= getVisibleLeafColumns().size() - 1) return; 907 else if (columnDiff > 0 && currentColumn == -1) return; 908 909 TableColumnBase tc = focusedCell.getTableColumn(); 910 tc = getColumn(tc, columnDiff); 911 912 int row = focusedCell.getRow() + rowDiff; 913 sm.clearAndSelect(row, tc); 914 setAnchor(row, tc); 915 } 916 917 protected void cancelEdit() { 918 editCell(-1, null); 919 } 920 921 protected void activate() { 922 TableSelectionModel sm = getSelectionModel(); 923 if (sm == null) return; 924 925 TableFocusModel fm = getFocusModel(); 926 if (fm == null) return; 927 928 TablePositionBase<TC> cell = getFocusedCell(); 929 sm.select(cell.getRow(), cell.getTableColumn()); 930 setAnchor(cell); 931 932 // edit this row also 933 if (cell.getRow() >= 0) { 934 editCell(cell.getRow(), cell.getTableColumn()); 935 } 936 } 937 938 protected void selectAllToFocus(boolean setAnchorToFocusIndex) { 939 TableSelectionModel sm = getSelectionModel(); 940 if (sm == null) return; 941 942 TableFocusModel fm = getFocusModel(); 943 if (fm == null) return; 944 945 TablePositionBase<TC> focusedCell = getFocusedCell(); 946 int focusRow = focusedCell.getRow(); 947 948 TablePositionBase<TC> anchor = getAnchor(); 949 int anchorRow = anchor.getRow(); 950 951 sm.clearSelection(); 952 if (! sm.isCellSelectionEnabled()) { 953 int startPos = anchorRow; 954 int endPos = anchorRow > focusRow ? focusRow - 1 : focusRow + 1; 955 sm.selectRange(startPos, endPos); 956 } else { 957 // we add all cells/rows between the current selection focus and 958 // the anchor (inclusive) to the current selection. 959 // We want focus to end up on the current focus position. 960 sm.selectRange(anchor.getRow(), anchor.getTableColumn(), 961 focusedCell.getRow(), focusedCell.getTableColumn()); 962 } 963 964 setAnchor(setAnchorToFocusIndex ? focusedCell : anchor); 965 } 966 967 protected void selectAll() { 968 TableSelectionModel sm = getSelectionModel(); 969 if (sm == null) return; 970 sm.selectAll(); 971 } 972 973 protected void selectAllToFirstRow() { 974 TableSelectionModel sm = getSelectionModel(); 975 if (sm == null) return; 976 977 TableFocusModel fm = getFocusModel(); 978 if (fm == null) return; 979 980 final boolean isSingleSelection = sm.getSelectionMode() == SelectionMode.SINGLE; 981 final TablePositionBase focusedCell = getFocusedCell(); 982 final TableColumnBase<?,?> column = getFocusedCell().getTableColumn(); 983 int leadIndex = focusedCell.getRow(); 984 985 if (isShiftDown) { 986 leadIndex = getAnchor() == null ? leadIndex : getAnchor().getRow(); 987 } 988 989 sm.clearSelection(); 990 if (! sm.isCellSelectionEnabled()) { 991 // we are going from 0 to one before the focused cell as that is 992 // the requirement of selectRange, so we call focus on the 0th row 993 if (isSingleSelection) { 994 sm.select(0); 995 } else { 996 sm.selectRange(leadIndex, -1); 997 } 998 fm.focus(0); 999 } else { 1000 if (isSingleSelection) { 1001 sm.select(0, column); 1002 } else { 1003 sm.selectRange(leadIndex, column, -1, column); 1004 } 1005 fm.focus(0, column); 1006 } 1007 1008 if (isShiftDown) { 1009 setAnchor(leadIndex, column); 1010 } 1011 1012 if (onMoveToFirstCell != null) onMoveToFirstCell.run(); 1013 } 1014 1015 protected void selectAllToLastRow() { 1016 TableSelectionModel sm = getSelectionModel(); 1017 if (sm == null) return; 1018 1019 TableFocusModel fm = getFocusModel(); 1020 if (fm == null) return; 1021 1022 final int itemCount = getItemCount(); 1023 final TablePositionBase focusedCell = getFocusedCell(); 1024 final TableColumnBase<?,?> column = getFocusedCell().getTableColumn(); 1025 int leadIndex = focusedCell.getRow(); 1026 1027 if (isShiftDown) { 1028 leadIndex = getAnchor() == null ? leadIndex : getAnchor().getRow(); 1029 } 1030 1031 sm.clearSelection(); 1032 if (! sm.isCellSelectionEnabled()) { 1033 sm.selectRange(leadIndex, itemCount); 1034 } else { 1035 sm.selectRange(leadIndex, column, itemCount - 1, column); 1036 } 1037 1038 if (isShiftDown) { 1039 setAnchor(leadIndex, column); 1040 } 1041 1042 if (onMoveToLastCell != null) onMoveToLastCell.run(); 1043 } 1044 1045 protected void selectAllPageUp() { 1046 TableSelectionModel sm = getSelectionModel(); 1047 if (sm == null) return; 1048 1049 TableFocusModel fm = getFocusModel(); 1050 if (fm == null) return; 1051 1052 int leadIndex = fm.getFocusedIndex(); 1053 final TableColumnBase col = sm.isCellSelectionEnabled() ? getFocusedCell().getTableColumn() : null; 1054 if (isShiftDown) { 1055 leadIndex = getAnchor() == null ? leadIndex : getAnchor().getRow(); 1056 setAnchor(leadIndex, col); 1057 } 1058 1059 int leadSelectedIndex = onScrollPageUp.call(false); 1060 1061 selectionChanging = true; 1062 if (sm.getSelectionMode() == null || sm.getSelectionMode() == SelectionMode.SINGLE) { 1063 if (sm.isCellSelectionEnabled()) { 1064 sm.select(leadSelectedIndex, col); 1065 } else { 1066 sm.select(leadSelectedIndex); 1067 } 1068 } else { 1069 sm.clearSelection(); 1070 if (sm.isCellSelectionEnabled()) { 1071 sm.selectRange(leadIndex, col, leadSelectedIndex, col); 1072 } else { 1073 // fix for RT-34407 1074 int adjust = leadIndex < leadSelectedIndex ? 1 : -1; 1075 sm.selectRange(leadIndex, leadSelectedIndex + adjust); 1076 } 1077 } 1078 selectionChanging = false; 1079 } 1080 1081 protected void selectAllPageDown() { 1082 TableSelectionModel sm = getSelectionModel(); 1083 if (sm == null) return; 1084 1085 TableFocusModel fm = getFocusModel(); 1086 if (fm == null) return; 1087 1088 int leadIndex = fm.getFocusedIndex(); 1089 final TableColumnBase col = sm.isCellSelectionEnabled() ? getFocusedCell().getTableColumn() : null; 1090 if (isShiftDown) { 1091 leadIndex = getAnchor() == null ? leadIndex : getAnchor().getRow(); 1092 setAnchor(leadIndex, col); 1093 } 1094 1095 int leadSelectedIndex = onScrollPageDown.call(false); 1096 1097 selectionChanging = true; 1098 if (sm.getSelectionMode() == null || sm.getSelectionMode() == SelectionMode.SINGLE) { 1099 if (sm.isCellSelectionEnabled()) { 1100 sm.select(leadSelectedIndex, col); 1101 } else { 1102 sm.select(leadSelectedIndex); 1103 } 1104 } else { 1105 sm.clearSelection(); 1106 1107 if (sm.isCellSelectionEnabled()) { 1108 sm.selectRange(leadIndex, col, leadSelectedIndex, col); 1109 } else { 1110 // fix for RT-34407 1111 int adjust = leadIndex < leadSelectedIndex ? 1 : -1; 1112 sm.selectRange(leadIndex, leadSelectedIndex + adjust); 1113 } 1114 } 1115 selectionChanging = false; 1116 } 1117 1118 protected void toggleFocusOwnerSelection() { 1119 TableSelectionModel sm = getSelectionModel(); 1120 if (sm == null) return; 1121 1122 TableFocusModel fm = getFocusModel(); 1123 if (fm == null) return; 1124 1125 TablePositionBase focusedCell = getFocusedCell(); 1126 1127 if (sm.isSelected(focusedCell.getRow(), focusedCell.getTableColumn())) { 1128 sm.clearSelection(focusedCell.getRow(), focusedCell.getTableColumn()); 1129 fm.focus(focusedCell.getRow(), focusedCell.getTableColumn()); 1130 } else { 1131 sm.select(focusedCell.getRow(), focusedCell.getTableColumn()); 1132 } 1133 1134 setAnchor(focusedCell.getRow(), focusedCell.getTableColumn()); 1135 } 1136 1137 // This functionality was added, but then removed when it was realised by 1138 // UX that TableView should not include 'spreadsheet-like' functionality. 1139 // When / if we ever introduce this kind of control, this functionality can 1140 // be re-enabled then. 1141 /* 1142 protected void moveToLeftMostColumn() { 1143 // Functionality as described in RT-12752 1144 if (onMoveToLeftMostColumn != null) onMoveToLeftMostColumn.run(); 1145 1146 TableSelectionModel sm = getSelectionModel(); 1147 if (sm == null || ! sm.isCellSelectionEnabled()) return; 1148 1149 TableFocusModel fm = getFocusModel(); 1150 if (fm == null) return; 1151 1152 TablePosition focusedCell = fm.getFocusedCell(); 1153 1154 TableColumn endColumn = getControl().getVisibleLeafColumn(0); 1155 sm.clearAndSelect(focusedCell.getRow(), endColumn); 1156 } 1157 1158 protected void moveToRightMostColumn() { 1159 // Functionality as described in RT-12752 1160 if (onMoveToRightMostColumn != null) onMoveToRightMostColumn.run(); 1161 1162 TableSelectionModel sm = getSelectionModel(); 1163 if (sm == null || ! sm.isCellSelectionEnabled()) return; 1164 1165 TableFocusModel fm = getFocusModel(); 1166 if (fm == null) return; 1167 1168 TablePosition focusedCell = fm.getFocusedCell(); 1169 1170 TableColumn endColumn = getControl().getVisibleLeafColumn(getControl().getVisibleLeafColumns().size() - 1); 1171 sm.clearAndSelect(focusedCell.getRow(), endColumn); 1172 } 1173 */ 1174 1175 1176 /************************************************************************** 1177 * Discontinuous Selection * 1178 *************************************************************************/ 1179 1180 protected void discontinuousSelectPreviousRow() { 1181 TableSelectionModel sm = getSelectionModel(); 1182 if (sm == null) return; 1183 1184 if (sm.getSelectionMode() != SelectionMode.MULTIPLE) { 1185 selectPreviousRow(); 1186 return; 1187 } 1188 1189 TableFocusModel fm = getFocusModel(); 1190 if (fm == null) return; 1191 1192 int focusIndex = fm.getFocusedIndex(); 1193 final int newFocusIndex = focusIndex - 1; 1194 if (newFocusIndex < 0) return; 1195 1196 int startIndex = focusIndex; 1197 final TableColumnBase col = sm.isCellSelectionEnabled() ? getFocusedCell().getTableColumn() : null; 1198 if (isShiftDown) { 1199 startIndex = getAnchor() == null ? focusIndex : getAnchor().getRow(); 1200 } 1201 1202 if (! sm.isCellSelectionEnabled()) { 1203 sm.selectRange(newFocusIndex, startIndex + 1); 1204 fm.focus(newFocusIndex); 1205 } else { 1206 for (int i = newFocusIndex; i < startIndex + 1; i++) { 1207 sm.select(i, col); 1208 } 1209 fm.focus(newFocusIndex, col); 1210 } 1211 1212 if (onFocusPreviousRow != null) onFocusPreviousRow.run(); 1213 } 1214 1215 protected void discontinuousSelectNextRow() { 1216 TableSelectionModel sm = getSelectionModel(); 1217 if (sm == null) return; 1218 1219 if (sm.getSelectionMode() != SelectionMode.MULTIPLE) { 1220 selectNextRow(); 1221 return; 1222 } 1223 1224 TableFocusModel fm = getFocusModel(); 1225 if (fm == null) return; 1226 1227 int focusIndex = fm.getFocusedIndex(); 1228 final int newFocusIndex = focusIndex + 1; 1229 if (newFocusIndex >= getItemCount()) return; 1230 1231 int startIndex = focusIndex; 1232 final TableColumnBase col = sm.isCellSelectionEnabled() ? getFocusedCell().getTableColumn() : null; 1233 if (isShiftDown) { 1234 startIndex = getAnchor() == null ? focusIndex : getAnchor().getRow(); 1235 } 1236 1237 if (! sm.isCellSelectionEnabled()) { 1238 sm.selectRange(startIndex, newFocusIndex + 1); 1239 fm.focus(newFocusIndex); 1240 } else { 1241 for (int i = startIndex; i < newFocusIndex + 1; i++) { 1242 sm.select(i, col); 1243 } 1244 fm.focus(newFocusIndex, col); 1245 } 1246 1247 if (onFocusNextRow != null) onFocusNextRow.run(); 1248 } 1249 1250 protected void discontinuousSelectPreviousColumn() { 1251 TableSelectionModel sm = getSelectionModel(); 1252 if (sm == null || ! sm.isCellSelectionEnabled()) return; 1253 1254 TableFocusModel fm = getFocusModel(); 1255 if (fm == null) return; 1256 1257 TableColumnBase tc = getColumn(getFocusedCell().getTableColumn(), -1); 1258 sm.select(fm.getFocusedIndex(), tc); 1259 } 1260 1261 protected void discontinuousSelectNextColumn() { 1262 TableSelectionModel sm = getSelectionModel(); 1263 if (sm == null || ! sm.isCellSelectionEnabled()) return; 1264 1265 TableFocusModel fm = getFocusModel(); 1266 if (fm == null) return; 1267 1268 TableColumnBase tc = getColumn(getFocusedCell().getTableColumn(), 1); 1269 sm.select(fm.getFocusedIndex(), tc); 1270 } 1271 1272 protected void discontinuousSelectPageUp() { 1273 TableSelectionModel sm = getSelectionModel(); 1274 if (sm == null) return; 1275 1276 TableFocusModel fm = getFocusModel(); 1277 if (fm == null) return; 1278 1279 int anchor = hasAnchor() ? getAnchor().getRow() : fm.getFocusedIndex(); 1280 int leadSelectedIndex = onScrollPageUp.call(false); 1281 1282 if (! sm.isCellSelectionEnabled()) { 1283 sm.selectRange(anchor, leadSelectedIndex - 1); 1284 } 1285 } 1286 1287 protected void discontinuousSelectPageDown() { 1288 TableSelectionModel sm = getSelectionModel(); 1289 if (sm == null) return; 1290 1291 TableFocusModel fm = getFocusModel(); 1292 if (fm == null) return; 1293 1294 int anchor = hasAnchor() ? getAnchor().getRow() : fm.getFocusedIndex(); 1295 int leadSelectedIndex = onScrollPageDown.call(false); 1296 1297 if (! sm.isCellSelectionEnabled()) { 1298 sm.selectRange(anchor, leadSelectedIndex + 1); 1299 } 1300 } 1301 1302 protected void discontinuousSelectAllToFirstRow() { 1303 TableSelectionModel sm = getSelectionModel(); 1304 if (sm == null) return; 1305 1306 TableFocusModel fm = getFocusModel(); 1307 if (fm == null) return; 1308 1309 int index = fm.getFocusedIndex(); 1310 1311 if (! sm.isCellSelectionEnabled()) { 1312 sm.selectRange(0, index); 1313 fm.focus(0); 1314 } else { 1315 for (int i = 0; i < index; i++) { 1316 sm.select(i, getFocusedCell().getTableColumn()); 1317 } 1318 fm.focus(0, getFocusedCell().getTableColumn()); 1319 } 1320 1321 if (onMoveToFirstCell != null) onMoveToFirstCell.run(); 1322 } 1323 1324 protected void discontinuousSelectAllToLastRow() { 1325 TableSelectionModel sm = getSelectionModel(); 1326 if (sm == null) return; 1327 1328 TableFocusModel fm = getFocusModel(); 1329 if (fm == null) return; 1330 1331 int index = fm.getFocusedIndex() + 1; 1332 1333 if (! sm.isCellSelectionEnabled()) { 1334 sm.selectRange(index, getItemCount()); 1335 } else { 1336 for (int i = index; i < getItemCount(); i++) { 1337 sm.select(i, getFocusedCell().getTableColumn()); 1338 } 1339 } 1340 1341 if (onMoveToLastCell != null) onMoveToLastCell.run(); 1342 } 1343 }