/******************************************************************************* | |
* 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 java.util.ArrayList; | |
import org.eclipse.bpel.common.ui.command.ICommandFramework; | |
import org.eclipse.bpel.common.ui.details.IDetailsAreaConstants; | |
import org.eclipse.bpel.common.ui.details.viewers.CComboViewer; | |
import org.eclipse.bpel.common.ui.editmodel.AbstractEditModelCommand; | |
import org.eclipse.bpel.common.ui.editmodel.EditModelCommandStack; | |
import org.eclipse.bpel.common.ui.flatui.FlatFormLayout; | |
import org.eclipse.bpel.model.Process; | |
import org.eclipse.bpel.model.adapters.IProperty; | |
import org.eclipse.bpel.ui.BPELEditor; | |
import org.eclipse.bpel.ui.BPELTabbedPropertySheetPage; | |
import org.eclipse.bpel.ui.actions.ShowPropertiesViewAction; | |
import org.eclipse.bpel.ui.adapters.AdapterNotification; | |
import org.eclipse.bpel.ui.adapters.IMarkerHolder; | |
import org.eclipse.bpel.ui.commands.CompoundCommand; | |
import org.eclipse.bpel.ui.proposal.providers.ModelContentProposalProvider; | |
import org.eclipse.bpel.ui.util.BPELUtil; | |
import org.eclipse.bpel.ui.util.MultiObjectAdapter; | |
import org.eclipse.core.resources.IFile; | |
import org.eclipse.core.resources.IMarker; | |
import org.eclipse.emf.common.notify.Notification; | |
import org.eclipse.emf.ecore.EObject; | |
import org.eclipse.emf.ecore.resource.Resource; | |
import org.eclipse.gef.commands.Command; | |
import org.eclipse.jface.viewers.ISelection; | |
import org.eclipse.jface.viewers.IStructuredSelection; | |
import org.eclipse.jface.viewers.StructuredSelection; | |
import org.eclipse.swt.SWT; | |
import org.eclipse.swt.events.DisposeEvent; | |
import org.eclipse.swt.events.DisposeListener; | |
import org.eclipse.swt.layout.FillLayout; | |
import org.eclipse.swt.widgets.Composite; | |
import org.eclipse.swt.widgets.Display; | |
import org.eclipse.ui.IFileEditorInput; | |
import org.eclipse.ui.IWorkbenchPart; | |
import org.eclipse.ui.internal.views.properties.tabbed.view.TabbedPropertyViewer; | |
import org.eclipse.ui.views.properties.tabbed.AbstractPropertySection; | |
import org.eclipse.ui.views.properties.tabbed.TabContents; | |
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage; | |
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory; | |
/** | |
* An abstract implementation which provides some adapter support and useful stuff. | |
* This was based on the common implementation characteristics of bpel.ui properties | |
* pages. | |
* | |
* Implementors may subclass this class, or they could extend AbstractPropertySection directly. | |
*/ | |
@SuppressWarnings("nls") | |
public abstract class BPELPropertySection extends AbstractPropertySection | |
{ | |
protected static final String EMPTY_STRING = ""; //$NON-NLS-1$ | |
protected static final IMarker EMPTY_MARKERS[] = new IMarker[] {}; | |
protected static final MultiObjectAdapter[] EMPTY_MULTI_OBJECT_ARRAY = new MultiObjectAdapter[0]; | |
/** Standard label width */ | |
public static final int STANDARD_LABEL_WIDTH_SM = 105; | |
/** Standard label width - applying the 25% fudge factor */ | |
public static final int STANDARD_LABEL_WIDTH_AVG = STANDARD_LABEL_WIDTH_SM * 5/4; | |
/** Standard label width - applying the 50% fudge factor */ | |
public static final int STANDARD_LABEL_WIDTH_LRG = STANDARD_LABEL_WIDTH_SM * 3/2; | |
/** Standard button width */ | |
public static final int STANDARD_BUTTON_WIDTH = 60; | |
/** Short button width */ | |
public static final int SHORT_BUTTON_WIDTH = 45; | |
protected MultiObjectAdapter[] fAdapters; | |
protected boolean isCreated; | |
protected boolean isHidden; | |
protected EObject fModelObject; | |
protected TabbedPropertySheetWidgetFactory fWidgetFactory; | |
protected BPELTabbedPropertySheetPage fTabbedPropertySheetPage; | |
final protected ModelContentProposalProvider.ValueProvider inputValueProvider = new ModelContentProposalProvider.ValueProvider () { | |
@Override | |
public Object value() { | |
return getModel(); | |
} | |
}; | |
/** | |
* @see org.eclipse.ui.views.properties.tabbed.AbstractPropertySection#getWidgetFactory() | |
*/ | |
@Override | |
public TabbedPropertySheetWidgetFactory getWidgetFactory() { | |
TabbedPropertySheetWidgetFactory wf = super.getWidgetFactory(); | |
wf.setBorderStyle(SWT.BORDER); | |
return wf; | |
} | |
/** | |
* Brand new shiny BPELPropertySection | |
*/ | |
public BPELPropertySection() { | |
super(); | |
fAdapters = createAdapters(); | |
} | |
/** | |
* Subclasses must override this to return DetailsAdapters. | |
* The default implementation returns an empty array. | |
*/ | |
protected MultiObjectAdapter[] createAdapters() { | |
return EMPTY_MULTI_OBJECT_ARRAY; | |
} | |
/** | |
* Removes all adapters from whatever they are attached to. | |
* Subclasses may override. | |
*/ | |
protected void removeAllAdapters() { | |
for (MultiObjectAdapter a : fAdapters) { | |
a.removeFromAll(); | |
} | |
} | |
/** | |
* This implementation just hooks the first adapter on the input object. | |
* Subclasses may override. | |
*/ | |
protected void addAllAdapters() { | |
assert isCreated : "Not yet created!" ; | |
if (fAdapters.length > 0) { | |
if (getModel() != null) { | |
fAdapters[0].addToObject(getModel()); | |
} | |
} | |
} | |
/** | |
* Convenience method for removing and re-adding adapters. This is a simple and general | |
* way to react to model changes that grow or shrink the set of model objects we want our | |
* adapters to be on. | |
*/ | |
protected void refreshAdapters() { | |
removeAllAdapters(); | |
addAllAdapters(); | |
} | |
/** | |
* This method is intended to set the input object. Subclasses may override this | |
* method to perform necessary cleanup before changing the input object, and/or | |
* perform initialization after changing the input object. | |
* | |
* Subclasses may also override to change the policy of which object is used as | |
* the input for a particular properties section. | |
* | |
* For example: a section for a custom activity, may wish to override this method | |
* to use the custom activity ExtensibilityElement as the "main" input object. | |
*/ | |
@SuppressWarnings("unchecked") | |
protected void basicSetInput(EObject newInput) { | |
fModelObject = newInput; | |
} | |
@SuppressWarnings("unchecked") | |
protected void restoreUserContextFromInput () { | |
IProperty<String,Object> prop = BPELUtil.adapt(fModelObject, IProperty.class); | |
if (prop != null) { | |
restoreUserContext( prop.getProperty( getClass().getName() ) ); | |
} | |
} | |
@SuppressWarnings("unchecked") | |
protected void saveUserContextToInput () { | |
IProperty<String,Object> prop = BPELUtil.adapt(fModelObject, IProperty.class); | |
if (prop != null) { | |
prop.setProperty(getClass().getName(), getUserContext() ); | |
} | |
} | |
/** | |
* @see org.eclipse.ui.views.properties.tabbed.AbstractPropertySection#setInput(org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection) | |
*/ | |
@Override | |
public final void setInput(IWorkbenchPart part, ISelection selection) { | |
super.setInput(part, selection); | |
if ((selection instanceof IStructuredSelection) == false) { | |
return ; | |
} | |
Object model = ((IStructuredSelection)selection).getFirstElement(); | |
if (model == fModelObject) { | |
return; | |
} | |
removeAllAdapters(); | |
super.setInput(part, selection); | |
basicSetInput((EObject)model); | |
// Careful: don't assume input == newInput. | |
// There are basicSetInput() hacks that violate that assumption =) | |
// TODO: is this comment related to the custom activities? | |
addAllAdapters(); | |
} | |
/** | |
* @see org.eclipse.ui.views.properties.tabbed.AbstractPropertySection#aboutToBeHidden() | |
*/ | |
@Override | |
public void aboutToBeHidden() { | |
isHidden = true; | |
} | |
/** | |
* @see org.eclipse.ui.views.properties.tabbed.AbstractPropertySection#aboutToBeShown() | |
*/ | |
@Override | |
public void aboutToBeShown() { | |
isHidden = false; | |
} | |
@SuppressWarnings("unchecked") | |
protected <T extends EObject> T getModel() { | |
return (T) fModelObject; | |
} | |
protected final <T extends EObject> T getInput() { | |
return getModel(); | |
} | |
/** | |
* Refresh the given CComboViewer, and also make sure selectedObject is selected in it. | |
*/ | |
protected void refreshCCombo(CComboViewer viewer, Object selectedObject) { | |
viewer.refresh(); | |
if (selectedObject == null) { | |
viewer.setSelectionNoNotify ( StructuredSelection.EMPTY ,false ); | |
} else { | |
viewer.setSelectionNoNotify (new StructuredSelection(selectedObject), false); | |
} | |
} | |
/** | |
* Create the controls. | |
* | |
* @see org.eclipse.ui.views.properties.tabbed.AbstractPropertySection#createControls(org.eclipse.swt.widgets.Composite, org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage) | |
*/ | |
@Override | |
public void createControls (final Composite parent, TabbedPropertySheetPage aTabbedPropertySheetPage) { | |
super.createControls(parent, aTabbedPropertySheetPage); | |
this.fTabbedPropertySheetPage = (BPELTabbedPropertySheetPage)aTabbedPropertySheetPage; | |
this.fWidgetFactory = getWidgetFactory(); | |
assert !isCreated : "Not yet created!"; | |
Composite marginComposite = fWidgetFactory.createComposite(parent); | |
FillLayout fillLayout = new FillLayout(); | |
fillLayout.marginWidth = IDetailsAreaConstants.HMARGIN; | |
fillLayout.marginHeight = IDetailsAreaConstants.VMARGIN/2; | |
marginComposite.setLayout(fillLayout); | |
createClient(marginComposite); | |
isHidden = true; | |
isCreated = true; | |
parent.addDisposeListener(new DisposeListener() { | |
public void widgetDisposed(DisposeEvent e) { | |
dispose(); | |
} | |
}); | |
} | |
/** | |
* Subclasses should override this to create the child controls of the section. | |
*/ | |
protected abstract void createClient(Composite parent); | |
/** | |
* @see org.eclipse.ui.views.properties.tabbed.AbstractPropertySection#dispose() | |
*/ | |
@Override | |
public void dispose() { | |
if (isCreated) { | |
// TODO HACK: this shouldn't really be here! But where should it be?? | |
getCommandFramework().applyCurrentChange(); | |
removeAllAdapters(); | |
} | |
isCreated = false; | |
} | |
/** | |
* @see org.eclipse.ui.views.properties.tabbed.AbstractPropertySection#refresh() | |
*/ | |
@Override | |
public void refresh() { | |
super.refresh(); | |
updateStatusLabels(); | |
} | |
/** | |
* Subclasses must override this method in order to refresh the status labels | |
*/ | |
protected void updateStatusLabels() { | |
} | |
/** | |
* Gets all IMarker according to the passed input model. | |
* @see IMarkerHolder.getMarkers | |
*/ | |
protected IMarker[] getMarkers (Object input) { | |
IMarkerHolder markerHolder = BPELUtil.adapt(input, IMarkerHolder.class); | |
if (markerHolder != null) { | |
ArrayList<IMarker> filteredMarkers = new ArrayList<IMarker>(4); | |
for(IMarker m : markerHolder.getMarkers(input)) { | |
if (isValidMarker(m)) { | |
filteredMarkers.add(m); | |
} | |
} | |
if (filteredMarkers.size() > 0) { | |
return filteredMarkers.toArray(EMPTY_MARKERS); | |
} | |
} | |
return EMPTY_MARKERS; | |
} | |
protected boolean markersHaveChanged ( Notification n ) { | |
int eventGroup = n.getEventType() / 100; | |
return eventGroup == AdapterNotification.NOTIFICATION_MARKERS_CHANGED_GROUP ; | |
} | |
protected void updateMarkers ( ) { | |
} | |
protected CompoundCommand makeCompound ( Command command ) { | |
if (command == null) { | |
return null; | |
} | |
if (command instanceof CompoundCommand) { | |
return (CompoundCommand) command; | |
} | |
CompoundCommand cc = new CompoundCommand (); | |
cc.add(command); | |
return cc; | |
} | |
protected void runCommand ( Command command ) { | |
getCommandFramework().execute( wrapInShowContextCommand(command) ); | |
} | |
protected ICommandFramework getCommandFramework() { | |
BPELEditor editor = getBPELEditor(); | |
if (editor != null) { | |
return editor.getCommandFramework(); | |
} | |
return null; | |
} | |
/** | |
* @return the BPELEditor | |
*/ | |
public BPELEditor getBPELEditor() { | |
if (fTabbedPropertySheetPage != null) { | |
return fTabbedPropertySheetPage.getEditor(); | |
} | |
return null; | |
} | |
/** | |
* @return the BPEL process | |
*/ | |
public Process getProcess() { | |
return getBPELEditor().getProcess(); | |
} | |
protected EditController createEditController ( ) { | |
return new EditController ( getCommandFramework() ) { | |
@Override | |
public Command createApplyCommand() { | |
return wrapInShowContextCommand( super.createApplyCommand() ); | |
} | |
}; | |
} | |
/** | |
* Convenience accessor with default policy (this is overridden in certain subclasses). | |
*/ | |
protected Command wrapInShowContextCommand(final Command inner) { | |
return wrapInShowContextCommand(inner, this); | |
} | |
/** | |
* Create a command that wraps the command passed in the show/restore context commands. | |
* | |
* @param inner the inner command to be run. | |
* @param section the BPEL property section | |
* @return the command new wrapped command. | |
*/ | |
protected Command wrapInShowContextCommand(final Command inner, BPELPropertySection section) { | |
/** | |
* Sometimes we have property sections inside property sections. | |
* | |
* The owners section's input needs to be saved, because it is used to restore | |
* the selection later on in the "wrapping" command. The "inner" section's input | |
* may not be visibly selectable. For example, consider "Variable" property sheet. | |
* A separate section is used for the "From" part of Variable. | |
*/ | |
final Object previousInput = section.getInput(); | |
final TabbedPropertyViewer viewer = getTabbedPropertySheetPage().getTabbedPropertyViewer(); | |
final int tabIndex = viewer.getSelectionIndex(); | |
// Bug 120110 - found this problem while building the extension activity examples | |
// it's possible that a property section can update the model while the property | |
// section tab itself has already been disposed (getCurrentTab() will be null). | |
final int sectionIndex = getTabbedPropertySheetPage().getCurrentTab()==null | |
? -1 | |
: getTabbedPropertySheetPage().getCurrentTab().getSectionIndex(section); | |
if (!inner.canExecute()) { | |
System.out.println("WARNING: unexecutable command passed to wrapInShowContextCommand():"); //$NON-NLS-1$ | |
System.out.println(" "+inner.getDebugLabel()); //$NON-NLS-1$ | |
} | |
return new AbstractEditModelCommand() { | |
Object beforeContext, afterContext; | |
@Override | |
public String getLabel() { | |
return inner.getLabel(); | |
} | |
@Override | |
public void setLabel(String label) { | |
inner.setLabel(label); | |
} | |
@Override | |
public String getDebugLabel() { | |
return "ShowContext wrapper:[" + inner.getDebugLabel() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ | |
} | |
@Override | |
public boolean canExecute() { | |
return inner.canExecute(); | |
} | |
@Override | |
public void execute() { | |
BPELPropertySection aSection = getSection(sectionIndex); | |
beforeContext = (aSection==null)? null : aSection.getUserContext(); | |
inner.execute(); | |
afterContext = (aSection==null)? null : aSection.getUserContext(); | |
} | |
@Override | |
public boolean canUndo() { | |
return inner.canUndo(); | |
} | |
@Override | |
public void undo() { | |
inner.undo(); | |
showPropertiesTab(); | |
BPELPropertySection aSection = getSection(sectionIndex); | |
if (aSection != null) { | |
aSection.restoreUserContext(beforeContext); | |
} | |
} | |
@Override | |
public void redo() { | |
inner.redo(); | |
showPropertiesTab(); | |
BPELPropertySection aSection = getSection(sectionIndex); | |
if (aSection != null) { | |
aSection.restoreUserContext(afterContext); | |
} | |
} | |
@Override | |
public void dispose() { | |
inner.dispose(); | |
} | |
protected BPELPropertySection getSection (int index) { | |
TabContents tab = getTabbedPropertySheetPage().getCurrentTab(); | |
if (tab != null) { | |
return (BPELPropertySection) tab.getSectionAtIndex(sectionIndex); | |
} | |
return null; | |
} | |
protected void showPropertiesTab() { | |
// TODO: Try to avoid selecting the model object all | |
// the time, as it could cause unnecessary flashing. | |
getBPELEditor().selectModelObject(previousInput); | |
if (tabIndex != viewer.getSelectionIndex()) { | |
Object selectedTab = viewer.getElementAt(tabIndex); | |
if (selectedTab != null) { | |
viewer.setSelection(new StructuredSelection(selectedTab)); | |
} | |
} | |
} | |
// TODO: THIS IS A HACK.. these helpers might belong somewhere else. | |
@Override | |
public Resource[] getResources() { | |
return EditModelCommandStack.getResources(inner); | |
} | |
@Override | |
public Resource[] getModifiedResources() { | |
return EditModelCommandStack.getModifiedResources(inner); | |
} | |
}; | |
} | |
/** | |
* Creates a composite with a flat border around it. | |
*/ | |
protected Composite createBorderComposite(Composite parent) { | |
return BPELUtil.createBorderComposite(parent, fWidgetFactory); | |
} | |
/** | |
* NOTE: use this method, NOT the method in TabbedPropertySheetWidgetFactory, | |
* whose semantics were inexplicably changed. | |
* | |
* TODO: We need a new/better story for layouts and borders ?? | |
*/ | |
protected Composite createFlatFormComposite(Composite parent) { | |
Composite result = fWidgetFactory.createFlatFormComposite(parent); | |
FlatFormLayout formLayout = new FlatFormLayout(); | |
formLayout.marginWidth = formLayout.marginHeight = 0; | |
result.setLayout(formLayout); | |
return result; | |
} | |
/** | |
* @return the BPEL Tabbed Property Sheet page. | |
*/ | |
public BPELTabbedPropertySheetPage getTabbedPropertySheetPage() { | |
return fTabbedPropertySheetPage; | |
} | |
/** | |
* @return the IFile that the editor is editing. | |
*/ | |
public IFile getBPELFile() { | |
return ((IFileEditorInput) getBPELEditor().getEditorInput()).getFile(); | |
} | |
/** | |
* Returns a token indicating which widget should have focus. Note that the token can't | |
* be tied to this particular instance of the section; after the section is destroyed | |
* and re-created, the token must still be valid. | |
* @return the user context | |
*/ | |
public Object getUserContext() { | |
return null; | |
} | |
/** | |
* Accepts a token created by getUserContext() and gives focus to the widget represented | |
* by the token. | |
* @param userContext the user context to restore. | |
*/ | |
public void restoreUserContext(Object userContext) { | |
} | |
/** | |
* Shows the given marker. | |
* @param marker | |
*/ | |
public void gotoMarker (IMarker marker) { | |
} | |
/** | |
* Returns true if this section knows how to show the given marker. | |
* | |
* @param marker the marker to be checked. | |
* @return true if so, false otherwise ... | |
*/ | |
public boolean isValidMarker (IMarker marker) { | |
return true; | |
} | |
/** | |
* Return the Context names that allows us to point markers correctly at this | |
* section. | |
* | |
* @return an array of context names | |
*/ | |
public String[] getContextNames () { | |
return new String[] {}; | |
} | |
/** | |
* Given a model object, selects it in the BPEL Editor and makes sure the | |
* properties pages are also shown for it. | |
*/ | |
protected void selectModelObject(final EObject target) { | |
Runnable runnable = new Runnable() { | |
public void run() { | |
getBPELEditor().selectModelObject(target); | |
new ShowPropertiesViewAction().run(); | |
} | |
}; | |
Display.getDefault().asyncExec(runnable); | |
} | |
} |