1 /******************************************************************************* 2 * Copyright (c) 2006, 2016 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Tom Schindl <tom.schindl@bestsolution.at> - refactoring (bug 153993) 14 * fix in bug: 151295,178946,166500,195908,201906,207676,180504,216706,218336 15 * Tony Juckel - Bug 130854 - JFace TableViewer ignoring ICellEditor validator state 16 * Andrew Obuchowicz <aobuchow@redhat.com> - Bug 549194: Add TABBING_CYCLE_IN_VIEWER 17 *******************************************************************************/ 18 19 package org.eclipse.jface.viewers; 20 21 import org.eclipse.core.runtime.ListenerList; 22 import org.eclipse.swt.SWT; 23 import org.eclipse.swt.events.DisposeListener; 24 import org.eclipse.swt.events.FocusAdapter; 25 import org.eclipse.swt.events.FocusEvent; 26 import org.eclipse.swt.events.FocusListener; 27 import org.eclipse.swt.events.MouseAdapter; 28 import org.eclipse.swt.events.MouseEvent; 29 import org.eclipse.swt.events.MouseListener; 30 import org.eclipse.swt.events.TraverseEvent; 31 import org.eclipse.swt.events.TraverseListener; 32 import org.eclipse.swt.widgets.Control; 33 import org.eclipse.swt.widgets.Item; 34 35 /** 36 * This is the base for all editor implementations of Viewers. ColumnViewer 37 * implementors have to subclass this class and implement the missing methods 38 * 39 * @since 3.3 40 * @see TableViewerEditor 41 * @see TreeViewerEditor 42 */ 43 public abstract class ColumnViewerEditor { 44 private CellEditor cellEditor; 45 46 private ICellEditorListener cellEditorListener; 47 48 private FocusListener focusListener; 49 50 private MouseListener mouseListener; 51 52 private ColumnViewer viewer; 53 54 private TraverseListener tabeditingListener; 55 56 private ViewerCell cell; 57 58 private ListenerList<ColumnViewerEditorActivationListener> editorActivationListener; 59 60 private ColumnViewerEditorActivationStrategy editorActivationStrategy; 61 62 private boolean inEditorDeactivation; 63 64 private DisposeListener disposeListener; 65 66 /** 67 * Tabbing from cell to cell is turned off 68 */ 69 public static final int DEFAULT = 1; 70 71 /** 72 * Should if the end of the row is reach started from the start/end of the 73 * row below/above 74 */ 75 public static final int TABBING_MOVE_TO_ROW_NEIGHBOR = 1 << 1; 76 77 /** 78 * Should if the end of the row is reach started from the beginning in the 79 * same row 80 */ 81 public static final int TABBING_CYCLE_IN_ROW = 1 << 2; 82 83 /** 84 * Support tabbing to Cell above/below the current cell 85 */ 86 public static final int TABBING_VERTICAL = 1 << 3; 87 88 /** 89 * Should tabbing from column to column with in one row be supported 90 */ 91 public static final int TABBING_HORIZONTAL = 1 << 4; 92 93 /** 94 * Style mask used to enable keyboard activation 95 */ 96 public static final int KEYBOARD_ACTIVATION = 1 << 5; 97 98 /** 99 * Style mask used to turn <b>off</b> the feature that an editor activation 100 * is canceled on double click. It is also possible to turn off this feature 101 * per cell-editor using {@link CellEditor#getDoubleClickTimeout()} 102 * @since 3.4 103 */ 104 public static final int KEEP_EDITOR_ON_DOUBLE_CLICK = 1 << 6; 105 106 /** 107 * Should if the end of the viewer is reached, start from the first element in 108 * the viewer (conversely, hitting shift-tab in the first element of the viewer 109 * results in jumping to the last element of the viewer) 110 * 111 * @since 3.17 112 */ 113 public static final int TABBING_CYCLE_IN_VIEWER = 1 << 7; 114 115 116 private int feature; 117 118 /** 119 * @param viewer the viewer this editor is attached to 120 * @param editorActivationStrategy the strategy used to decide about editor 121 * activation 122 * @param feature bit mask controlling the editor 123 * <ul> 124 * <li>{@link ColumnViewerEditor#DEFAULT}</li> 125 * <li>{@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}</li> 126 * <li>{@link ColumnViewerEditor#TABBING_HORIZONTAL}</li> 127 * <li>{@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}</li> 128 * <li>{@link ColumnViewerEditor#TABBING_VERTICAL}</li> 129 * <li>{@link ColumnViewerEditor#TABBING_CYCLE_IN_VIEWER}</li> 130 * </ul> 131 */ ColumnViewerEditor(final ColumnViewer viewer, ColumnViewerEditorActivationStrategy editorActivationStrategy, int feature)132 protected ColumnViewerEditor(final ColumnViewer viewer, 133 ColumnViewerEditorActivationStrategy editorActivationStrategy, 134 int feature) { 135 this.viewer = viewer; 136 this.editorActivationStrategy = editorActivationStrategy; 137 if ((feature & KEYBOARD_ACTIVATION) == KEYBOARD_ACTIVATION) { 138 this.editorActivationStrategy 139 .setEnableEditorActivationWithKeyboard(true); 140 } 141 this.feature = feature; 142 this.disposeListener = e -> { 143 if( viewer.isCellEditorActive() ) { 144 cancelEditing(); 145 } 146 }; 147 initCellEditorListener(); 148 } 149 initCellEditorListener()150 private void initCellEditorListener() { 151 cellEditorListener = new ICellEditorListener() { 152 @Override 153 public void editorValueChanged(boolean oldValidState, 154 boolean newValidState) { 155 // Ignore. 156 } 157 158 @Override 159 public void cancelEditor() { 160 ColumnViewerEditor.this.cancelEditing(); 161 } 162 163 @Override 164 public void applyEditorValue() { 165 ColumnViewerEditor.this.applyEditorValue(); 166 } 167 }; 168 } 169 activateCellEditor(final ColumnViewerEditorActivationEvent activationEvent)170 private boolean activateCellEditor(final ColumnViewerEditorActivationEvent activationEvent) { 171 172 ViewerColumn part = viewer.getViewerColumn(cell.getColumnIndex()); 173 Object element = cell.getElement(); 174 175 if (part != null && part.getEditingSupport() != null 176 && part.getEditingSupport().canEdit(element)) { 177 cellEditor = part.getEditingSupport().getCellEditor(element); 178 if (cellEditor != null) { 179 int timeout = cellEditor.getDoubleClickTimeout(); 180 181 final int activationTime; 182 183 if (timeout != 0) { 184 activationTime = activationEvent.time + timeout; 185 } else { 186 activationTime = 0; 187 } 188 189 if (editorActivationListener != null && !editorActivationListener.isEmpty()) { 190 for (ColumnViewerEditorActivationListener ls : editorActivationListener) { 191 ls.beforeEditorActivated(activationEvent); 192 193 // Was the activation canceled ? 194 if (activationEvent.cancel) { 195 return false; 196 } 197 } 198 } 199 200 updateFocusCell(cell, activationEvent); 201 202 cellEditor.addListener(cellEditorListener); 203 part.getEditingSupport().initializeCellEditorValue(cellEditor, 204 cell); 205 206 // Tricky flow of control here: 207 // activate() can trigger callback to cellEditorListener which 208 // will clear cellEditor 209 // so must get control first, but must still call activate() 210 // even if there is no control. 211 final Control control = cellEditor.getControl(); 212 cellEditor.activate(activationEvent); 213 if (control == null) { 214 return false; 215 } 216 setLayoutData(cellEditor.getLayoutData()); 217 setEditor(control, (Item) cell.getItem(), cell.getColumnIndex()); 218 cellEditor.setFocus(); 219 220 if (cellEditor.dependsOnExternalFocusListener()) { 221 if (focusListener == null) { 222 focusListener = new FocusAdapter() { 223 @Override 224 public void focusLost(FocusEvent e) { 225 applyEditorValue(); 226 } 227 }; 228 } 229 control.addFocusListener(focusListener); 230 } 231 232 mouseListener = new MouseAdapter() { 233 @Override 234 public void mouseDown(MouseEvent e) { 235 // time wrap? 236 // check for expiration of doubleClickTime 237 if (shouldFireDoubleClick(activationTime, e.time, activationEvent) && e.button == 1) { 238 control.removeMouseListener(mouseListener); 239 cancelEditing(); 240 handleDoubleClickEvent(); 241 } else if (mouseListener != null) { 242 control.removeMouseListener(mouseListener); 243 } 244 } 245 }; 246 247 if (activationTime != 0 248 && (feature & KEEP_EDITOR_ON_DOUBLE_CLICK) == 0) { 249 control.addMouseListener(mouseListener); 250 } 251 252 if (tabeditingListener == null) { 253 tabeditingListener = e -> { 254 if ((feature & DEFAULT) != DEFAULT) { 255 processTraverseEvent(cell.getColumnIndex(), 256 viewer.getViewerRowFromItem(cell 257 .getItem()), e); 258 } 259 }; 260 } 261 262 control.addTraverseListener(tabeditingListener); 263 264 if (editorActivationListener != null && !editorActivationListener.isEmpty()) { 265 for (ColumnViewerEditorActivationListener ls : editorActivationListener) { 266 ls.afterEditorActivated(activationEvent); 267 } 268 } 269 270 this.cell.getItem().addDisposeListener(disposeListener); 271 272 return true; 273 } 274 275 } 276 277 return false; 278 } 279 shouldFireDoubleClick(int activationTime, int mouseTime, ColumnViewerEditorActivationEvent activationEvent)280 private boolean shouldFireDoubleClick(int activationTime, int mouseTime, 281 ColumnViewerEditorActivationEvent activationEvent) { 282 return mouseTime <= activationTime 283 && activationEvent.eventType != ColumnViewerEditorActivationEvent.KEY_PRESSED 284 && activationEvent.eventType != ColumnViewerEditorActivationEvent.PROGRAMMATIC 285 && activationEvent.eventType != ColumnViewerEditorActivationEvent.TRAVERSAL; 286 } 287 288 /** 289 * Applies the current value and deactivates the currently active cell 290 * editor. 291 */ applyEditorValue()292 void applyEditorValue() { 293 // avoid re-entering 294 if (!inEditorDeactivation) { 295 try { 296 inEditorDeactivation = true; 297 CellEditor c = this.cellEditor; 298 if (c != null && this.cell != null) { 299 ColumnViewerEditorDeactivationEvent tmp = new ColumnViewerEditorDeactivationEvent( 300 cell); 301 tmp.eventType = ColumnViewerEditorDeactivationEvent.EDITOR_SAVED; 302 if (editorActivationListener != null && !editorActivationListener.isEmpty()) { 303 for (ColumnViewerEditorActivationListener ls : editorActivationListener) { 304 ls.beforeEditorDeactivated(tmp); 305 } 306 } 307 308 Item t = (Item) this.cell.getItem(); 309 310 // don't null out table item -- same item is still selected 311 if (t != null && !t.isDisposed() && c.isValueValid()) { 312 saveEditorValue(c); 313 } 314 if (!viewer.getControl().isDisposed()) { 315 setEditor(null, null, 0); 316 } 317 318 c.removeListener(cellEditorListener); 319 Control control = c.getControl(); 320 if (control != null && !control.isDisposed()) { 321 if (mouseListener != null) { 322 control.removeMouseListener(mouseListener); 323 // Clear the instance not needed any more 324 mouseListener = null; 325 } 326 if (focusListener != null) { 327 control.removeFocusListener(focusListener); 328 } 329 330 if (tabeditingListener != null) { 331 control.removeTraverseListener(tabeditingListener); 332 } 333 } 334 c.deactivate(tmp); 335 336 if (editorActivationListener != null && !editorActivationListener.isEmpty()) { 337 for (ColumnViewerEditorActivationListener ls : editorActivationListener) { 338 ls.afterEditorDeactivated(tmp); 339 } 340 } 341 342 if( ! this.cell.getItem().isDisposed() ) { 343 this.cell.getItem().removeDisposeListener(disposeListener); 344 } 345 } 346 347 this.cellEditor = null; 348 this.cell = null; 349 } finally { 350 inEditorDeactivation = false; 351 } 352 } 353 } 354 355 /** 356 * Cancel editing 357 */ cancelEditing()358 void cancelEditing() { 359 // avoid re-entering 360 if (!inEditorDeactivation) { 361 try { 362 inEditorDeactivation = true; 363 if (cellEditor != null) { 364 ColumnViewerEditorDeactivationEvent tmp = new ColumnViewerEditorDeactivationEvent( 365 cell); 366 tmp.eventType = ColumnViewerEditorDeactivationEvent.EDITOR_CANCELED; 367 if (editorActivationListener != null && !editorActivationListener.isEmpty()) { 368 for (ColumnViewerEditorActivationListener ls : editorActivationListener) { 369 ls.beforeEditorDeactivated(tmp); 370 } 371 } 372 373 if (!viewer.getControl().isDisposed()) { 374 setEditor(null, null, 0); 375 } 376 377 cellEditor.removeListener(cellEditorListener); 378 379 Control control = cellEditor.getControl(); 380 if (control != null && !viewer.getControl().isDisposed()) { 381 if (mouseListener != null) { 382 control.removeMouseListener(mouseListener); 383 // Clear the instance not needed any more 384 mouseListener = null; 385 } 386 if (focusListener != null) { 387 control.removeFocusListener(focusListener); 388 } 389 390 if (tabeditingListener != null) { 391 control.removeTraverseListener(tabeditingListener); 392 } 393 } 394 395 CellEditor oldEditor = cellEditor; 396 oldEditor.deactivate(tmp); 397 398 if (editorActivationListener != null && !editorActivationListener.isEmpty()) { 399 for (ColumnViewerEditorActivationListener ls : editorActivationListener) { 400 ls.afterEditorDeactivated(tmp); 401 } 402 } 403 404 if( ! this.cell.getItem().isDisposed() ) { 405 this.cell.getItem().addDisposeListener(disposeListener); 406 } 407 408 this.cellEditor = null; 409 this.cell = null; 410 411 } 412 } finally { 413 inEditorDeactivation = false; 414 } 415 } 416 } 417 418 /** 419 * Enable the editor by mouse down 420 * 421 * @param event 422 */ handleEditorActivationEvent(ColumnViewerEditorActivationEvent event)423 void handleEditorActivationEvent(ColumnViewerEditorActivationEvent event) { 424 425 // Only activate if the event isn't tagged as canceled 426 if (!event.cancel 427 && editorActivationStrategy.isEditorActivationEvent(event)) { 428 if (cellEditor != null) { 429 applyEditorValue(); 430 } 431 432 this.cell = (ViewerCell) event.getSource(); 433 434 // Only null if we are not in a deactivation process see bug 260892 435 if( ! activateCellEditor(event) && ! inEditorDeactivation ) { 436 this.cell = null; 437 this.cellEditor = null; 438 } 439 } 440 } 441 saveEditorValue(CellEditor cellEditor)442 private void saveEditorValue(CellEditor cellEditor) { 443 ViewerColumn part = viewer.getViewerColumn(cell.getColumnIndex()); 444 445 if (part != null && part.getEditingSupport() != null) { 446 part.getEditingSupport().saveCellEditorValue(cellEditor, cell); 447 } 448 } 449 450 /** 451 * Return whether there is an active cell editor. 452 * 453 * @return <code>true</code> if there is an active cell editor; otherwise 454 * <code>false</code> is returned. 455 */ isCellEditorActive()456 boolean isCellEditorActive() { 457 return cellEditor != null; 458 } 459 handleDoubleClickEvent()460 void handleDoubleClickEvent() { 461 viewer.fireDoubleClick(new DoubleClickEvent(viewer, viewer 462 .getSelection())); 463 viewer.fireOpen(new OpenEvent(viewer, viewer.getStructuredSelection())); 464 } 465 466 /** 467 * Adds the given listener, it is to be notified when the cell editor is 468 * activated or deactivated. 469 * 470 * @param listener 471 * the listener to add 472 */ addEditorActivationListener( ColumnViewerEditorActivationListener listener)473 public void addEditorActivationListener( 474 ColumnViewerEditorActivationListener listener) { 475 if (editorActivationListener == null) { 476 editorActivationListener = new ListenerList<>(); 477 } 478 editorActivationListener.add(listener); 479 } 480 481 /** 482 * Removes the given listener. 483 * 484 * @param listener 485 * the listener to remove 486 */ removeEditorActivationListener( ColumnViewerEditorActivationListener listener)487 public void removeEditorActivationListener( 488 ColumnViewerEditorActivationListener listener) { 489 if (editorActivationListener != null) { 490 editorActivationListener.remove(listener); 491 } 492 } 493 494 /** 495 * Process the traverse event and opens the next available editor depending 496 * of the implemented strategy. The default implementation uses the style 497 * constants 498 * <ul> 499 * <li>{@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}</li> 500 * <li>{@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}</li> 501 * <li>{@link ColumnViewerEditor#TABBING_VERTICAL}</li> 502 * <li>{@link ColumnViewerEditor#TABBING_HORIZONTAL}</li> 503 * </ul> 504 * 505 * <p> 506 * Subclasses may overwrite to implement their custom logic to edit the next 507 * cell 508 * </p> 509 * 510 * @param columnIndex 511 * the index of the current column 512 * @param row 513 * the current row - may only be used for the duration of this 514 * method call 515 * @param event 516 * the traverse event 517 */ processTraverseEvent(int columnIndex, ViewerRow row, TraverseEvent event)518 protected void processTraverseEvent(int columnIndex, ViewerRow row, 519 TraverseEvent event) { 520 521 ViewerCell cell2edit = null; 522 523 if (event.detail == SWT.TRAVERSE_TAB_PREVIOUS) { 524 event.doit = false; 525 526 if ((event.stateMask & SWT.CTRL) == SWT.CTRL 527 && (feature & TABBING_VERTICAL) == TABBING_VERTICAL) { 528 cell2edit = searchCellAboveBelow(row, viewer, columnIndex, true); 529 } else if ((feature & TABBING_HORIZONTAL) == TABBING_HORIZONTAL) { 530 cell2edit = searchPreviousCell(row, row.getCell(columnIndex), 531 row.getCell(columnIndex), viewer); 532 } 533 } else if (event.detail == SWT.TRAVERSE_TAB_NEXT) { 534 event.doit = false; 535 536 if ((event.stateMask & SWT.CTRL) == SWT.CTRL 537 && (feature & TABBING_VERTICAL) == TABBING_VERTICAL) { 538 cell2edit = searchCellAboveBelow(row, viewer, columnIndex, 539 false); 540 } else if ((feature & TABBING_HORIZONTAL) == TABBING_HORIZONTAL) { 541 cell2edit = searchNextCell(row, row.getCell(columnIndex), row 542 .getCell(columnIndex), viewer); 543 } 544 } 545 546 if (cell2edit != null) { 547 548 viewer.getControl().setRedraw(false); 549 ColumnViewerEditorActivationEvent acEvent = new ColumnViewerEditorActivationEvent( 550 cell2edit, event); 551 viewer.triggerEditorActivationEvent(acEvent); 552 viewer.getControl().setRedraw(true); 553 } 554 } 555 searchCellAboveBelow(ViewerRow row, ColumnViewer viewer, int columnIndex, boolean above)556 private ViewerCell searchCellAboveBelow(ViewerRow row, ColumnViewer viewer, 557 int columnIndex, boolean above) { 558 ViewerCell rv = null; 559 560 ViewerRow newRow = null; 561 562 if (above) { 563 newRow = row.getNeighbor(ViewerRow.ABOVE, false); 564 } else { 565 newRow = row.getNeighbor(ViewerRow.BELOW, false); 566 } 567 568 if (newRow != null) { 569 ViewerColumn column = viewer.getViewerColumn(columnIndex); 570 if (column != null 571 && column.getEditingSupport() != null 572 && column.getEditingSupport().canEdit( 573 newRow.getItem().getData())) { 574 rv = newRow.getCell(columnIndex); 575 } else { 576 rv = searchCellAboveBelow(newRow, viewer, columnIndex, above); 577 } 578 } 579 580 return rv; 581 } 582 isCellEditable(ColumnViewer viewer, ViewerCell cell)583 private boolean isCellEditable(ColumnViewer viewer, ViewerCell cell) { 584 ViewerColumn column = viewer.getViewerColumn(cell.getColumnIndex()); 585 return column != null && column.getEditingSupport() != null 586 && column.getEditingSupport().canEdit(cell.getElement()); 587 } 588 searchPreviousCell(ViewerRow row, ViewerCell currentCell, ViewerCell originalCell, ColumnViewer viewer)589 private ViewerCell searchPreviousCell(ViewerRow row, 590 ViewerCell currentCell, ViewerCell originalCell, ColumnViewer viewer) { 591 ViewerCell rv = null; 592 ViewerCell previousCell; 593 594 if (currentCell != null) { 595 previousCell = currentCell.getNeighbor(ViewerCell.LEFT, true); 596 } else { 597 if (row.getColumnCount() != 0) { 598 previousCell = row.getCell(row.getCreationIndex(row 599 .getColumnCount() - 1)); 600 } else { 601 previousCell = row.getCell(0); 602 } 603 604 } 605 606 // No endless loop 607 if (originalCell.equals(previousCell)) { 608 return null; 609 } 610 611 if (previousCell != null) { 612 if (isCellEditable(viewer, previousCell)) { 613 rv = previousCell; 614 } else { 615 rv = searchPreviousCell(row, previousCell, originalCell, viewer); 616 } 617 } else { 618 if ((feature & TABBING_CYCLE_IN_ROW) == TABBING_CYCLE_IN_ROW) { 619 rv = searchPreviousCell(row, null, originalCell, viewer); 620 } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) == TABBING_MOVE_TO_ROW_NEIGHBOR) { 621 ViewerRow rowAbove = row.getNeighbor(ViewerRow.ABOVE, false); 622 if (rowAbove != null) { 623 rv = searchPreviousCell(rowAbove, null, originalCell, 624 viewer); 625 } 626 } 627 if ((feature & TABBING_CYCLE_IN_VIEWER) == TABBING_CYCLE_IN_VIEWER) { 628 ViewerRow rowAbove = row.getNeighbor(ViewerRow.ABOVE, false); 629 if (rowAbove == null) { 630 return getLastCell(row); 631 } 632 633 } 634 } 635 636 return rv; 637 } 638 639 searchNextCell(ViewerRow row, ViewerCell currentCell, ViewerCell originalCell, ColumnViewer viewer)640 private ViewerCell searchNextCell(ViewerRow row, ViewerCell currentCell, 641 ViewerCell originalCell, ColumnViewer viewer) { 642 ViewerCell rv = null; 643 644 ViewerCell nextCell; 645 646 if (currentCell != null) { 647 nextCell = currentCell.getNeighbor(ViewerCell.RIGHT, true); 648 } else { 649 nextCell = row.getCell(row.getCreationIndex(0)); 650 } 651 652 // No endless loop 653 if (originalCell.equals(nextCell)) { 654 return null; 655 } 656 657 if (nextCell != null) { 658 if (isCellEditable(viewer, nextCell)) { 659 rv = nextCell; 660 } else { 661 rv = searchNextCell(row, nextCell, originalCell, viewer); 662 } 663 } else { 664 if ((feature & TABBING_CYCLE_IN_ROW) == TABBING_CYCLE_IN_ROW) { 665 rv = searchNextCell(row, null, originalCell, viewer); 666 } else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) == TABBING_MOVE_TO_ROW_NEIGHBOR) { 667 ViewerRow rowBelow = row.getNeighbor(ViewerRow.BELOW, false); 668 if (rowBelow != null) { 669 rv = searchNextCell(rowBelow, null, originalCell, viewer); 670 } 671 } 672 if ((feature & TABBING_CYCLE_IN_VIEWER) == TABBING_CYCLE_IN_VIEWER) { 673 ViewerRow rowBelow = row.getNeighbor(ViewerRow.BELOW, false); 674 if (rowBelow == null) { 675 return getFirstCell(row); 676 } 677 678 679 } 680 } 681 682 return rv; 683 } 684 685 /** 686 * Searches for the top-left-most cell and returns it 687 * 688 * @param row Row to search from 689 * @return Top-left-most cell in the viewer 690 * 691 * 692 */ getFirstCell(ViewerRow row)693 private ViewerCell getFirstCell(ViewerRow row) { 694 ViewerRow rowAbove = row.getNeighbor(ViewerRow.ABOVE, false); 695 ViewerRow topMostRow = rowAbove; 696 if (rowAbove == null) { 697 // Already on the top most-row, return left-most cell 698 return row.getCell(row.getCreationIndex(0)); 699 } 700 while (rowAbove != null) { 701 topMostRow = rowAbove; 702 rowAbove = rowAbove.getNeighbor(ViewerRow.ABOVE, false); 703 } 704 705 return topMostRow.getCell(row.getCreationIndex(0)); 706 } 707 708 /** 709 * Searches for the bottom-right-most cell and returns it 710 * 711 * @param row Row to search from 712 * @return bottom-right-most cell in the viewer 713 * 714 * 715 */ getLastCell(ViewerRow row)716 private ViewerCell getLastCell(ViewerRow row) { 717 ViewerRow rowBelow = row.getNeighbor(ViewerRow.BELOW, false); 718 ViewerRow bottomMostRow = rowBelow; 719 if (rowBelow == null) { 720 // Already on the bottom most-row, return right-most cell 721 return row.getCell(row.getCreationIndex(row.getColumnCount() - 1)); 722 } 723 while (rowBelow != null) { 724 bottomMostRow = rowBelow; 725 rowBelow = rowBelow.getNeighbor(ViewerRow.BELOW, false); 726 } 727 728 return bottomMostRow.getCell(row.getCreationIndex(bottomMostRow.getColumnCount() - 1)); 729 } 730 731 732 /** 733 * Position the editor inside the control 734 * 735 * @param w the editor control 736 * @param item the item (row) in which the editor is drawn in 737 * @param fColumnNumber the column number in which the editor is shown 738 */ setEditor(Control w, Item item, int fColumnNumber)739 protected abstract void setEditor(Control w, Item item, int fColumnNumber); 740 741 /** 742 * set the layout data for the editor 743 * 744 * @param layoutData 745 * the layout data used when editor is displayed 746 */ setLayoutData(CellEditor.LayoutData layoutData)747 protected abstract void setLayoutData(CellEditor.LayoutData layoutData); 748 749 /** 750 * @param focusCell 751 * updates the cell with the current input focus 752 * @param event 753 * the event requesting to update the focusCell 754 */ updateFocusCell(ViewerCell focusCell, ColumnViewerEditorActivationEvent event)755 protected abstract void updateFocusCell(ViewerCell focusCell, 756 ColumnViewerEditorActivationEvent event); 757 758 /** 759 * @return the cell currently holding the focus if no cell has the focus or 760 * the viewer implementation doesn't support <code>null</code> is 761 * returned 762 * 763 */ getFocusCell()764 public ViewerCell getFocusCell() { 765 return null; 766 } 767 768 /** 769 * @return the viewer working for 770 */ getViewer()771 protected ColumnViewer getViewer() { 772 return viewer; 773 } 774 }