| /******************************************************************************* |
| * Copyright (c) 2000, 2003 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.jface.viewers; |
| |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.jface.util.Assert; |
| import org.eclipse.jface.util.IPropertyChangeListener; |
| import org.eclipse.jface.util.ListenerList; |
| 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; |
| /** |
| * 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(3); |
| /** |
| * List of cell editor property change listeners |
| * (element type: <code>IPropertyChangeListener</code>). |
| */ |
| private ListenerList propertyChangeListeners = new ListenerList(3); |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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 isVisible()==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]; |
| Platform.run(new SafeRunnable() { |
| public void run() { |
| l.applyEditorValue(); |
| } |
| public void handleException(Throwable e) { |
| super.handleException(e); |
| //If and unexpected exception happens, remove it |
| //to make sure the workbench keeps running. |
| removeListener(l); |
| } |
| }); |
| } |
| } |
| /** |
| * 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]; |
| Platform.run(new SafeRunnable() { |
| public void run() { |
| l.cancelEditor(); |
| } |
| public void handleException(Throwable e) { |
| super.handleException(e); |
| //If and unexpected exception happens, remove it |
| //to make sure the workbench keeps running. |
| removeListener(l); |
| } |
| }); |
| } |
| } |
| /** |
| * 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]; |
| Platform.run(new SafeRunnable() { |
| public void run() { |
| l.editorValueChanged(oldValidState, newValidState); |
| } |
| public void handleException(Throwable e) { |
| super.handleException(e); |
| //If and unexpected exception happens, remove it |
| //to make sure the workbench keeps running. |
| removeListener(l); |
| } |
| }); |
| } |
| } |
| /** |
| * 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]; |
| Platform.run(new SafeRunnable() { |
| public void run() { |
| l.propertyChange(new PropertyChangeEvent(this, actionId, null, null)); |
| } |
| public void handleException(Throwable e) { |
| super.handleException(e); |
| //If and unexpected exception happens, remove it |
| //to make sure the workbench keeps running. |
| removePropertyChangeListener(l); |
| } |
| }); |
| } |
| } |
| /** |
| * 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 visible, and <code>false</code> if not visible |
| */ |
| public boolean isActivated() { |
| return control != null && control.isVisible(); |
| } |
| /** |
| * 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. |
| * |
| * @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 |
| */ |
| 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. |
| * |
| * @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 valie 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); |
| } |
| } |