blob: 9033604145fc76b1c7666cd57024421bce3ff1e5 [file] [log] [blame]
/*******************************************************************************
* 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();
this.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 : this.fAdapters) {
a.removeFromAll();
}
}
/**
* This implementation just hooks the first adapter on the input object.
* Subclasses may override.
*/
protected void addAllAdapters() {
assert this.isCreated : "Not yet created!" ;
if (this.fAdapters.length > 0) {
if (getModel() != null) {
this.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.
*/
protected void basicSetInput(EObject newInput) {
this.fModelObject = newInput;
}
@SuppressWarnings("unchecked")
protected void restoreUserContextFromInput () {
IProperty<String,Object> prop = BPELUtil.adapt(this.fModelObject, IProperty.class);
if (prop != null) {
restoreUserContext( prop.getProperty( getClass().getName() ) );
}
}
@SuppressWarnings("unchecked")
protected void saveUserContextToInput () {
IProperty<String,Object> prop = BPELUtil.adapt(this.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 == this.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() {
this.isHidden = true;
}
/**
* @see org.eclipse.ui.views.properties.tabbed.AbstractPropertySection#aboutToBeShown()
*/
@Override
public void aboutToBeShown() {
this.isHidden = false;
}
@SuppressWarnings("unchecked")
protected <T extends EObject> T getModel() {
return (T) this.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 !this.isCreated : "Not yet created!";
Composite marginComposite = this.fWidgetFactory.createComposite(parent);
FillLayout fillLayout = new FillLayout();
fillLayout.marginWidth = IDetailsAreaConstants.HMARGIN;
fillLayout.marginHeight = IDetailsAreaConstants.VMARGIN/2;
marginComposite.setLayout(fillLayout);
createClient(marginComposite);
this.isHidden = true;
this.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 (this.isCreated) {
// TODO HACK: this shouldn't really be here! But where should it be??
getCommandFramework().applyCurrentChange();
removeAllAdapters();
}
this.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 (this.fTabbedPropertySheetPage != null) {
return this.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);
this.beforeContext = (aSection==null)? null : aSection.getUserContext();
inner.execute();
this.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(this.beforeContext);
}
}
@Override
public void redo() {
inner.redo();
showPropertiesTab();
BPELPropertySection aSection = getSection(sectionIndex);
if (aSection != null) {
aSection.restoreUserContext(this.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, this.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 = this.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 this.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);
}
}