/*******************************************************************************
* This file is protected by Copyright.
* Please refer to the COPYRIGHT file distributed with this source distribution.
*
* This file is part of REDHAWK IDE.
*
* 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
*******************************************************************************/
package gov.redhawk.ide.graphiti.ui.properties;
import java.io.IOException;
import java.net.URL;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CompoundCommand;
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.EReference;
import org.eclipse.emf.ecore.util.EContentAdapter;
import org.eclipse.emf.edit.command.AddCommand;
import org.eclipse.emf.edit.command.DeleteCommand;
import org.eclipse.emf.edit.command.RemoveCommand;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.eclipse.graphiti.ui.platform.GraphitiShapeEditPart;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
import gov.redhawk.core.graphiti.ui.properties.AbstractPropertiesSection;
import gov.redhawk.ide.graphiti.ui.GraphitiUIPlugin;
import mil.jpeojtrs.sca.dcd.DcdPackage;
import mil.jpeojtrs.sca.partitioning.ComponentInstantiation;
import mil.jpeojtrs.sca.partitioning.PartitioningFactory;
import mil.jpeojtrs.sca.partitioning.PartitioningPackage;
import mil.jpeojtrs.sca.partitioning.Requirements;
import mil.jpeojtrs.sca.partitioning.Requires;
import mil.jpeojtrs.sca.sad.SadComponentInstantiation;
import mil.jpeojtrs.sca.sad.SadPackage;
/**
* An abstract implementation of component instantiations requirements property section.
* This section displays either the device_requires or deployer_requires values of the selection.
* Add/remove buttons handle adding/removing requires elements from the selection.
*/
public abstract class AbstractRequirementsPropertySection extends AbstractPropertiesSection {
private RequirementsPropertyComposite sectionComposite;
private Button addButton;
private Button removeButton;
/** Maintain reference to the active component instantiation so we can remove our content adapter when necessary **/
private ComponentInstantiation activeComp;
/** Flag that can be set so that GUI driven changes don't create a loop with our adapter **/
private boolean userEdit = false;
/**
* Get either the component or device {@link Requirements} model element, depending on context
* @return
*/
protected abstract Requirements getSelectionRequirements();
private EContentAdapter contentAdapter = new EContentAdapter() {
@Override
public void notifyChanged(Notification notification) {
super.notifyChanged(notification);
if (userEdit) {
return;
}
if (!getTreeViewer().getTree().isDisposed()) {
getTreeViewer().setInput(getEObject());
}
}
@Override
protected void addAdapter(Notifier notifier) {
if (notifier instanceof Requirements || notifier instanceof Requires) {
super.addAdapter(notifier);
}
}
};
@Override
public void createControls(Composite parent, final TabbedPropertySheetPage tabbedPropertySheetPage) {
super.createControls(parent, tabbedPropertySheetPage);
int treeStyle = getWidgetFactory().getOrientation() | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.SINGLE | SWT.FULL_SELECTION;
parent.setLayout(new GridLayout(2, false));
parent.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
// Composite containing buttons used to add/remove requires elements
createButtonComposite(parent);
// Composite containing the requirements table
sectionComposite = new RequirementsPropertyComposite(parent, SWT.None, treeStyle);
// Add a selection change listener to handle button enablement
getTreeViewer().addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
updateControls();
}
});
}
/**
* Create the composite that will contain buttons for adding/removing {@link Requires} elements
*/
private void createButtonComposite(Composite parent) {
Composite actionComposite = new Composite(parent, SWT.NONE);
actionComposite.setLayout(new GridLayout());
actionComposite.setLayoutData(GridDataFactory.swtDefaults().align(SWT.CENTER, SWT.BEGINNING).create());
actionComposite.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
Image addImage = null;
Image removeImage = null;
try {
URL url = FileLocator.find(GraphitiUIPlugin.getDefault().getBundle(), new Path("$nl$/icons/obj16/add.gif"), null);
addImage = new Image(Display.getCurrent(), url.openStream());
url = FileLocator.find(GraphitiUIPlugin.getDefault().getBundle(), new Path("$nl$/icons/obj16/remove.gif"), null);
removeImage = new Image(Display.getCurrent(), url.openStream());
} catch (IOException e) {
// PASS
}
addButton = new Button(actionComposite, SWT.PUSH);
addButton.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
addButton.setToolTipText("Add Requires");
addButton.setImage(addImage);
removeButton = new Button(actionComposite, SWT.PUSH);
removeButton.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
removeButton.setToolTipText("Remove Requires");
removeButton.setImage(removeImage);
// Add button selection listeners
addButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
Command command = createAddCommand();
userEdit = true;
getEditingDomain().getCommandStack().execute(command);
userEdit = false;
getTreeViewer().refresh();
// Automatically select the most recently added item
int size = getTreeViewer().getTree().getItemCount();
getTreeViewer().getTree().setSelection(getTreeViewer().getTree().getItem(size - 1));
updateControls();
}
});
removeButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
Command command = createRemoveCommand();
// Table only allows a single selection, so safe to assume the item we want is at index[0]
int selIndex = getTreeViewer().getTree().indexOf(getTreeViewer().getTree().getSelection()[0]);
userEdit = true;
getEditingDomain().getCommandStack().execute(command);
userEdit = false;
getTreeViewer().refresh();
// Update the selection to be the preceding item if it exists, otherwise select the top item
if (selIndex > 0) {
TreeItem item = getTreeViewer().getTree().getItem(selIndex - 1);
getTreeViewer().getTree().select(item);
} else if (getSelectionRequirements() != null) {
TreeItem item = getTreeViewer().getTree().getItem(0);
getTreeViewer().getTree().select(item);
}
updateControls();
}
});
}
/**
* Add a new {@link Requires} element to the component instantiation
*/
private Command createAddCommand() {
CompoundCommand command = new CompoundCommand();
Requirements requirements = getSelectionRequirements();
// Need to create a requirements section if it doesn't exist
if (requirements == null) {
requirements = PartitioningFactory.eINSTANCE.createRequirements();
EReference eRef = null;
if (getEObject() instanceof SadComponentInstantiation) {
eRef = SadPackage.Literals.SAD_COMPONENT_INSTANTIATION__DEVICE_REQUIRES;
} else {
eRef = DcdPackage.Literals.DCD_COMPONENT_INSTANTIATION__DEPLOYER_REQUIRES;
}
Command addRequirementsCommand = SetCommand.create(getEditingDomain(), getEObject(), eRef, requirements);
command.append(addRequirementsCommand);
}
Requires newRequires = PartitioningFactory.eINSTANCE.createRequires();
newRequires.setId("<Enter ID>");
newRequires.setValue("<Enter Value>");
Command addRequiresCommand = AddCommand.create(getEditingDomain(), requirements, PartitioningPackage.Literals.REQUIREMENTS__REQUIRES, newRequires);
command.append(addRequiresCommand);
return command;
}
/**
* Remove a {@link Requires} element from the component instantiation. Delete the {@link Requirements} element if it
* is now empty.
*/
private Command createRemoveCommand() {
Requirements requirements = getSelectionRequirements();
// If the last element is removed, the requirements section also needs to be removed
if (requirements.getRequires().size() <= 1) {
return DeleteCommand.create(getEditingDomain(), requirements);
}
Requires requires = (Requires) ((StructuredSelection) getTreeViewer().getSelection()).getFirstElement();
return RemoveCommand.create(getEditingDomain(), requirements, PartitioningPackage.Literals.REQUIREMENTS__REQUIRES, requires);
}
/**
* Currently just controls whether or not the remove button is enabled.
* Can be expanded in the future as necessary.
*/
private void updateControls() {
if (getSelectionRequirements() == null || getTreeViewer().getSelection().isEmpty()) {
removeButton.setEnabled(false);
} else if (!removeButton.isEnabled()) {
removeButton.setEnabled(true);
}
}
public TreeViewer getTreeViewer() {
return sectionComposite.getTreeViewer();
}
@Override
public void setInput(IWorkbenchPart part, ISelection selection) {
if (activeComp != null && activeComp != getEObject()) {
contentAdapter.unsetTarget(activeComp);
activeComp.eAdapters().remove(contentAdapter);
}
super.setInput(part, selection);
// Update button state
updateControls();
if (activeComp != getEObject()) {
activeComp = (ComponentInstantiation) getEObject();
contentAdapter.setTarget(activeComp);
activeComp.eAdapters().add(contentAdapter);
}
}
@Override
public boolean shouldUseExtraSpace() {
return true;
}
@Override
public void dispose() {
if (activeComp != null) {
contentAdapter.unsetTarget(activeComp);
activeComp.eAdapters().remove(contentAdapter);
}
super.dispose();
}
@Override
protected EObject unwrap(Object object) {
if (object instanceof GraphitiShapeEditPart) {
object = ((GraphitiShapeEditPart) object).getModel();
}
if (object instanceof PictogramElement) {
return ((PictogramElement) object).getLink().getBusinessObjects().get(0);
}
return super.unwrap(object);
}
}