org.eclipse.jface.viewers.ColumnViewerEditor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.eclipse.jface Show documentation
Show all versions of org.eclipse.jface Show documentation
This is org.eclipse.jface jar used by Scout SDK
The newest version!
/*******************************************************************************
* Copyright (c) 2006, 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Tom Schindl - refactoring (bug 153993)
* fix in bug: 151295,178946,166500,195908,201906,207676,180504,216706,218336
* Tony Juckel - Bug 130854 - JFace TableViewer ignoring ICellEditor validator state
*******************************************************************************/
package org.eclipse.jface.viewers;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Item;
/**
* This is the base for all editor implementations of Viewers. ColumnViewer
* implementors have to subclass this class and implement the missing methods
*
* @since 3.3
* @see TableViewerEditor
* @see TreeViewerEditor
*/
public abstract class ColumnViewerEditor {
private CellEditor cellEditor;
private ICellEditorListener cellEditorListener;
private FocusListener focusListener;
private MouseListener mouseListener;
private ColumnViewer viewer;
private TraverseListener tabeditingListener;
private ViewerCell cell;
private ListenerList editorActivationListener;
private ColumnViewerEditorActivationStrategy editorActivationStrategy;
private boolean inEditorDeactivation;
private DisposeListener disposeListener;
/**
* Tabbing from cell to cell is turned off
*/
public static final int DEFAULT = 1;
/**
* Should if the end of the row is reach started from the start/end of the
* row below/above
*/
public static final int TABBING_MOVE_TO_ROW_NEIGHBOR = 1 << 1;
/**
* Should if the end of the row is reach started from the beginning in the
* same row
*/
public static final int TABBING_CYCLE_IN_ROW = 1 << 2;
/**
* Support tabbing to Cell above/below the current cell
*/
public static final int TABBING_VERTICAL = 1 << 3;
/**
* Should tabbing from column to column with in one row be supported
*/
public static final int TABBING_HORIZONTAL = 1 << 4;
/**
* Style mask used to enable keyboard activation
*/
public static final int KEYBOARD_ACTIVATION = 1 << 5;
/**
* Style mask used to turn off the feature that an editor activation
* is canceled on double click. It is also possible to turn off this feature
* per cell-editor using {@link CellEditor#getDoubleClickTimeout()}
* @since 3.4
*/
public static final int KEEP_EDITOR_ON_DOUBLE_CLICK = 1 << 6;
private int feature;
/**
* @param viewer
* the viewer this editor is attached to
* @param editorActivationStrategy
* the strategy used to decide about editor activation
* @param feature
* bit mask controlling the editor
*
* - {@link ColumnViewerEditor#DEFAULT}
* - {@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}
* - {@link ColumnViewerEditor#TABBING_HORIZONTAL}
* - {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}
* - {@link ColumnViewerEditor#TABBING_VERTICAL}
*
*/
protected ColumnViewerEditor(final ColumnViewer viewer,
ColumnViewerEditorActivationStrategy editorActivationStrategy,
int feature) {
this.viewer = viewer;
this.editorActivationStrategy = editorActivationStrategy;
if ((feature & KEYBOARD_ACTIVATION) == KEYBOARD_ACTIVATION) {
this.editorActivationStrategy
.setEnableEditorActivationWithKeyboard(true);
}
this.feature = feature;
this.disposeListener = e -> {
if( viewer.isCellEditorActive() ) {
cancelEditing();
}
};
initCellEditorListener();
}
private void initCellEditorListener() {
cellEditorListener = new ICellEditorListener() {
@Override
public void editorValueChanged(boolean oldValidState,
boolean newValidState) {
// Ignore.
}
@Override
public void cancelEditor() {
ColumnViewerEditor.this.cancelEditing();
}
@Override
public void applyEditorValue() {
ColumnViewerEditor.this.applyEditorValue();
}
};
}
private boolean activateCellEditor(final ColumnViewerEditorActivationEvent activationEvent) {
ViewerColumn part = viewer.getViewerColumn(cell.getColumnIndex());
Object element = cell.getElement();
if (part != null && part.getEditingSupport() != null
&& part.getEditingSupport().canEdit(element)) {
cellEditor = part.getEditingSupport().getCellEditor(element);
if (cellEditor != null) {
int timeout = cellEditor.getDoubleClickTimeout();
final int activationTime;
if (timeout != 0) {
activationTime = activationEvent.time + timeout;
} else {
activationTime = 0;
}
if (editorActivationListener != null
&& !editorActivationListener.isEmpty()) {
Object[] ls = editorActivationListener.getListeners();
for (int i = 0; i < ls.length; i++) {
((ColumnViewerEditorActivationListener) ls[i])
.beforeEditorActivated(activationEvent);
// Was the activation canceled ?
if (activationEvent.cancel) {
return false;
}
}
}
updateFocusCell(cell, activationEvent);
cellEditor.addListener(cellEditorListener);
part.getEditingSupport().initializeCellEditorValue(cellEditor,
cell);
// Tricky flow of control here:
// activate() can trigger callback to cellEditorListener which
// will clear cellEditor
// so must get control first, but must still call activate()
// even if there is no control.
final Control control = cellEditor.getControl();
cellEditor.activate(activationEvent);
if (control == null) {
return false;
}
setLayoutData(cellEditor.getLayoutData());
setEditor(control, (Item) cell.getItem(), cell.getColumnIndex());
cellEditor.setFocus();
if (cellEditor.dependsOnExternalFocusListener()) {
if (focusListener == null) {
focusListener = new FocusAdapter() {
@Override
public void focusLost(FocusEvent e) {
applyEditorValue();
}
};
}
control.addFocusListener(focusListener);
}
mouseListener = new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
// time wrap?
// check for expiration of doubleClickTime
if (shouldFireDoubleClick(activationTime, e.time, activationEvent) && e.button == 1) {
control.removeMouseListener(mouseListener);
cancelEditing();
handleDoubleClickEvent();
} else if (mouseListener != null) {
control.removeMouseListener(mouseListener);
}
}
};
if (activationTime != 0
&& (feature & KEEP_EDITOR_ON_DOUBLE_CLICK) == 0) {
control.addMouseListener(mouseListener);
}
if (tabeditingListener == null) {
tabeditingListener = e -> {
if ((feature & DEFAULT) != DEFAULT) {
processTraverseEvent(cell.getColumnIndex(),
viewer.getViewerRowFromItem(cell
.getItem()), e);
}
};
}
control.addTraverseListener(tabeditingListener);
if (editorActivationListener != null
&& !editorActivationListener.isEmpty()) {
Object[] ls = editorActivationListener.getListeners();
for (int i = 0; i < ls.length; i++) {
((ColumnViewerEditorActivationListener) ls[i])
.afterEditorActivated(activationEvent);
}
}
this.cell.getItem().addDisposeListener(disposeListener);
return true;
}
}
return false;
}
private boolean shouldFireDoubleClick(int activationTime, int mouseTime,
ColumnViewerEditorActivationEvent activationEvent) {
return mouseTime <= activationTime
&& activationEvent.eventType != ColumnViewerEditorActivationEvent.KEY_PRESSED
&& activationEvent.eventType != ColumnViewerEditorActivationEvent.PROGRAMMATIC
&& activationEvent.eventType != ColumnViewerEditorActivationEvent.TRAVERSAL;
}
/**
* Applies the current value and deactivates the currently active cell
* editor.
*/
void applyEditorValue() {
// avoid re-entering
if (!inEditorDeactivation) {
try {
inEditorDeactivation = true;
CellEditor c = this.cellEditor;
if (c != null && this.cell != null) {
ColumnViewerEditorDeactivationEvent tmp = new ColumnViewerEditorDeactivationEvent(
cell);
tmp.eventType = ColumnViewerEditorDeactivationEvent.EDITOR_SAVED;
if (editorActivationListener != null
&& !editorActivationListener.isEmpty()) {
Object[] ls = editorActivationListener.getListeners();
for (int i = 0; i < ls.length; i++) {
((ColumnViewerEditorActivationListener) ls[i])
.beforeEditorDeactivated(tmp);
}
}
Item t = (Item) this.cell.getItem();
// don't null out table item -- same item is still selected
if (t != null && !t.isDisposed() && c.isValueValid()) {
saveEditorValue(c);
}
if (!viewer.getControl().isDisposed()) {
setEditor(null, null, 0);
}
c.removeListener(cellEditorListener);
Control control = c.getControl();
if (control != null && !control.isDisposed()) {
if (mouseListener != null) {
control.removeMouseListener(mouseListener);
// Clear the instance not needed any more
mouseListener = null;
}
if (focusListener != null) {
control.removeFocusListener(focusListener);
}
if (tabeditingListener != null) {
control.removeTraverseListener(tabeditingListener);
}
}
c.deactivate(tmp);
if (editorActivationListener != null
&& !editorActivationListener.isEmpty()) {
Object[] ls = editorActivationListener.getListeners();
for (int i = 0; i < ls.length; i++) {
((ColumnViewerEditorActivationListener) ls[i])
.afterEditorDeactivated(tmp);
}
}
if( ! this.cell.getItem().isDisposed() ) {
this.cell.getItem().removeDisposeListener(disposeListener);
}
}
this.cellEditor = null;
this.cell = null;
} finally {
inEditorDeactivation = false;
}
}
}
/**
* Cancel editing
*/
void cancelEditing() {
// avoid re-entering
if (!inEditorDeactivation) {
try {
inEditorDeactivation = true;
if (cellEditor != null) {
ColumnViewerEditorDeactivationEvent tmp = new ColumnViewerEditorDeactivationEvent(
cell);
tmp.eventType = ColumnViewerEditorDeactivationEvent.EDITOR_CANCELED;
if (editorActivationListener != null
&& !editorActivationListener.isEmpty()) {
Object[] ls = editorActivationListener.getListeners();
for (int i = 0; i < ls.length; i++) {
((ColumnViewerEditorActivationListener) ls[i])
.beforeEditorDeactivated(tmp);
}
}
if (!viewer.getControl().isDisposed()) {
setEditor(null, null, 0);
}
cellEditor.removeListener(cellEditorListener);
Control control = cellEditor.getControl();
if (control != null && !viewer.getControl().isDisposed()) {
if (mouseListener != null) {
control.removeMouseListener(mouseListener);
// Clear the instance not needed any more
mouseListener = null;
}
if (focusListener != null) {
control.removeFocusListener(focusListener);
}
if (tabeditingListener != null) {
control.removeTraverseListener(tabeditingListener);
}
}
CellEditor oldEditor = cellEditor;
oldEditor.deactivate(tmp);
if (editorActivationListener != null
&& !editorActivationListener.isEmpty()) {
Object[] ls = editorActivationListener.getListeners();
for (int i = 0; i < ls.length; i++) {
((ColumnViewerEditorActivationListener) ls[i])
.afterEditorDeactivated(tmp);
}
}
if( ! this.cell.getItem().isDisposed() ) {
this.cell.getItem().addDisposeListener(disposeListener);
}
this.cellEditor = null;
this.cell = null;
}
} finally {
inEditorDeactivation = false;
}
}
}
/**
* Enable the editor by mouse down
*
* @param event
*/
void handleEditorActivationEvent(ColumnViewerEditorActivationEvent event) {
// Only activate if the event isn't tagged as canceled
if (!event.cancel
&& editorActivationStrategy.isEditorActivationEvent(event)) {
if (cellEditor != null) {
applyEditorValue();
}
this.cell = (ViewerCell) event.getSource();
// Only null if we are not in a deactivation process see bug 260892
if( ! activateCellEditor(event) && ! inEditorDeactivation ) {
this.cell = null;
this.cellEditor = null;
}
}
}
private void saveEditorValue(CellEditor cellEditor) {
ViewerColumn part = viewer.getViewerColumn(cell.getColumnIndex());
if (part != null && part.getEditingSupport() != null) {
part.getEditingSupport().saveCellEditorValue(cellEditor, cell);
}
}
/**
* Return whether there is an active cell editor.
*
* @return true
if there is an active cell editor; otherwise
* false
is returned.
*/
boolean isCellEditorActive() {
return cellEditor != null;
}
void handleDoubleClickEvent() {
viewer.fireDoubleClick(new DoubleClickEvent(viewer, viewer
.getSelection()));
viewer.fireOpen(new OpenEvent(viewer, viewer.getSelection()));
}
/**
* Adds the given listener, it is to be notified when the cell editor is
* activated or deactivated.
*
* @param listener
* the listener to add
*/
public void addEditorActivationListener(
ColumnViewerEditorActivationListener listener) {
if (editorActivationListener == null) {
editorActivationListener = new ListenerList();
}
editorActivationListener.add(listener);
}
/**
* Removes the given listener.
*
* @param listener
* the listener to remove
*/
public void removeEditorActivationListener(
ColumnViewerEditorActivationListener listener) {
if (editorActivationListener != null) {
editorActivationListener.remove(listener);
}
}
/**
* Process the traverse event and opens the next available editor depending
* of the implemented strategy. The default implementation uses the style
* constants
*
* - {@link ColumnViewerEditor#TABBING_MOVE_TO_ROW_NEIGHBOR}
* - {@link ColumnViewerEditor#TABBING_CYCLE_IN_ROW}
* - {@link ColumnViewerEditor#TABBING_VERTICAL}
* - {@link ColumnViewerEditor#TABBING_HORIZONTAL}
*
*
*
* Subclasses may overwrite to implement their custom logic to edit the next
* cell
*
*
* @param columnIndex
* the index of the current column
* @param row
* the current row - may only be used for the duration of this
* method call
* @param event
* the traverse event
*/
protected void processTraverseEvent(int columnIndex, ViewerRow row,
TraverseEvent event) {
ViewerCell cell2edit = null;
if (event.detail == SWT.TRAVERSE_TAB_PREVIOUS) {
event.doit = false;
if ((event.stateMask & SWT.CTRL) == SWT.CTRL
&& (feature & TABBING_VERTICAL) == TABBING_VERTICAL) {
cell2edit = searchCellAboveBelow(row, viewer, columnIndex, true);
} else if ((feature & TABBING_HORIZONTAL) == TABBING_HORIZONTAL) {
cell2edit = searchPreviousCell(row, row.getCell(columnIndex),
row.getCell(columnIndex), viewer);
}
} else if (event.detail == SWT.TRAVERSE_TAB_NEXT) {
event.doit = false;
if ((event.stateMask & SWT.CTRL) == SWT.CTRL
&& (feature & TABBING_VERTICAL) == TABBING_VERTICAL) {
cell2edit = searchCellAboveBelow(row, viewer, columnIndex,
false);
} else if ((feature & TABBING_HORIZONTAL) == TABBING_HORIZONTAL) {
cell2edit = searchNextCell(row, row.getCell(columnIndex), row
.getCell(columnIndex), viewer);
}
}
if (cell2edit != null) {
viewer.getControl().setRedraw(false);
ColumnViewerEditorActivationEvent acEvent = new ColumnViewerEditorActivationEvent(
cell2edit, event);
viewer.triggerEditorActivationEvent(acEvent);
viewer.getControl().setRedraw(true);
}
}
private ViewerCell searchCellAboveBelow(ViewerRow row, ColumnViewer viewer,
int columnIndex, boolean above) {
ViewerCell rv = null;
ViewerRow newRow = null;
if (above) {
newRow = row.getNeighbor(ViewerRow.ABOVE, false);
} else {
newRow = row.getNeighbor(ViewerRow.BELOW, false);
}
if (newRow != null) {
ViewerColumn column = viewer.getViewerColumn(columnIndex);
if (column != null
&& column.getEditingSupport() != null
&& column.getEditingSupport().canEdit(
newRow.getItem().getData())) {
rv = newRow.getCell(columnIndex);
} else {
rv = searchCellAboveBelow(newRow, viewer, columnIndex, above);
}
}
return rv;
}
private boolean isCellEditable(ColumnViewer viewer, ViewerCell cell) {
ViewerColumn column = viewer.getViewerColumn(cell.getColumnIndex());
return column != null && column.getEditingSupport() != null
&& column.getEditingSupport().canEdit(cell.getElement());
}
private ViewerCell searchPreviousCell(ViewerRow row,
ViewerCell currentCell, ViewerCell originalCell, ColumnViewer viewer) {
ViewerCell rv = null;
ViewerCell previousCell;
if (currentCell != null) {
previousCell = currentCell.getNeighbor(ViewerCell.LEFT, true);
} else {
if (row.getColumnCount() != 0) {
previousCell = row.getCell(row.getCreationIndex(row
.getColumnCount() - 1));
} else {
previousCell = row.getCell(0);
}
}
// No endless loop
if (originalCell.equals(previousCell)) {
return null;
}
if (previousCell != null) {
if (isCellEditable(viewer, previousCell)) {
rv = previousCell;
} else {
rv = searchPreviousCell(row, previousCell, originalCell, viewer);
}
} else {
if ((feature & TABBING_CYCLE_IN_ROW) == TABBING_CYCLE_IN_ROW) {
rv = searchPreviousCell(row, null, originalCell, viewer);
} else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) == TABBING_MOVE_TO_ROW_NEIGHBOR) {
ViewerRow rowAbove = row.getNeighbor(ViewerRow.ABOVE, false);
if (rowAbove != null) {
rv = searchPreviousCell(rowAbove, null, originalCell,
viewer);
}
}
}
return rv;
}
private ViewerCell searchNextCell(ViewerRow row, ViewerCell currentCell,
ViewerCell originalCell, ColumnViewer viewer) {
ViewerCell rv = null;
ViewerCell nextCell;
if (currentCell != null) {
nextCell = currentCell.getNeighbor(ViewerCell.RIGHT, true);
} else {
nextCell = row.getCell(row.getCreationIndex(0));
}
// No endless loop
if (originalCell.equals(nextCell)) {
return null;
}
if (nextCell != null) {
if (isCellEditable(viewer, nextCell)) {
rv = nextCell;
} else {
rv = searchNextCell(row, nextCell, originalCell, viewer);
}
} else {
if ((feature & TABBING_CYCLE_IN_ROW) == TABBING_CYCLE_IN_ROW) {
rv = searchNextCell(row, null, originalCell, viewer);
} else if ((feature & TABBING_MOVE_TO_ROW_NEIGHBOR) == TABBING_MOVE_TO_ROW_NEIGHBOR) {
ViewerRow rowBelow = row.getNeighbor(ViewerRow.BELOW, false);
if (rowBelow != null) {
rv = searchNextCell(rowBelow, null, originalCell, viewer);
}
}
}
return rv;
}
/**
* Position the editor inside the control
*
* @param w
* the editor control
* @param item
* the item (row) in which the editor is drawn in
* @param fColumnNumber
* the column number in which the editor is shown
*/
protected abstract void setEditor(Control w, Item item, int fColumnNumber);
/**
* set the layout data for the editor
*
* @param layoutData
* the layout data used when editor is displayed
*/
protected abstract void setLayoutData(CellEditor.LayoutData layoutData);
/**
* @param focusCell
* updates the cell with the current input focus
* @param event
* the event requesting to update the focusCell
*/
protected abstract void updateFocusCell(ViewerCell focusCell,
ColumnViewerEditorActivationEvent event);
/**
* @return the cell currently holding the focus if no cell has the focus or
* the viewer implementation doesn't support null
is
* returned
*
*/
public ViewerCell getFocusCell() {
return null;
}
/**
* @return the viewer working for
*/
protected ColumnViewer getViewer() {
return viewer;
}
}