/******************************************************************************* | |
* 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 2.0 | |
* which accompanies this distribution, and is available at | |
* https://www.eclipse.org/legal/epl-2.0/ | |
* | |
* SPDX-License-Identifier: EPL-2.0 | |
* | |
* 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() ); | |
} | |
} | |
} |