blob: f058334bf75e50fa9c10e942cb7f1d4c8752d3e1 [file] [log] [blame]
// NewModifierPage.java
package org.eclipse.stem.ui.wizards;
/*******************************************************************************
* Copyright (c) 2008 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
*******************************************************************************/
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.IItemPropertyDescriptor;
import org.eclipse.emf.edit.provider.IItemPropertySource;
import org.eclipse.stem.core.common.CommonPackage;
import org.eclipse.stem.core.common.Identifiable;
import org.eclipse.stem.core.graph.GraphPackage;
import org.eclipse.stem.core.graph.LabelValue;
import org.eclipse.stem.core.graph.StaticLabel;
import org.eclipse.stem.core.modifier.FeatureModifier;
import org.eclipse.stem.core.modifier.NOPModifier;
import org.eclipse.stem.core.modifier.RangeModifier;
import org.eclipse.stem.core.modifier.SequenceModifier;
import org.eclipse.stem.core.modifier.SingleValueModifier;
import org.eclipse.stem.ui.adapters.featuremodifier.EStructuralFeatureFeatureModifierAdapterFactory;
import org.eclipse.stem.ui.adapters.featuremodifiereditcomposite.FeatureModifierEditCompositeAdapter;
import org.eclipse.stem.ui.adapters.featuremodifiereditcomposite.FeatureModifierFeatureModifierEditCompositeAdapterFactory;
import org.eclipse.stem.ui.adapters.propertystrings.PropertyStringProvider;
import org.eclipse.stem.ui.adapters.propertystrings.PropertyStringProviderAdapter;
import org.eclipse.stem.ui.adapters.propertystrings.PropertyStringProviderAdapterFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.PlatformUI;
/**
* This class is a {@link NewIdentifiablePage} that is customized to contain a
* list of the {@link EStructuralFeature}s of an {@link Identifiable} that can
* be modified by a {@link FeatureModifier}. It is extended for each type of
* {@link Identifiable} which can be modified. Instances of subclasses of this
* class are created by an composable adapter factory. This approach allows new
* types of {@link Identifiable}s to be added to the system and have the
* {@link NewModifierWizard} be able to handle them, complete with NLS
* translations, etc.
*/
abstract public class NewModifierPage extends NewIdentifiablePage {
/**
* This is the {@link Identifiable} for which the page is creating a
* {@link org.eclipse.stem.core.modifier.Modifier}.
*/
protected EObject target;
private final List<FeatureModifierComposite> featureModifierComposites = new ArrayList<FeatureModifierComposite>();
/**
* Constructor
*
* @param identifiable
* the {@link Identifiable} for which the page is creating a
* {@link org.eclipse.stem.core.modifier.Modifier}.
*/
protected NewModifierPage(final EObject target) {
super(Messages.getString("NModifierWiz.page_title"));
setTitle(Messages.getString("NModifierWiz.page_title")); //$NON-NLS-1$
setDescription(Messages.getString("NModifierWiz.page_description")); //$NON-NLS-1$
this.target = target;
} // NewModifierPage
/**
* @see org.eclipse.stem.ui.wizards.NewIdentifiableWizard.NewIdentifiablePage#createSpecificComposite(org.eclipse.swt.widgets.Composite)
*/
@Override
protected Composite createSpecificComposite(final Composite parent) {
//Display display = parent.getDisplay();
//Color blue = display.getSystemColor(SWT.COLOR_BLUE);
final Composite outerComposite = new Composite(parent,SWT.BORDER);
outerComposite.setLayout(new FillLayout());
// set the minimum width and height of the scrolled content - method 2
final ScrolledComposite scrolledComposite = new ScrolledComposite(outerComposite, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
scrolledComposite.setExpandHorizontal(true);
scrolledComposite.setExpandVertical(true);
final Composite innerComposite = new Composite(scrolledComposite, SWT.NONE);
scrolledComposite.setContent(innerComposite);
//innerComposite.setBackground(blue);
GridLayout layout = new GridLayout();
layout.numColumns = 2;
innerComposite.setLayout(layout);
scrolledComposite.setMinSize(innerComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
innerComposite.setSize(innerComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
innerComposite.layout();
Point innerSize = innerComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT);
scrolledComposite.setSize(innerSize);
outerComposite.setSize(innerSize);
innerComposite.layout();
outerComposite.layout();
parent.layout();
/* Not needed with Chris fix to the wizards
Label testLabel = new Label(innerComposite, SWT.BORDER);
testLabel.setText("press to scroll");
Button add = new Button (innerComposite, SWT.PUSH);
add.setText("force scroll");
final int[] index = new int[]{0};
add.addListener(SWT.Selection, new Listener() {
public void handleEvent(@SuppressWarnings("unused") Event e) {
index[0]++;
Label emptyLabel = new Label(innerComposite, SWT.NONE);
emptyLabel.setText("bottom");
// reset size of content so children can be seen - method 1
innerComposite.setSize(innerComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
innerComposite.layout();
// reset the minimum width and height so children can be seen - method 2
Point innerSize = innerComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT);
innerSize.y*=3;
innerSize.y/=2;
scrolledComposite.setMinSize(innerSize);
}
});
*/
// Get the adapter that will provide NLS'd names for the
// properties of the Identifiable
final PropertyStringProviderAdapter pspa = (PropertyStringProviderAdapter) PropertyStringProviderAdapterFactory.INSTANCE
.adapt(target, PropertyStringProvider.class);
final ComposedAdapterFactory itemProviderFactory = new ComposedAdapterFactory(
ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
final IItemPropertySource propertySource = (IItemPropertySource) itemProviderFactory
.adapt(target, IItemPropertySource.class);
final List<IItemPropertyDescriptor> properties = propertySource
.getPropertyDescriptors(target);
for (final IItemPropertyDescriptor descriptor : properties) {
final EStructuralFeature feature = (EStructuralFeature) descriptor
.getFeature(null);
// Is this a property that the user specifies?
if (isUserSpecifiedProperty(feature)) {
// Yes
// Label for the name of the property
final Label label = new Label(innerComposite, SWT.NONE);
label.setText(pspa.getPropertyName(descriptor));
// label.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_CYAN));
GridData gridData1 = new GridData(GridData.BEGINNING);
gridData1.horizontalAlignment = SWT.BEGINNING;
gridData1.horizontalIndent = 0;
label.setLayoutData(gridData1);
// Composite for the editor
final FeatureModifierComposite featureModifierComposite = new FeatureModifierComposite(
innerComposite, target, feature, pspa, descriptor,
isModifiableProperty(feature), this);
GridData gridData2 = new GridData(GridData.BEGINNING);
gridData2.grabExcessHorizontalSpace = true;
gridData2.horizontalAlignment = SWT.FILL;
gridData2.horizontalIndent = 0;
featureModifierComposite.setLayoutData(gridData2);
featureModifierComposites.add(featureModifierComposite);
} // if user specified
} // for each property
// Any text for the bottom?
final String bottomText = getBottomText();
if (bottomText != null) {
// Yes
final Label bottomTextLabel = new Label(innerComposite, SWT.NONE);
bottomTextLabel.setText(bottomText);
final GridData bottomTextGD = new GridData(GridData.END);
bottomTextGD.grabExcessHorizontalSpace = true;
bottomTextGD.horizontalSpan = 2;
bottomTextLabel.setLayoutData(bottomTextGD);
} // if
// reset the minimum width and height so children can be seen - method 2
// TODO why does the following not make the scrollbar show up at the start ??
innerSize = innerComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT);
innerComposite.setSize(innerSize);
innerComposite.layout();
// scrolledComposite.setSize(innerSize);
// innerSize.y*=3;
// innerSize.y/=2;
scrolledComposite.setMinSize(innerSize);
PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, "org.eclipse.stem.doc.newmodifier_contextid");
return outerComposite;
} // createSpecificComposite
/**
* @see org.eclipse.stem.ui.wizards.NewIdentifiableWizard.NewIdentifiablePage#getDCDescription()
*/
@Override
protected String getDCDescription() {
return MessageFormat.format(Messages
.getString("NModifierWiz.DC_DESCRIPTION"),
new Object[] { serializationFileNameField.getText() });
} // getDCDescription
/**
* @param feature
* a {@link EStructuralFeature} of an {@link Identifiable}.
* @return <code>true</code> if the {@link EStructuralFeature} represents
* a property that is specified by a user, <code>false</code>
* otherwise.
*/
protected boolean isUserSpecifiedProperty(final EStructuralFeature feature) {
boolean retValue = !feature.getEContainingClass().equals(
CommonPackage.Literals.DUBLIN_CORE);
retValue = retValue && !(feature instanceof EReference);
return retValue;
} // isUserSpecifiedProperty
/**
* @return a line of text to appear below the properties, or
* <code>null</code> if no text is to appear.
*/
protected String getBottomText() {
return null;
}
/**
* @param feature
* the {@link EStructuralFeature} to test for modifiability
* @return <code>true</code> (Default) if the feature represents a property
* that the user should be able to modify for an
* {@link org.eclipse.stem.core.experiment.Experiment} or a
* {@link org.eclipse.stem.core.trigger.Trigger}.
*/
protected boolean isModifiableProperty(final EStructuralFeature feature) {
boolean retValue = false;
retValue |= (feature.getEType().equals(EcorePackage.Literals.EINT));
retValue |= (feature.getEType().equals(EcorePackage.Literals.ELONG));
retValue |= (feature.getEType().equals(EcorePackage.Literals.EDOUBLE));
return retValue;
} // isModifiableProperty
/**
* @return the {@link FeatureModifier}s on the page that the user has
* specified.
*/
public final List<FeatureModifier> getFeatureModifiers() {
final List<FeatureModifier> retValue = new ArrayList<FeatureModifier>();
for (final FeatureModifierComposite featureModifierComposite : featureModifierComposites) {
final FeatureModifier featureModifier = featureModifierComposite
.getFeatureModifier();
// Is this FeatureModifier something other than a NOP?
if (!(featureModifier instanceof NOPModifier)) {
// Yes
featureModifier.setTarget(target);
retValue.add(featureModifier);
} // if
} // for each FeatureModifierComposite
return retValue;
} // getFeatureModifiers
/**
* see org.eclipse.stem.ui.wizards.NewIdentifiablePage#validatePage()
*/
@Override
public boolean validatePage() {
return super.validatePage();
} // validatePage
/**
* This class is a {@link Composite} that displays the details of a
* {@link FeatureModifier}.
*/
protected static class FeatureModifierComposite extends Composite {
/**
* This is a place holder for the current
* {@link FeatureModifierEditComposite} to which a reference is
* maintained by {@link #editComposite}. When the user selects one of
* the radio buttons ({@link #noneButton}, {@link #rangeButton}, or
* {@link #sequenceButton}) the child of this {@link Composite} is
* changed by {@link #setEditComposite(Composite)}.
*/
final Composite editCompositeHolder;
/**
* This is the current {@link FeatureModifierEditComposite} being
* displayed to the user and is the child of
* {@link #editCompositeHolder}.
*/
private FeatureModifierEditComposite editComposite;
/**
* This is the {@link FeatureModifier}, if it exists, created by the
* user for the {@link EStructuralFeature} referenced by
* {@link #feature}.
*/
FeatureModifier featureModifier = null;
/**
* These buttons are grouped together as "radio buttons" and allow the
* user to specify which type of {@link FeatureModifier} to create.
* Initially, {@link #noneButton} is selected and the
* {@link FeatureModifierEditComposite} returned by
* {@link #createDefaultEditComposite(Composite, NewModifierPage, Identifiable, EStructuralFeature)}
* is displayed, which simply shows the current value of the feature.
*/
Button noneButton = null;
Button singleButton = null;
Button rangeButton = null;
Button sequenceButton = null;
/**
* @param parent
* the parent {@link Composite}
* @param target
* the {@link EObject} that contains the
* {@link EStructuralFeature}
* @param feature
* the feature to be modified
* @param psp
* the provider of NLS'd strings to label the properties of
* the {@link Identifiable}
* @param descriptor
* @param modifiable
* if <code>true</code> then the {@link EStructuralFeature}
* can be modified.
* @param parentNewModifierPage
* the {@link NewModifierPage} that contains the parent
* {@link Composite}.
*/
public FeatureModifierComposite(final Composite parent,
final EObject target,
final EStructuralFeature feature,
final PropertyStringProvider psp,
final IItemPropertyDescriptor descriptor,
final boolean modifiable,
final NewModifierPage parentNewModifierPage) {
super(parent, SWT.NONE);
final FormLayout layout = new FormLayout();
layout.spacing = 2;
setLayout(layout);
// Edit Composite Holder
editCompositeHolder = new Composite(this, SWT.NONE);
final FillLayout fillLayout = new FillLayout();
fillLayout.marginHeight = 0;
fillLayout.marginWidth = 0;
editCompositeHolder.setLayout(fillLayout);
editCompositeHolder.setToolTipText(psp
.getPropertyToolTip(descriptor));
// Units Label
final Label unitLabel = new Label(this, SWT.NONE);
unitLabel.setText(psp.getPropertyUnits(descriptor));
// Radio button group
final Group group = new Group(this, SWT.SHADOW_ETCHED_IN);
group.setText("Select Modifier Type");
group.setLayout(new RowLayout(SWT.HORIZONTAL));
group.setEnabled(modifiable);
noneButton = new Button(group, SWT.RADIO);
noneButton.setText("None");
noneButton.setEnabled(modifiable);
// range button
singleButton = new Button(group, SWT.RADIO);
singleButton.setText(Messages.getString("Single.type"));
singleButton.setEnabled(modifiable);
// range button
rangeButton = new Button(group, SWT.RADIO);
rangeButton.setText(Messages.getString("Range.type"));
rangeButton.setEnabled(modifiable);
// sequence button
sequenceButton = new Button(group, SWT.RADIO);
sequenceButton.setText(Messages.getString("Sequence.type"));
sequenceButton.setEnabled(modifiable);
final int offset = 10;
// The left of the editCompositeHolder is attached to the left of
// the parent composite
FormData formData = new FormData();
formData.left = new FormAttachment(0, 0);
formData.top = new FormAttachment(0, offset);
editCompositeHolder.setLayoutData(formData);
// left->holder.right
// right->group
formData = new FormData();
formData.left = new FormAttachment(editCompositeHolder, 0,
SWT.RIGHT);
formData.right = new FormAttachment(group, 0, SWT.LEFT);
formData.bottom = new FormAttachment(editCompositeHolder, -3,
SWT.BOTTOM);
unitLabel.setLayoutData(formData);
// The right of the radio button composite is attached to the right
// of the parent composite.
formData = new FormData();
formData.right = new FormAttachment(100, 0);
group.setLayoutData(formData);
// This call takes the feature and adapts it to a modifier that does
// nothing. When we call setFeatureModifier that modifier is adapted
// to a FeatureModifierEditComposite (and assigned to
// editComposite). That composite doesn't allow editing, but,
// instead, displays a "default" representation of the feature's
// value. For a String, it would just display the value of the
// String, for a number it just displays a string representing the
// value of the number
setFeatureModifier(EStructuralFeatureFeatureModifierAdapterFactory.INSTANCE
.adapt(feature, NOPModifier.class));
switchToFeatureModifier(target, parentNewModifierPage);
noneButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(
@SuppressWarnings("unused") final SelectionEvent e) {
// Put up the default?
if (noneButton.getSelection()
&& !(featureModifier instanceof NOPModifier)) {
// Yes
final FeatureModifier fm = EStructuralFeatureFeatureModifierAdapterFactory.INSTANCE
.adapt(feature, NOPModifier.class);
fm.setTarget(feature);
setFeatureModifier(fm);
switchToFeatureModifier(target,
parentNewModifierPage);
} // if
}
});
singleButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(
@SuppressWarnings("unused") final SelectionEvent e) {
// Put up the single value modifier?
if (singleButton.getSelection()
&& !(featureModifier instanceof SingleValueModifier)) {
// Yes
setFeatureModifier(EStructuralFeatureFeatureModifierAdapterFactory.INSTANCE
.adapt(feature, SingleValueModifier.class));
switchToFeatureModifier(target,
parentNewModifierPage);
// selectRangeButton();
} // if
}
});
rangeButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(
@SuppressWarnings("unused") final SelectionEvent e) {
// Put up the range modifier?
if (rangeButton.getSelection()
&& !(featureModifier instanceof RangeModifier)) {
// Yes
setFeatureModifier(EStructuralFeatureFeatureModifierAdapterFactory.INSTANCE
.adapt(feature, RangeModifier.class));
switchToFeatureModifier(target,
parentNewModifierPage);
// selectRangeButton();
} // if
}
});
sequenceButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(
@SuppressWarnings("unused") final SelectionEvent e) {
// Put up the sequence modifier?
if (sequenceButton.getSelection()
&& !(featureModifier instanceof SequenceModifier)) {
// Yes
setFeatureModifier(EStructuralFeatureFeatureModifierAdapterFactory.INSTANCE
.adapt(feature, SequenceModifier.class));
switchToFeatureModifier(target,
parentNewModifierPage);
// selectSequenceButton();
} // if
}
});
// The Feature Modifier Composite must have
// fixed size to put these in a scroll pane
setSize(500,150);
} // FeatureModifierComposite
void selectNoneButton() {
noneButton.setSelection(true);
singleButton.setSelection(false);
rangeButton.setSelection(false);
sequenceButton.setSelection(false);
} // selectNoneButton
FeatureModifierEditComposite createDefaultEditComposite(
final Composite parent,
final NewModifierPage parentNewModifierPage,
final Identifiable identifiable,
final EStructuralFeature feature) {
final FeatureModifierEditComposite retValue = new FeatureModifierEditComposite(
parent, parentNewModifierPage, null) {
@Override
protected boolean validate() {
return true;
}
};
retValue.setLayout(new GridLayout());
final Label label = new Label(retValue, SWT.BORDER | SWT.TRAIL);
label.setText(identifiable.eGet(feature).toString());
final GridData gridData = new GridData();
gridData.horizontalAlignment = SWT.END;
label.setLayoutData(gridData);
return retValue;
} // createDefaultEditComposite
void switchToFeatureModifier(final EObject target,
final NewModifierPage parentNewModifierPage) {
// The target of the featureModifier needs to be adjusted here. It
// would typically be identifiable, but if identifiable is an
// instance of a Label then the
// features of the value of the current value feature of the
// identifiable have been included as features. The problem is that
// when that happens the target of the FeatureModifier needs to be
// the label value and not the identifiable.
// A label
if (target instanceof org.eclipse.stem.core.graph.Label
&& (target.eClass() != featureModifier
.getEStructuralFeature().getEContainingClass())) {
// Yes
final org.eclipse.stem.core.graph.Label label = (org.eclipse.stem.core.graph.Label) target;
final LabelValue lv = (LabelValue) label
.eGet(GraphPackage.Literals.LABEL__CURRENT_VALUE);
featureModifier.setTarget(lv);
} // if static label
else {
featureModifier.setTarget(target);
}
final FeatureModifierEditCompositeAdapter fmecAdapter = (FeatureModifierEditCompositeAdapter) FeatureModifierFeatureModifierEditCompositeAdapterFactory.INSTANCE
.adapt(featureModifier,
FeatureModifierEditCompositeAdapter.class);
fmecAdapter.setTarget(featureModifier);
setEditComposite(fmecAdapter.createEditComposite(
editCompositeHolder, target, parentNewModifierPage));
fmecAdapter.setSelectedButton(noneButton, singleButton, rangeButton,
sequenceButton);
} // switchToFeatureModifier
void setEditComposite(final FeatureModifierEditComposite editComposite) {
// Get rid of the old one?
if (this.editComposite != null) {
// Yes
this.editComposite.dispose();
} // if
this.editComposite = editComposite;
getShell().pack();
// getShell().layout();
getShell().layout(true, true);
} // setEditComposite
/**
* @return the {@link FeatureModifier}, possibly <code>null</code>
*/
public final FeatureModifier getFeatureModifier() {
return featureModifier;
}
/**
* @param featureModifier
* the {@link FeatureModifier} to set
*/
protected final void setFeatureModifier(
final FeatureModifier featureModifier) {
this.featureModifier = featureModifier;
}
} // FeatureModifierComposite
/**
* This class is a {@link Composite} that allows a user to specify the
* details of a {@link FeatureModifier}.
*/
abstract public static class FeatureModifierEditComposite extends Composite {
/**
* This is the {@link FeatureModifier} that this {@link Composite} is
* editing.
*/
protected FeatureModifier featureModifier = null;
/**
* This is the {@link NewModifierPage} that is the ultimate parent of
* the parent {@link Composite}
*/
protected NewModifierPage parentNewModifierPage;
protected ModifyListener modifyListener = new ModifyListener() {
public void modifyText(
@SuppressWarnings("unused") final ModifyEvent e) {
parentNewModifierPage.setPageComplete(validate());
}
};
/**
* @param parent
* the parent {@link Composite}
* @param parentNewModifierPage
* the ultimate parent {@link NewModifierPage} of the parent
* {@link Composite}
* @param featureModifier
* the {@link FeatureModifier} being edited.
*/
public FeatureModifierEditComposite(final Composite parent,
final NewModifierPage parentNewModifierPage,
final FeatureModifier featureModifier) {
super(parent, SWT.NONE);
this.parentNewModifierPage = parentNewModifierPage;
this.featureModifier = featureModifier;
} // FeatureModifierEditComposite
/**
* @return the {@link FeatureModifier}
*/
public final FeatureModifier getFeatureModifier() {
return featureModifier;
}
/**
* @return the modifyListener
*/
public final ModifyListener getModifyListener() {
return modifyListener;
}
/**
* As a side-effect this method sets the error message of the
* {@link #parentNewModifierPage} if it returns <code>false</code>.
*
* @return <code>true</code> if the
* {@link FeatureModifierEditComposite} is valid.
*/
abstract protected boolean validate();
} // FeatureModifierEditComposite
} // NewModifierPage