/*******************************************************************************
* Copyright (c) 2006-2013 The RCP Company 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:
* The RCP Company - initial API and implementation
*******************************************************************************/
package com.rcpcompany.uibindings.internal.cellEditors;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.IHandler;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
import org.eclipse.jface.viewers.TextCellEditor;
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.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.handlers.IHandlerActivation;
import org.eclipse.ui.handlers.IHandlerService;
import com.rcpcompany.uibindings.Constants;
import com.rcpcompany.uibindings.IBindingContext;
import com.rcpcompany.uibindings.IManager;
import com.rcpcompany.uibindings.IValueBinding;
import com.rcpcompany.uibindings.IValueBindingCell;
import com.rcpcompany.uibindings.internal.Activator;
import com.rcpcompany.utils.logging.LogUtils;
/**
* Special edition of {@link TextCellEditor}.
*
* @author Tonny Madsen, The RCP Company
*/
public class ControlCellEditor extends CellEditor {
/**
* The control of the control cell editor can implement this interface to let the interaction
* with the editor easier.
*/
public interface IControlCellEditorSupport {
/**
* Returns the primary {@link Text} control of the shown control.
*
* @return the primary {@link Text} control or <code>null</code>
*/
Text getTextControl();
/**
* Returns whether the control has opened a popup dialog.
* <p>
* The information is used to determine whether focus lost can be because of an open dialog.
*
* @return <code>true</code> if the control has opened a dialog
*/
boolean hasOpenDialog();
}
/**
* The cell of the editor.
*/
private final IValueBindingCell myCell;
/**
* The editor binding for the control of this cell editor.
*/
private IValueBinding myEditorBinding;
/**
* For Text based editors: Used to signal that the text has just been replaced and should not be
* selected.
*/
private boolean justReplacedText = false;
private IHandlerActivation myActiveUndoHandler;
/* package */IHandler undoHandler = new AbstractHandler() {
@Override
public Object execute(ExecutionEvent event) {
if (Activator.getDefault().TRACE_HANDLERS) {
LogUtils.debug(this, "");
}
cancelEdit();
return null;
}
};
/**
* The context - cached to handle when the binding is disposed...
*/
private final IBindingContext myContext;
/**
* Constructs and returns a new cell editors for the specified cell.
*
* @param parent the parent composite
* @param cell the cell for this cell editor
*/
public ControlCellEditor(Composite parent, IValueBindingCell cell) {
super(parent, SWT.SINGLE | SWT.LEAD);
myCell = cell;
myContext = myCell.getLabelBinding().getContext();
/*
* Call create again... this time myCell is set.
*/
create(parent);
}
@Override
protected Control createControl(Composite parent) {
/*
* Called twice!
*
* First time indirectly from the super constructor and thus myCell is not set.
*/
if (getCell() == null) return null;
return getLabelBinding().createPreferredControl(parent, SWT.NONE, true);
}
/**
* Returns the cell of the {@link CellEditor}.
*
* @return the cell
*/
private IValueBindingCell getCell() {
return myCell;
}
/**
* Returns the label binding used in the container for the cell.
* <p>
* This is <em>not</em> the same as the binding for the control of this cell editor.
*
* @return the label binding
*/
private IValueBinding getLabelBinding() {
return getCell().getLabelBinding();
}
/**
* Returns the editor binding for this cell editor.
*
* @return the binding
*/
public IValueBinding getEditorBinding() {
return myEditorBinding;
}
/**
* Sets the editor binding of this cell editor.
*
* @param binding the new editor binding
*/
public void setEditorBinding(IValueBinding binding) {
myEditorBinding = binding;
initControl();
}
/**
* Initializes the control.
*/
private void initControl() {
final Control c = getControl();
final Text t = getTextControl();
c.addListener(SWT.DefaultSelection, myListener);
c.addListener(SWT.KeyDown, myListener);
c.addListener(SWT.Traverse, myListener);
c.addListener(SWT.FocusOut, myListener);
if (t != null && t != c) {
t.addListener(SWT.FocusOut, myListener);
}
}
/**
* Listener for the control of the cell editor.
*/
private final Listener myListener = new Listener() {
@Override
public void handleEvent(Event event) {
// LogUtils.debug(this, ToStringUtils.toString(event));
switch (event.type) {
case SWT.DefaultSelection:
endEdit();
break;
case SWT.KeyDown:
if (event.character == SWT.ESC) {
cancelEdit();
} else if (event.character == SWT.CR) {
/*
* The general handling of CR in Text widgets is via the Selection event (see
* above).
*
* But... for a MULTI-line Text widget M1+CR is also handled
*/
final Text text = getTextControl();
if (text != null) {
if (!text.isDisposed() && (text.getStyle() & SWT.MULTI) == SWT.MULTI
&& (event.stateMask & SWT.CTRL) != SWT.CTRL) {
endEdit();
}
} else {
// endEdit();
}
}
break;
case SWT.FocusOut:
focusLost();
break;
case SWT.Traverse:
/*
* ESC and CR are handled by the editor
*/
if (event.detail == SWT.TRAVERSE_ESCAPE || event.detail == SWT.TRAVERSE_RETURN) {
event.doit = false;
}
break;
default:
break;
}
}
};
/**
* Returns the context for this cell editor.
* <p>
* It is the same context for the {@link #getLabelBinding() label binding} and the cell
* {@link #getEditorBinding() editor binding}.
*
* @return the context
*/
private IBindingContext getContext() {
return myContext;
}
/**
* Returns the primary {@link Text} control component of the current {@link #getControl() cell
* editor control} if one exists.
*
* @return the primary {@link Text} control or <code>null</code>
*/
private Text getTextControl() {
if (getControl() instanceof Text) return (Text) getControl();
if (getControl() instanceof IControlCellEditorSupport)
return ((IControlCellEditorSupport) getControl()).getTextControl();
final Control c = getEditorBinding().getUIAttribute().getFieldAssistControl();
if (c instanceof Text) return (Text) c;
return null;
}
@Override
public void activate(ColumnViewerEditorActivationEvent activationEvent) {
super.activate(activationEvent);
/*
* If the editor was activated using a key - see Manager.isEditCellAnyKey() - then replace
* the content and consume the event
*/
if (activationEvent.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED
&& !Character.isISOControl(activationEvent.character)) {
final Text t = getTextControl();
if (t != null) {
t.setText(new String(new char[] { activationEvent.character }));
justReplacedText = true;
if (activationEvent.sourceEvent instanceof KeyEvent) {
final KeyEvent e = (KeyEvent) activationEvent.sourceEvent;
e.doit = false;
}
}
}
final String commandID = IManager.Factory.getManager().getCommandIDs()
.get(IWorkbenchCommandConstants.EDIT_UNDO);
if (Activator.getDefault().TRACE_HANDLERS) {
LogUtils.debug(undoHandler, "activating '" + commandID + "' handler");
}
final IHandlerService hs = (IHandlerService) getContext().getServiceLocator().getService(IHandlerService.class);
myActiveUndoHandler = hs.activateHandler(commandID, undoHandler, Constants.TRUE_EXPRESSION);
}
@Override
public void deactivate() {
super.deactivate();
if (myActiveUndoHandler != null) {
if (Activator.getDefault().TRACE_HANDLERS) {
final String commandID = IManager.Factory.getManager().getCommandIDs()
.get(IWorkbenchCommandConstants.EDIT_UNDO);
LogUtils.debug(undoHandler, "deactivating '" + commandID + "' handler");
}
final IHandlerService hs = (IHandlerService) getContext().getServiceLocator().getService(
IHandlerService.class);
hs.deactivateHandler(myActiveUndoHandler);
myActiveUndoHandler = null;
}
}
@Override
protected void doSetFocus() {
getControl().setFocus();
final Text text = getTextControl();
if (text != null) {
if (justReplacedText) {
text.setSelection(text.getText().length());
justReplacedText = false;
} else {
text.selectAll();
}
}
}
@Override
protected void focusLost() {
/*
* If the control has opened a dialog, then do nothing...
*/
if (getControl() instanceof IControlCellEditorSupport
&& ((IControlCellEditorSupport) getControl()).hasOpenDialog()) return;
super.focusLost();
}
/**
* Cancels the current edit.
*/
private void cancelEdit() {
fireCancelEditor();
deactivate();
}
/**
* Ends the current edit.
* <p>
* Handled as a focus lost event...
*/
private void endEdit() {
focusLost();
}
@Override
protected Object doGetValue() {
return null;
}
@Override
protected void doSetValue(Object value) {
}
}