/*******************************************************************************
* Copyright (c) 2005, 2012 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
*******************************************************************************/
package org.eclipse.bpel.ui.properties;
import org.eclipse.bpel.common.ui.command.ICommandFramework;
import org.eclipse.bpel.common.ui.details.IOngoingChange;
import org.eclipse.bpel.common.ui.details.IValue;
import org.eclipse.bpel.ui.Messages;
import org.eclipse.bpel.ui.commands.util.UpdateModelCommand;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.gef.commands.Command;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
/**
* The edit controller listens on both the model object and the UI control that is used to edit it
* and make sure that the two items are in sync. In addition any pending changes are applied as
* an update command and placed on the command stack.
*
* The IValue interface provides for a way to get() and set() and object from/into the receiver.
* There is one such proxy for the model side (how to get/set items in the model) and one
* such proxy for the view side (how to get/set the value of the model in the view).
*
* The Controller has an input object and when set (or re-set via setInput()) it will register
* itself as an adapter of the input object. As such it receives all notifications from the
* object about changes and can thus update the view from that (look at the notifyChanged()
* method below).
*
* On the view side, it registers to listen for various control changes on the UI controls and
* when such changes occur it notifies the command framework of pending or completed edits.
* These edits are wrapped into an update command which uses the target object and the feature
* to set the value in the model.
*
*/
public class EditController implements IOngoingChange, Listener , Adapter {
/** The command framework */
ICommandFramework fCommandFramework;
/** Updating view */
boolean fbViewUpdate = false;
/** The input object, that we are controlling */
EObject fInput;
/** The feature to set/unset (may be unspecified when custom model setter is used) */
EStructuralFeature fFeature;
/** The label */
String fLabel;
/** That's how we set/get the value into the view */
IValue fViewValue;
/** That's how we set/get the values into the model */
IValue fModelValue ;
/**
*
* @param commandFramework
*/
public EditController (ICommandFramework commandFramework ) {
fCommandFramework = commandFramework;
}
/**
* @param target
*/
public void setInput (EObject target) {
if (fInput != null) {
fInput.eAdapters().remove(this);
}
fInput = target;
if (fInput != null) {
fInput.eAdapters().add(this);
}
}
/**
* @return the current input
*/
public EObject getInput () {
return fInput;
}
/**
* @param feature
*/
public void setFeature ( EStructuralFeature feature ) {
if (fFeature == feature) {
return ;
}
fFeature = feature;
fModelValue = new IValue () {
public Object get() {
return fInput.eGet(fFeature);
}
public void set(Object object) {
fInput.eSet(fFeature,object);
}
};
}
/**
* Set the model IValue proxy. This is how data is set/gotten from the model. In most cases, this will
* be simply the defined mechanism in this class (using EMF reflection).
* @param value
*/
public void setModeIValue ( IValue value ) {
fModelValue = value;
}
/**
* Return the model IValue proxy. This is how data is set/gotten from the model. In most cases, this will
* be simply the defined mechanism in this class (using EMF reflection).
*
* @return the model IValue proxy
*/
public IValue getModelIValue() {
return fModelValue;
}
/**
* Get the IValue proxy for the view side. This is how data is pushed into the view or retrieved from a view.
* Any transformation of data from text to model object has to happen in such proxies.
*
* @param value
*/
public void setViewIValue ( IValue value ) {
fViewValue = value;
}
/**
* Return the view IValue proxy. This is how data is set/gotten from the view.
* @return the view IValue proxy
*/
public IValue getViewIValue() {
return fViewValue;
}
/**
* The label argument.
*
* @param label
*/
public void setLabel ( String label ) {
fLabel = label;
}
/**
* @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
*/
public void handleEvent (Event event) {
if (fbViewUpdate) {
return ;
}
switch (event.type) {
case SWT.KeyDown:
if (event.character == SWT.CR) {
finish();
}
break;
case SWT.FocusOut:
finish();
break;
case SWT.Modify:
case SWT.Selection:
case SWT.DefaultSelection:
modify();
break;
case SWT.Dispose:
abort();
setInput(null);
break;
}
}
void finish() {
fCommandFramework.notifyChangeDone(this);
}
void modify() {
fCommandFramework.notifyChangeInProgress(this);
}
void abort () {
fCommandFramework.abortCurrentChange();
}
protected boolean isModifyBasedControl(Control c) {
if (c instanceof CCombo) {
return (c.getStyle() & SWT.READ_ONLY) == 0; // if not read only
}
return (c instanceof Text);
}
protected boolean isSelectionBasedControl(Control c) {
return !(c instanceof Text);
}
void registerListener ( Control control, int eventType ) {
control.addListener(eventType, this);
}
/**
* Registers this ChangeHelper with the given control to listen for events
* which indicate that a change is in progress (or done).
*
* @param controls
*/
public void startListeningTo (Control ... controls ) {
for (Control control : controls) {
registerListener ( control, SWT.FocusOut );
registerListener ( control, SWT.Dispose );
if (isModifyBasedControl(control)) {
registerListener ( control, SWT.Modify );
}
if (isSelectionBasedControl(control)) {
registerListener ( control, SWT.Selection );
registerListener ( control, SWT.DefaultSelection );
}
}
}
/**
* Registers this ChangeHelper with the given control to listen for the
* Enter key. When Enter is pressed, the change is considered done (this
* is appropriate for single-line Text widgets).
* @param controls
*/
public void startListeningForEnter (Control ... controls) {
// NOTE: KeyDown rather than KeyUp, because of similar usage in CCombo.
for(Control control : controls) {
registerListener ( control, SWT.KeyDown );
}
}
/**
* Unregisters this ChangeHelper from a control previously passed to
* startListeningTo() and/or startListeningForEnter().
* @param controls
*/
public void stopListeningTo (Control ...controls ) {
for(Control control : controls) {
control.removeListener(SWT.FocusOut, this);
if (isModifyBasedControl(control)) {
control.removeListener(SWT.Modify, this);
}
if (isSelectionBasedControl(control)) {
control.removeListener(SWT.Selection, this);
control.removeListener(SWT.DefaultSelection, this);
}
control.removeListener(SWT.KeyDown, this);
}
}
/**
* The apply command is a generic UpdateModelCommand which sets the value of the model
* from the view. Because this is an AutoUndoRedoCommand, everything that is mutated
* is recorded and so it can be undone at some point.
*
* @see org.eclipse.bpel.common.ui.details.IOngoingChange#createApplyCommand()
*/
public Command createApplyCommand() {
return new UpdateModelCommand(fInput,getLabel()) {
@Override
public void doExecute() {
fModelValue.set ( fViewValue.get() );
}
};
}
/**
* @see org.eclipse.bpel.common.ui.details.IOngoingChange#getLabel()
*/
@SuppressWarnings("nls")
public String getLabel() {
if (fLabel != null) {
return NLS.bind(Messages.SetCommand_Change_2, fLabel );
}
if (fFeature != null) {
return NLS.bind(Messages.SetCommand_Change_2, fFeature.getName() );
}
return NLS.bind(Messages.SetCommand_Change_2, "...");
}
/**
* @see org.eclipse.bpel.common.ui.details.IOngoingChange#restoreOldState()
*/
public void restoreOldState() {
}
/**
* @see org.eclipse.emf.common.notify.Adapter#getTarget()
*/
public Notifier getTarget() {
return fInput;
}
/**
* @see org.eclipse.emf.common.notify.Adapter#isAdapterForType(java.lang.Object)
*/
public boolean isAdapterForType (Object type) {
Class<?> clazz = null;
// what is type ? (an interface)
if (type instanceof Class) {
clazz = (Class<?>) type;
return clazz.isInstance(this);
}
// what else could it be ?
return false;
}
/**
* The model is notifying us that something has happened.
*
* @see org.eclipse.emf.common.notify.Adapter#notifyChanged(org.eclipse.emf.common.notify.Notification)
*/
public void notifyChanged (Notification notification) {
// System.out.println(this + ".notifyChanged(): " + notification);
if (notification.getEventType() == Notification.SET || notification.getEventType() == Notification.UNSET) {
if ( notification.getFeature() == fFeature || checkNotification(notification)) {
updateView ( fModelValue.get () );
}
} else if (notification.getEventType() == Notification.REMOVING_ADAPTER) {
// If we were removed, then we set the view to "null"
if (notification.getOldValue() == this) {
updateView (null);
}
}
}
/**
* Check if the notification is one that we should be concerned about.
*
* This method may be overridden by a subclass if no feature is known or a method
* other then "EMF reflection" is used to set/get values from the model.
*
* @param notification
* @return true if yes, false if no
*/
public boolean checkNotification ( Notification notification ) {
return false;
}
/**
* Update the view using the value given
*
* @param value
*/
void updateView (Object value) {
fbViewUpdate = true;
try {
fViewValue.set( value );
} finally {
fbViewUpdate = false;
}
}
/**
* Target has changed, which for us just means to update the view from the model
*
* @see org.eclipse.emf.common.notify.Adapter#setTarget(org.eclipse.emf.common.notify.Notifier)
*/
public void setTarget (Notifier newTarget) {
if (newTarget == null) {
updateView(null);
} else {
updateView ( fModelValue.get() );
}
}
}