/******************************************************************************* | |
* <copyright> | |
* | |
* Copyright (c) 2013, 2013 SAP AG. | |
* 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: | |
* SAP AG - initial API, implementation and documentation | |
* | |
* </copyright> | |
* | |
*******************************************************************************/ | |
package org.eclipse.fmc.blockdiagram.editor.algorithm.node; | |
import java.util.ArrayList; | |
import java.util.HashSet; | |
import java.util.List; | |
import org.eclipse.emf.common.util.BasicEList; | |
import org.eclipse.emf.common.util.EList; | |
import org.eclipse.fmc.blockdiagram.editor.util.FMCUtil; | |
import org.eclipse.graphiti.features.IFeatureProvider; | |
import org.eclipse.graphiti.mm.algorithms.Ellipse; | |
import org.eclipse.graphiti.mm.algorithms.GraphicsAlgorithm; | |
import org.eclipse.graphiti.mm.algorithms.MultiText; | |
import org.eclipse.graphiti.mm.pictograms.Anchor; | |
import org.eclipse.graphiti.mm.pictograms.BoxRelativeAnchor; | |
import org.eclipse.graphiti.mm.pictograms.CompositeConnection; | |
import org.eclipse.graphiti.mm.pictograms.Connection; | |
import org.eclipse.graphiti.mm.pictograms.ConnectionDecorator; | |
import org.eclipse.graphiti.mm.pictograms.ContainerShape; | |
import org.eclipse.graphiti.mm.pictograms.CurvedConnection; | |
import org.eclipse.graphiti.mm.pictograms.Diagram; | |
import org.eclipse.graphiti.mm.pictograms.Shape; | |
import org.eclipse.graphiti.services.Graphiti; | |
import org.eclipse.graphiti.services.IGaService; | |
import org.eclipse.graphiti.services.IPeService; | |
/** | |
* This abstract class implements methods for nodes with anchors and | |
* multiplicity property. The node can be named and may contain nested nodes. | |
* | |
* @author Benjamin Schmeling | |
* @author Heiko Witteborg | |
* | |
*/ | |
public abstract class AbstractNode implements AnchoredNode, MultipleNode, | |
NamedNode, ResizableNode, ContainerNode { | |
protected IGaService ga = Graphiti.getGaService(); | |
protected IPeService pe = Graphiti.getPeService(); | |
protected int textMargin = DEFAULT_MARGIN; | |
// ---------------------------------------- | |
// ----------- Anchored Node -------------- | |
@Override | |
/* | |
* (non-Javadoc) | |
* | |
* @see | |
* org.eclipse.fmc.blockdiagram.editor.node.AnchoredNode#setBoxAnchorsVisible(org | |
* .eclipse.graphiti.mm.pictograms.Shape, boolean) | |
*/ | |
public void setBoxAnchorsVisible(Shape shape, boolean visible) { | |
for (Anchor anchor : shape.getAnchors()) { | |
if (anchor instanceof BoxRelativeAnchor) { | |
anchor.getGraphicsAlgorithm().setFilled(visible); | |
anchor.getGraphicsAlgorithm().setLineVisible(visible); | |
} | |
} | |
} | |
@Override | |
/* | |
* (non-Javadoc) | |
* | |
* @see | |
* org.eclipse.fmc.blockdiagram.editor.node.AnchoredNode#isBoxAnchorsVisible(org | |
* .eclipse.graphiti.mm.pictograms.Shape) | |
*/ | |
public boolean isBoxAnchorsVisible(Shape shape) { | |
for (Anchor anchor : shape.getAnchors()) { | |
if (anchor instanceof BoxRelativeAnchor) { | |
return anchor.getGraphicsAlgorithm().getLineVisible(); | |
} | |
} | |
return false; | |
} | |
@Override | |
/* | |
* (non-Javadoc) | |
* | |
* @see | |
* org.eclipse.fmc.blockdiagram.editor.node.AnchoredNode#resizeBoxAnchorSet(org. | |
* eclipse.graphiti.mm.pictograms.ContainerShape, int, int, int, int) | |
*/ | |
public void resizeBoxAnchorSet(ContainerShape container, int x, int y, | |
int width, int height) { | |
createBoxAnchorSet(container); | |
} | |
@Override | |
/* | |
* (non-Javadoc) | |
* | |
* @see | |
* org.eclipse.fmc.blockdiagram.editor.node.AnchoredNode#createBoxAnchorSet(org. | |
* eclipse.graphiti.mm.pictograms.ContainerShape) | |
*/ | |
public void createBoxAnchorSet(Shape shape) { | |
boolean isAnchorsVisible = isBoxAnchorsVisible(shape); | |
FMCUtil.removeObsoleteAnchors(shape); | |
addBoxAnchorSet(shape); | |
this.setBoxAnchorsVisible(shape, isAnchorsVisible); | |
// removeInnerUnusedAnchors(container); | |
} | |
@Override | |
/* | |
* (non-Javadoc) | |
* | |
* @see | |
* org.eclipse.fmc.blockdiagram.editor.node.AnchoredNode#removeUnusedBoxAnchors( | |
* org.eclipse.graphiti.mm.pictograms.Shape) | |
*/ | |
public void removeUnusedBoxAnchors(Shape shape) { | |
FMCUtil.removeObsoleteAnchors(shape); | |
} | |
@Override | |
/* | |
* (non-Javadoc) | |
* | |
* @see | |
* org.eclipse.fmc.blockdiagram.editor.node.AnchoredNode#hasUnusedAnchors(org.eclipse | |
* .graphiti.mm.pictograms.Shape) | |
*/ | |
public boolean hasUnusedAnchors(Shape shape) { | |
for (Anchor anchor : new HashSet<Anchor>(shape.getAnchors())) { | |
if (anchor instanceof BoxRelativeAnchor) { | |
if (anchor.getIncomingConnections().isEmpty() | |
&& anchor.getOutgoingConnections().isEmpty()) { | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
protected abstract void addBoxAnchorSet(Shape container); | |
public BoxRelativeAnchor createBoxAnchor(Shape shape, double rw, double rh, | |
int w, int h) { | |
BoxRelativeAnchor boxRelativeAnchor = pe.createBoxRelativeAnchor(shape); | |
// boxRelativeAnchor.setActive(true); | |
boxRelativeAnchor.setRelativeWidth(rw); | |
boxRelativeAnchor.setRelativeHeight(rh); | |
boxRelativeAnchor.setReferencedGraphicsAlgorithm(shape | |
.getGraphicsAlgorithm()); | |
final Ellipse ellipse = ga.createEllipse(boxRelativeAnchor); | |
ellipse.setBackground(ga.manageColor( | |
pe.getDiagramForPictogramElement(shape), 195, 195, 240)); | |
ellipse.setTransparency(0.3); | |
ellipse.setLineVisible(false); | |
ellipse.setFilled(false); | |
ga.setLocationAndSize(ellipse, 0, 0, w, h); | |
return boxRelativeAnchor; | |
} | |
// ---------------------------------------- | |
// ----------- Multiple Node -------------- | |
@Override | |
/* | |
* (non-Javadoc) | |
* | |
* @see org.eclipse.fmc.blockdiagram.editor.graphicsalgorithms.MultipleBox# | |
* setMultipleInstances(org.eclipse.graphiti.mm.pictograms.ContainerShape, | |
* org.eclipse.graphiti.features.IFeatureProvider, boolean) | |
*/ | |
public void setMultipleInstances(ContainerShape container, | |
IFeatureProvider provider, boolean multipleInstances) { | |
if (multipleInstances) { | |
GraphicsAlgorithm mainShapeGa = container.getGraphicsAlgorithm() | |
.getGraphicsAlgorithmChildren().get(0); | |
// Resize main shape | |
this.resize(mainShapeGa, mainShapeGa.getX() + MULTI_INSTANCE_GAP, | |
mainShapeGa.getY(), mainShapeGa.getWidth() | |
- MULTI_INSTANCE_GAP, mainShapeGa.getHeight() | |
- MULTI_INSTANCE_GAP); | |
// Create background shape | |
Diagram diagram = Graphiti.getPeService() | |
.getDiagramForPictogramElement(container); | |
GraphicsAlgorithm bgShapeGa = createGraphics(diagram, | |
container.getGraphicsAlgorithm(), 0, MULTI_INSTANCE_GAP, | |
mainShapeGa.getWidth(), mainShapeGa.getHeight()); | |
bgShapeGa.setBackground(mainShapeGa.getBackground()); | |
bgShapeGa.setForeground(mainShapeGa.getForeground()); | |
bgShapeGa.setTransparency(mainShapeGa.getTransparency()); | |
bgShapeGa.setStyle(mainShapeGa.getStyle()); | |
// Synchronize polygons (e.g. rotation, flip) | |
synchronizeMultiInstanceChildren(container); | |
// Adapt first level container (relative anchors + invisible shape) | |
createBoxAnchorSet(container); | |
synchronizeFirstLevelShape(container); | |
// Adapt position of text field | |
relocateText(mainShapeGa); | |
} else { | |
GraphicsAlgorithm backgroundGa = getMultiInstanceChild(container, | |
false); | |
GraphicsAlgorithm foregroundGa = getMultiInstanceChild(container, | |
true); | |
// Remove background shape | |
container.getGraphicsAlgorithm().getGraphicsAlgorithmChildren() | |
.remove(backgroundGa); | |
// Resize main shape | |
this.resize(foregroundGa, foregroundGa.getX() - MULTI_INSTANCE_GAP, | |
foregroundGa.getY(), foregroundGa.getWidth() | |
+ MULTI_INSTANCE_GAP, foregroundGa.getHeight() | |
+ MULTI_INSTANCE_GAP); | |
// Adapt position of text field | |
relocateText(foregroundGa); | |
// Adapt first level container (relative anchors + invisible shape) | |
createBoxAnchorSet(container); | |
synchronizeFirstLevelShape(container); | |
} | |
} | |
/** | |
* Synchronize invisible shape of the first level container (do nothing by | |
* default). | |
* | |
* @param firstLevelContainer | |
* the ContainerShape of the node. | |
*/ | |
protected void synchronizeFirstLevelShape(ContainerShape firstLevelContainer) { | |
// synchronize invisible shape of the first level container (do nothing | |
// by default) | |
} | |
@Override | |
/* | |
* (non-Javadoc) | |
* | |
* @see org.eclipse.fmc.blockdiagram.editor.node.MultipleNode# | |
* synchronizeMultiInstanceChildren | |
* (org.eclipse.graphiti.mm.pictograms.ContainerShape) | |
*/ | |
public void synchronizeMultiInstanceChildren( | |
ContainerShape firstLevelContainer) { | |
// synchronize foreground and background ga (do nothing by default) | |
} | |
@Override | |
/* | |
* (non-Javadoc) | |
* | |
* @see | |
* org.eclipse.fmc.blockdiagram.editor.node.MultipleNode#getMultiInstanceChild(org | |
* .eclipse.graphiti.mm.pictograms.ContainerShape, boolean) | |
*/ | |
public GraphicsAlgorithm getMultiInstanceChild(ContainerShape container, | |
boolean main) { | |
GraphicsAlgorithm invisibleGa = container.getGraphicsAlgorithm(); | |
if (invisibleGa.getGraphicsAlgorithmChildren().size() > 1) { | |
return invisibleGa.getGraphicsAlgorithmChildren().get(main ? 0 : 1); | |
} | |
return null; | |
} | |
@Override | |
/* | |
* (non-Javadoc) | |
* | |
* @see org.eclipse.fmc.blockdiagram.editor.graphicsalgorithms.MultipleBox# | |
* isMultipleInstances(org.eclipse.graphiti.mm.pictograms.ContainerShape) | |
*/ | |
public boolean isMultipleInstances(ContainerShape container) { | |
return getMultiInstanceChild(container, false) != null; | |
} | |
// ---------------------------------------- | |
// ----------- Container Node ------------- | |
@Override | |
/* | |
* (non-Javadoc) | |
* | |
* @see | |
* org.eclipse.fmc.blockdiagram.editor.node.ContainerNode#getContainerShape(org. | |
* eclipse.graphiti.mm.algorithms.GraphicsAlgorithm) | |
*/ | |
public ContainerShape getContainerShape(GraphicsAlgorithm ga) { | |
if (ga.getPictogramElement() != null) | |
return (ContainerShape) ga.getPictogramElement(); | |
return this.getContainerShape(ga.getParentGraphicsAlgorithm()); | |
} | |
@Override | |
/* | |
* (non-Javadoc) | |
* | |
* @see | |
* org.eclipse.fmc.blockdiagram.editor.node.ContainerNode#isEmpty(org.eclipse.graphiti | |
* .mm.pictograms.ContainerShape) | |
*/ | |
public boolean isEmpty(ContainerShape cs) { | |
return this.getContainedShapes(cs).isEmpty(); | |
} | |
@Override | |
/* | |
* (non-Javadoc) | |
* | |
* @see | |
* org.eclipse.fmc.blockdiagram.editor.node.ContainerNode#isHiding(org.eclipse.graphiti | |
* .mm.pictograms.ContainerShape) | |
*/ | |
public boolean isHiding(ContainerShape cs) { | |
for (Shape child : this.getContainedShapes(cs)) { | |
if (child.isVisible()) | |
return false; | |
} | |
return true; | |
} | |
@Override | |
/* | |
* (non-Javadoc) | |
* | |
* @see | |
* org.eclipse.fmc.blockdiagram.editor.node.ContainerNode#getContainedShapes(org | |
* .eclipse.graphiti.mm.pictograms.ContainerShape) | |
*/ | |
public EList<Shape> getContainedShapes(ContainerShape cs) { | |
EList<Shape> results = new BasicEList<Shape>(); | |
for (Shape shape : cs.getChildren()) { | |
if (!(shape.getGraphicsAlgorithm() instanceof MultiText)) | |
results.add(shape); | |
} | |
return results; | |
} | |
@Override | |
/* | |
* (non-Javadoc) | |
* | |
* @see | |
* org.eclipse.fmc.blockdiagram.editor.node.ContainerNode#hideContainedShapes(org | |
* .eclipse.graphiti.mm.pictograms.ContainerShape, boolean) | |
*/ | |
public void hideContainedShapes(ContainerShape cs, boolean hideChildren) { | |
for (Shape child : this.getContainedShapes(cs)) { | |
child.setVisible(!hideChildren); | |
this.hideContainedConnections(child, hideChildren); | |
} | |
// Resize node | |
// final ContainerShape container = cs; | |
// final GraphicsAlgorithm containerGa = | |
// container.getGraphicsAlgorithm(); | |
// final int adaptedWidth = this.getContainedWidth(cs); | |
// final int adaptedHeight = this.getContainedHeight(cs); | |
// final IFeatureProvider fp = | |
// Util.getActiveEditor().getDiagramTypeProvider().getFeatureProvider(); | |
// final TransactionalEditingDomain editingDomain = | |
// this.getEditingDomain(); | |
// | |
// editingDomain.getCommandStack().execute( | |
// new RecordingCommand(editingDomain) { | |
// @Override | |
// protected void doExecute() { | |
// if(isWriteTransaction(editingDomain)){ | |
// ResizeShapeContext ctx = new ResizeShapeContext(container); | |
// ctx.setX(containerGa.getX()); | |
// ctx.setY(containerGa.getY()); | |
// ctx.setWidth(adaptedWidth); | |
// ctx.setHeight(adaptedHeight); | |
// IResizeShapeFeature resizeShapeFeature = | |
// fp.getResizeShapeFeature(ctx); | |
// if(resizeShapeFeature.canExecute(ctx)) | |
// resizeShapeFeature.execute(ctx); | |
// } | |
// } | |
// } | |
// ); | |
} | |
private void hideContainedConnections(Shape s, boolean hideChildren) { | |
for (Anchor anchor : s.getAnchors()) { | |
List<Connection> connections = new ArrayList<Connection>(); | |
connections.addAll(anchor.getIncomingConnections()); | |
connections.addAll(anchor.getOutgoingConnections()); | |
for (Connection con : connections) { | |
// unhide connection only if both start and end shape is visible | |
if (hideChildren | |
|| (con.getStart().getParent().isVisible() && con | |
.getEnd().getParent().isVisible())) { | |
con.setVisible(!hideChildren); | |
for (ConnectionDecorator conDecorator : con | |
.getConnectionDecorators()) { | |
conDecorator.setVisible(!hideChildren); | |
} | |
if (con instanceof CompositeConnection) { | |
CompositeConnection cc = (CompositeConnection) con; | |
con.setVisible(!hideChildren); | |
for (CurvedConnection conChild : cc.getChildren()) { | |
// TODO setVisible has no effect for composite | |
// connections - why? (workaround via transparency) | |
conChild.setVisible(!hideChildren); | |
conChild.getGraphicsAlgorithm().setTransparency( | |
hideChildren ? 1.0 : 0.0); | |
for (ConnectionDecorator conChildDecorator : conChild | |
.getConnectionDecorators()) { | |
conChildDecorator.setVisible(!hideChildren); | |
conChildDecorator.getGraphicsAlgorithm() | |
.setTransparency( | |
hideChildren ? 1.0 : 0.0); | |
} | |
} | |
} | |
} | |
} | |
} | |
// Hide connections of children, unhide connections only if the child | |
// element is visible | |
if (s instanceof ContainerShape) { | |
for (Shape childShape : ((ContainerShape) s).getChildren()) { | |
if (hideChildren || childShape.isVisible()) | |
this.hideContainedConnections(childShape, hideChildren); | |
} | |
} | |
} | |
// private boolean isWriteTransaction(TransactionalEditingDomain domain){ | |
// if(domain instanceof TransactionalEditingDomainImpl) | |
// return !((TransactionalEditingDomainImpl) | |
// domain).getActiveTransaction().isReadOnly(); | |
// else | |
// return true; | |
// } | |
// | |
// private TransactionalEditingDomain getEditingDomain(){ | |
// IWorkbenchPart activePart = PlatformUI.getWorkbench() | |
// .getActiveWorkbenchWindow().getActivePage().getActivePart(); | |
// | |
// IEditingDomainProvider provider = (IEditingDomainProvider) activePart | |
// .getAdapter(IEditingDomainProvider.class); | |
// assert provider.getEditingDomain() instanceof TransactionalEditingDomain; | |
// return (TransactionalEditingDomain) provider.getEditingDomain(); | |
// } | |
@Override | |
/* | |
* (non-Javadoc) | |
* | |
* @see | |
* org.eclipse.fmc.blockdiagram.editor.node.ContainerNode#getContainedWidth(org. | |
* eclipse.graphiti.mm.pictograms.ContainerShape) | |
*/ | |
public int getContainedWidth(ContainerShape cs) { | |
int result = 0; | |
for (Shape child : this.getContainedShapes(cs)) { | |
if (child.isVisible()) | |
result = Math.max(result, child.getGraphicsAlgorithm().getX() | |
+ child.getGraphicsAlgorithm().getWidth()); | |
} | |
if (result == 0) | |
return this.getDefaultWidth(); | |
return result + CONTAINER_MARGIN; | |
} | |
@Override | |
/* | |
* (non-Javadoc) | |
* | |
* @see | |
* org.eclipse.fmc.blockdiagram.editor.node.ContainerNode#getContainedHeight(org | |
* .eclipse.graphiti.mm.pictograms.ContainerShape) | |
*/ | |
public int getContainedHeight(ContainerShape cs) { | |
int result = 0; | |
for (Shape child : this.getContainedShapes(cs)) { | |
if (child.isVisible()) | |
result = Math.max(result, child.getGraphicsAlgorithm().getY() | |
+ child.getGraphicsAlgorithm().getHeight()); | |
} | |
if (result == 0) | |
return this.getDefaultHeight(); | |
return result + CONTAINER_MARGIN; | |
} | |
// ---------------------------------------- | |
// ----------- Named Node ----------------- | |
@Override | |
/* | |
* (non-Javadoc) | |
* | |
* @see | |
* org.eclipse.fmc.blockdiagram.editor.node.NamedNode#getText(org.eclipse.graphiti | |
* .mm.algorithms.GraphicsAlgorithm) | |
*/ | |
public MultiText getText(GraphicsAlgorithm graphicsAlgorithm) { | |
ContainerShape container = this.getContainerShape(graphicsAlgorithm); | |
for (Shape child : container.getChildren()) { | |
if (child.getGraphicsAlgorithm() instanceof MultiText) { | |
return (MultiText) child.getGraphicsAlgorithm(); | |
} | |
} | |
return null; | |
} | |
@Override | |
/* | |
* (non-Javadoc) | |
* | |
* @see org.eclipse.fmc.blockdiagram.editor.node.NamedNode#setTextMargin(int) | |
*/ | |
public void setTextMargin(int margin) { | |
this.textMargin = margin; | |
} | |
} |