blob: 889faa02a0b8a13a765f7d268038e91926b64f91 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2008 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 <tom.schindl@bestsolution.at> - bugfix in: 187963, 218336
*******************************************************************************/
package org.eclipse.jface.viewers;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
/**
* Abstract base class for cell editors. Implements property change listener
* handling, and SWT window management.
* <p>
* Subclasses implement particular kinds of cell editors. This package contains
* various specialized cell editors:
* <ul>
* <li><code>TextCellEditor</code> - for simple text strings</li>
* <li><code>ColorCellEditor</code> - for colors</li>
* <li><code>ComboBoxCellEditor</code> - value selected from drop-down combo
* box</li>
* <li><code>CheckboxCellEditor</code> - boolean valued checkbox</li>
* <li><code>DialogCellEditor</code> - value from arbitrary dialog</li>
* </ul>
* </p>
*/
public abstract class CellEditor {
/**
* List of cell editor listeners (element type:
* <code>ICellEditorListener</code>).
*/
private ListenerList listeners = new ListenerList();
/**
* List of cell editor property change listeners (element type:
* <code>IPropertyChangeListener</code>).
*/
private ListenerList propertyChangeListeners = new ListenerList();
/**
* Indicates whether this cell editor's current value is valid.
*/
private boolean valid = false;
/**
* Optional cell editor validator; <code>null</code> if none.
*/
private ICellEditorValidator validator = null;
/**
* The error message string to display for invalid values; <code>null</code>
* if none (that is, the value is valid).
*/
private String errorMessage = null;
/**
* Indicates whether this cell editor has been changed recently.
*/
private boolean dirty = false;
/**
* This cell editor's control, or <code>null</code> if not created yet.
*/
private Control control = null;
/**
* Default cell editor style
*/
private static final int defaultStyle = SWT.NONE;
/**
* This cell editor's style
*/
private int style = defaultStyle;
/**
* Struct-like layout data for cell editors, with reasonable defaults for
* all fields.
*/
public static class LayoutData {
/**
* Horizontal alignment; <code>SWT.LEFT</code> by default.
*/
public int horizontalAlignment = SWT.LEFT;
/**
* Indicates control grabs additional space; <code>true</code> by
* default.
*/
public boolean grabHorizontal = true;
/**
* Minimum width in pixels; <code>50</code> pixels by default.
*/
public int minimumWidth = 50;
/**
* Minimum height in pixels; by default the height is aligned to the
* row-height
*/
public int minimumHeight = SWT.DEFAULT;
/**
* The vertical alignment; <code>SWT.CENTER</code> by default.
*/
public int verticalAlignment = SWT.CENTER;
}
/**
* Property name for the copy action
*/
public static final String COPY = "copy"; //$NON-NLS-1$
/**
* Property name for the cut action
*/
public static final String CUT = "cut"; //$NON-NLS-1$
/**
* Property name for the delete action
*/
public static final String DELETE = "delete"; //$NON-NLS-1$
/**
* Property name for the find action
*/
public static final String FIND = "find"; //$NON-NLS-1$
/**
* Property name for the paste action
*/
public static final String PASTE = "paste"; //$NON-NLS-1$
/**
* Property name for the redo action
*/
public static final String REDO = "redo"; //$NON-NLS-1$
/**
* Property name for the select all action
*/
public static final String SELECT_ALL = "selectall"; //$NON-NLS-1$
/**
* Property name for the undo action
*/
public static final String UNDO = "undo"; //$NON-NLS-1$
/**
* Creates a new cell editor with no control The cell editor has no cell
* validator.
*
* @since 2.1
*/
protected CellEditor() {
}
/**
* Creates a new cell editor under the given parent control. The cell editor
* has no cell validator.
*
* @param parent
* the parent control
*/
protected CellEditor(Composite parent) {
this(parent, defaultStyle);
}
/**
* Creates a new cell editor under the given parent control. The cell editor
* has no cell validator.
*
* @param parent
* the parent control
* @param style
* the style bits
* @since 2.1
*/
protected CellEditor(Composite parent, int style) {
this.style = style;
create(parent);
}
/**
* Activates this cell editor.
* <p>
* The default implementation of this framework method does nothing.
* Subclasses may reimplement.
* </p>
*/
public void activate() {
}
/**
* Adds a listener to this cell editor. Has no effect if an identical
* listener is already registered.
*
* @param listener
* a cell editor listener
*/
public void addListener(ICellEditorListener listener) {
listeners.add(listener);
}
/**
* Adds a property change listener to this cell editor. Has no effect if an
* identical property change listener is already registered.
*
* @param listener
* a property change listener
*/
public void addPropertyChangeListener(IPropertyChangeListener listener) {
propertyChangeListeners.add(listener);
}
/**
* Creates the control for this cell editor under the given parent control.
* <p>
* This framework method must be implemented by concrete subclasses.
* </p>
*
* @param parent
* the parent control
* @return the new control, or <code>null</code> if this cell editor has
* no control
*/
protected abstract Control createControl(Composite parent);
/**
* Creates the control for this cell editor under the given parent control.
*
* @param parent
* the parent control
* @since 2.1
*/
public void create(Composite parent) {
Assert.isTrue(control == null);
control = createControl(parent);
// See 1GD5CA6: ITPUI:ALL - TaskView.setSelection does not work
// Control is created with getVisible()==true by default.
// This causes composite.setFocus() to work incorrectly.
// The cell editor's control grabs focus instead, even if it is not
// active.
// Make the control invisible here by default.
deactivate();
}
/**
* Hides this cell editor's control. Does nothing if this cell editor is not
* visible.
*/
public void deactivate() {
if (control != null && !control.isDisposed()) {
control.setVisible(false);
}
}
/**
* Disposes of this cell editor and frees any associated SWT resources.
*/
public void dispose() {
if (control != null && !control.isDisposed()) {
control.dispose();
}
control = null;
}
/**
* Returns this cell editor's value.
* <p>
* This framework method must be implemented by concrete subclasses.
* </p>
*
* @return the value of this cell editor
* @see #getValue
*/
protected abstract Object doGetValue();
/**
* Sets the focus to the cell editor's control.
* <p>
* This framework method must be implemented by concrete subclasses.
* </p>
*
* @see #setFocus
*/
protected abstract void doSetFocus();
/**
* Sets this cell editor's value.
* <p>
* This framework method must be implemented by concrete subclasses.
* </p>
*
* @param value
* the value of this cell editor
* @see #setValue
*/
protected abstract void doSetValue(Object value);
/**
* Notifies all registered cell editor listeners of an apply event. Only
* listeners registered at the time this method is called are notified.
*
* @see ICellEditorListener#applyEditorValue
*/
protected void fireApplyEditorValue() {
Object[] array = listeners.getListeners();
for (int i = 0; i < array.length; i++) {
final ICellEditorListener l = (ICellEditorListener) array[i];
SafeRunnable.run(new SafeRunnable() {
public void run() {
l.applyEditorValue();
}
});
}
}
/**
* Notifies all registered cell editor listeners that editing has been
* canceled.
*
* @see ICellEditorListener#cancelEditor
*/
protected void fireCancelEditor() {
Object[] array = listeners.getListeners();
for (int i = 0; i < array.length; i++) {
final ICellEditorListener l = (ICellEditorListener) array[i];
SafeRunnable.run(new SafeRunnable() {
public void run() {
l.cancelEditor();
}
});
}
}
/**
* Notifies all registered cell editor listeners of a value change.
*
* @param oldValidState
* the valid state before the end user changed the value
* @param newValidState
* the current valid state
* @see ICellEditorListener#editorValueChanged
*/
protected void fireEditorValueChanged(final boolean oldValidState,
final boolean newValidState) {
Object[] array = listeners.getListeners();
for (int i = 0; i < array.length; i++) {
final ICellEditorListener l = (ICellEditorListener) array[i];
SafeRunnable.run(new SafeRunnable() {
public void run() {
l.editorValueChanged(oldValidState, newValidState);
}
});
}
}
/**
* Notifies all registered property listeners of an enablement change.
*
* @param actionId
* the id indicating what action's enablement has changed.
*/
protected void fireEnablementChanged(final String actionId) {
Object[] array = propertyChangeListeners.getListeners();
for (int i = 0; i < array.length; i++) {
final IPropertyChangeListener l = (IPropertyChangeListener) array[i];
SafeRunnable.run(new SafeRunnable() {
public void run() {
l.propertyChange(new PropertyChangeEvent(this, actionId,
null, null));
}
});
}
}
/**
* Sets the style bits for this cell editor.
*
* @param style
* the SWT style bits for this cell editor
* @since 2.1
*/
public void setStyle(int style) {
this.style = style;
}
/**
* Returns the style bits for this cell editor.
*
* @return the style for this cell editor
* @since 2.1
*/
public int getStyle() {
return style;
}
/**
* Returns the control used to implement this cell editor.
*
* @return the control, or <code>null</code> if this cell editor has no
* control
*/
public Control getControl() {
return control;
}
/**
* Returns the current error message for this cell editor.
*
* @return the error message if the cell editor is in an invalid state, and
* <code>null</code> if the cell editor is valid
*/
public String getErrorMessage() {
return errorMessage;
}
/**
* Returns a layout data object for this cell editor. This is called each
* time the cell editor is activated and controls the layout of the SWT
* table editor.
* <p>
* The default implementation of this method sets the minimum width to the
* control's preferred width. Subclasses may extend or reimplement.
* </p>
*
* @return the layout data object
*/
public LayoutData getLayoutData() {
LayoutData result = new LayoutData();
Control control = getControl();
if (control != null) {
result.minimumWidth = control.computeSize(SWT.DEFAULT, SWT.DEFAULT,
true).x;
}
return result;
}
/**
* Returns the input validator for this cell editor.
*
* @return the input validator, or <code>null</code> if none
*/
public ICellEditorValidator getValidator() {
return validator;
}
/**
* Returns this cell editor's value provided that it has a valid one.
*
* @return the value of this cell editor, or <code>null</code> if the cell
* editor does not contain a valid value
*/
public final Object getValue() {
if (!valid) {
return null;
}
return doGetValue();
}
/**
* Returns whether this cell editor is activated.
*
* @return <code>true</code> if this cell editor's control is currently
* activated, and <code>false</code> if not activated
*/
public boolean isActivated() {
// Use the state of the visible style bit (getVisible()) rather than the
// window's actual visibility (isVisible()) to get correct handling when
// an ancestor control goes invisible, see bug 85331.
return control != null && control.getVisible();
}
/**
* Returns <code>true</code> if this cell editor is able to perform the
* copy action.
* <p>
* This default implementation always returns <code>false</code>.
* </p>
* <p>
* Subclasses may override
* </p>
*
* @return <code>true</code> if copy is possible, <code>false</code>
* otherwise
*/
public boolean isCopyEnabled() {
return false;
}
/**
* Returns whether the given value is valid for this cell editor. This cell
* editor's validator (if any) makes the actual determination.
*
* @param value
* the value to check for
*
* @return <code>true</code> if the value is valid, and <code>false</code>
* if invalid
*/
protected boolean isCorrect(Object value) {
errorMessage = null;
if (validator == null) {
return true;
}
errorMessage = validator.isValid(value);
return (errorMessage == null || errorMessage.equals(""));//$NON-NLS-1$
}
/**
* Returns <code>true</code> if this cell editor is able to perform the
* cut action.
* <p>
* This default implementation always returns <code>false</code>.
* </p>
* <p>
* Subclasses may override
* </p>
*
* @return <code>true</code> if cut is possible, <code>false</code>
* otherwise
*/
public boolean isCutEnabled() {
return false;
}
/**
* Returns <code>true</code> if this cell editor is able to perform the
* delete action.
* <p>
* This default implementation always returns <code>false</code>.
* </p>
* <p>
* Subclasses may override
* </p>
*
* @return <code>true</code> if delete is possible, <code>false</code>
* otherwise
*/
public boolean isDeleteEnabled() {
return false;
}
/**
* Returns whether the value of this cell editor has changed since the last
* call to <code>setValue</code>.
*
* @return <code>true</code> if the value has changed, and
* <code>false</code> if unchanged
*/
public boolean isDirty() {
return dirty;
}
/**
* Marks this cell editor as dirty.
*
* @since 2.1
*/
protected void markDirty() {
dirty = true;
}
/**
* Returns <code>true</code> if this cell editor is able to perform the
* find action.
* <p>
* This default implementation always returns <code>false</code>.
* </p>
* <p>
* Subclasses may override
* </p>
*
* @return <code>true</code> if find is possible, <code>false</code>
* otherwise
*/
public boolean isFindEnabled() {
return false;
}
/**
* Returns <code>true</code> if this cell editor is able to perform the
* paste action.
* <p>
* This default implementation always returns <code>false</code>.
* </p>
* <p>
* Subclasses may override
* </p>
*
* @return <code>true</code> if paste is possible, <code>false</code>
* otherwise
*/
public boolean isPasteEnabled() {
return false;
}
/**
* Returns <code>true</code> if this cell editor is able to perform the
* redo action.
* <p>
* This default implementation always returns <code>false</code>.
* </p>
* <p>
* Subclasses may override
* </p>
*
* @return <code>true</code> if redo is possible, <code>false</code>
* otherwise
*/
public boolean isRedoEnabled() {
return false;
}
/**
* Returns <code>true</code> if this cell editor is able to perform the
* select all action.
* <p>
* This default implementation always returns <code>false</code>.
* </p>
* <p>
* Subclasses may override
* </p>
*
* @return <code>true</code> if select all is possible, <code>false</code>
* otherwise
*/
public boolean isSelectAllEnabled() {
return false;
}
/**
* Returns <code>true</code> if this cell editor is able to perform the
* undo action.
* <p>
* This default implementation always returns <code>false</code>.
* </p>
* <p>
* Subclasses may override
* </p>
*
* @return <code>true</code> if undo is possible, <code>false</code>
* otherwise
*/
public boolean isUndoEnabled() {
return false;
}
/**
* Returns whether this cell editor has a valid value. The default value is
* false.
*
* @return <code>true</code> if the value is valid, and <code>false</code>
* if invalid
*
* @see #setValueValid(boolean)
*/
public boolean isValueValid() {
return valid;
}
/**
* Processes a key release event that occurred in this cell editor.
* <p>
* The default implementation of this framework method cancels editing when
* the ESC key is pressed. When the RETURN key is pressed the current value
* is applied and the cell editor deactivates. Subclasses should call this
* method at appropriate times. Subclasses may also extend or reimplement.
* </p>
*
* @param keyEvent
* the key event
*/
protected void keyReleaseOccured(KeyEvent keyEvent) {
if (keyEvent.character == '\u001b') { // Escape character
fireCancelEditor();
} else if (keyEvent.character == '\r') { // Return key
fireApplyEditorValue();
deactivate();
}
}
/**
* Processes a focus lost event that occurred in this cell editor.
* <p>
* The default implementation of this framework method applies the current
* value and deactivates the cell editor. Subclasses should call this method
* at appropriate times. Subclasses may also extend or reimplement.
* </p>
*/
protected void focusLost() {
if (isActivated()) {
fireApplyEditorValue();
deactivate();
}
}
/**
* Performs the copy action. This default implementation does nothing.
* <p>
* Subclasses may override
* </p>
*/
public void performCopy() {
}
/**
* Performs the cut action. This default implementation does nothing.
* <p>
* Subclasses may override
* </p>
*/
public void performCut() {
}
/**
* Performs the delete action. This default implementation does nothing.
* <p>
* Subclasses may override
* </p>
*/
public void performDelete() {
}
/**
* Performs the find action. This default implementation does nothing.
* <p>
* Subclasses may override
* </p>
*/
public void performFind() {
}
/**
* Performs the paste action. This default implementation does nothing.
* <p>
* Subclasses may override
* </p>
*/
public void performPaste() {
}
/**
* Performs the redo action. This default implementation does nothing.
* <p>
* Subclasses may override
* </p>
*/
public void performRedo() {
}
/**
* Performs the select all action. This default implementation does nothing.
* <p>
* Subclasses may override
* </p>
*/
public void performSelectAll() {
}
/**
* Performs the undo action. This default implementation does nothing.
* <p>
* Subclasses may override
* </p>
*/
public void performUndo() {
}
/**
* Removes the given listener from this cell editor. Has no affect if an
* identical listener is not registered.
*
* @param listener
* a cell editor listener
*/
public void removeListener(ICellEditorListener listener) {
listeners.remove(listener);
}
/**
* Removes the given property change listener from this cell editor. Has no
* affect if an identical property change listener is not registered.
*
* @param listener
* a property change listener
*/
public void removePropertyChangeListener(IPropertyChangeListener listener) {
propertyChangeListeners.remove(listener);
}
/**
* Sets or clears the current error message for this cell editor.
* <p>
* No formatting is done here, the message to be set is expected to be fully
* formatted before being passed in.
* </p>
*
* @param message
* the error message, or <code>null</code> to clear
*/
protected void setErrorMessage(String message) {
errorMessage = message;
}
/**
* Sets the focus to the cell editor's control.
*/
public void setFocus() {
doSetFocus();
}
/**
* Sets the input validator for this cell editor.
*
* @param validator
* the input validator, or <code>null</code> if none
*/
public void setValidator(ICellEditorValidator validator) {
this.validator = validator;
}
/**
* Sets this cell editor's value.
*
* @param value
* the value of this cell editor
*/
public final void setValue(Object value) {
valid = isCorrect(value);
dirty = false;
doSetValue(value);
}
/**
* Sets the valid state of this cell editor. The default value is false.
* Subclasses should call this method on construction.
*
* @param valid
* <code>true</code> if the current value is valid, and
* <code>false</code> if invalid
*
* @see #isValueValid
*/
protected void setValueValid(boolean valid) {
this.valid = valid;
}
/**
* The value has changed. Updates the valid state flag, marks this cell
* editor as dirty, and notifies all registered cell editor listeners of a
* value change.
*
* @param oldValidState
* the valid state before the end user changed the value
* @param newValidState
* the current valid state
* @see ICellEditorListener#editorValueChanged
*/
protected void valueChanged(boolean oldValidState, boolean newValidState) {
valid = newValidState;
dirty = true;
fireEditorValueChanged(oldValidState, newValidState);
}
/**
* Activate the editor but also inform the editor which event triggered its
* activation. <b>The default implementation simply calls
* {@link #activate()}</b>
*
* @param activationEvent
* the editor activation event
* @since 3.3
*/
public void activate(ColumnViewerEditorActivationEvent activationEvent) {
activate();
}
/**
* The default implementation of this method returns true. Subclasses that
* hook their own focus listener should override this method and return
* false. See also bug 58777.
*
* @return <code>true</code> to indicate that a focus listener has to be
* attached
* @since 3.4
*/
protected boolean dependsOnExternalFocusListener() {
return true;
}
/**
* @param event
* deactivation event
*
*/
protected void deactivate(ColumnViewerEditorDeactivationEvent event) {
deactivate();
}
/**
* Returns the duration, in milliseconds, between the mouse button click
* that activates the cell editor and a subsequent mouse button click that
* will be considered a <em>double click</em> on the underlying control.
* Clients may override, in particular, clients can return 0 to denote that
* two subsequent mouse clicks in a cell should not be interpreted as a
* double click.
*
* @return the timeout or <code>0</code>
*/
protected int getDoubleClickTimeout() {
return Display.getCurrent().getDoubleClickTime();
}
}