All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jface.viewers.ColumnViewerEditor Maven / Gradle / Ivy

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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy