/******************************************************************************
* Copyright (c) 2016 Oracle
* 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:
* Shenxue Zhou - initial implementation and ongoing maintenance
* Konstantin Komissarchik - [342897] Integrate with properties view
* Konstantin Komissarchik - [342775] Support EL in MasterDetailsTreeNodeDef.ImagePath
* Ling Hao - [44319] Image specification for diagram parts inconsistent with the rest of sdef
* Konstantin Komissarchik - [378756] Convert ModelElementListener and ModelPropertyListener to common listener infrastructure
* Ling Hao - [383924] Flexible diagram node shapes
******************************************************************************/
package org.eclipse.sapphire.ui.diagram.editor;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.sapphire.Element;
import org.eclipse.sapphire.FilteredListener;
import org.eclipse.sapphire.modeling.Status;
import org.eclipse.sapphire.modeling.el.FunctionResult;
import org.eclipse.sapphire.ui.Bounds;
import org.eclipse.sapphire.ui.PartValidationEvent;
import org.eclipse.sapphire.ui.SapphireAction;
import org.eclipse.sapphire.ui.SapphireActionHandler;
import org.eclipse.sapphire.ui.SapphireActionSystem;
import org.eclipse.sapphire.ui.SapphirePart;
import org.eclipse.sapphire.ui.diagram.def.IDiagramNodeDef;
import org.eclipse.sapphire.ui.diagram.shape.def.ImageDef;
import org.eclipse.sapphire.ui.diagram.shape.def.RectangleDef;
import org.eclipse.sapphire.ui.diagram.shape.def.ShapeDef;
import org.eclipse.sapphire.ui.diagram.shape.def.TextDef;
import org.eclipse.sapphire.ui.forms.PropertiesViewContributionManager;
import org.eclipse.sapphire.ui.forms.PropertiesViewContributionPart;
import org.eclipse.sapphire.ui.forms.PropertiesViewContributorPart;
/**
* @author <a href="mailto:shenxue.zhou@oracle.com">Shenxue Zhou</a>
* @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a>
* @author <a href="mailto:ling.hao@oracle.com">Ling Hao</a>
*/
public class DiagramNodePart
extends SapphirePart
implements PropertiesViewContributorPart
{
private static final String DEFAULT_ACTION_ID = "Sapphire.Diagram.Node.Default";
private DiagramNodeTemplate nodeTemplate;
private IDiagramNodeDef definition;
private Element modelElement;
private FunctionResult idFunctionResult;
private SapphireAction defaultAction;
private SapphireActionHandler defaultActionHandler;
private PropertiesViewContributionManager propertiesViewContributionManager;
private DiagramNodeBounds nodeBounds = new DiagramNodeBounds();
private ShapePart shapePart;
private static final String NODE_ID_SEPARATOR = "#";
@Override
protected void init()
{
super.init();
this.nodeTemplate = (DiagramNodeTemplate) parent();
this.definition = (IDiagramNodeDef)super.definition;
this.modelElement = getModelElement();
this.idFunctionResult = initExpression
(
this.definition.getInstanceId().content(),
String.class,
null,
new Runnable()
{
public void run()
{
}
}
);
// create shape part
createShapePart();
if (getPropertiesViewContribution() != null)
{
getPropertiesViewContribution().attach
(
new FilteredListener<PartValidationEvent>()
{
@Override
protected void handleTypedEvent( PartValidationEvent event )
{
refreshValidation();
}
}
);
}
}
public DiagramNodeTemplate getDiagramNodeTemplate()
{
return this.nodeTemplate;
}
@Override
public Element getLocalModelElement()
{
return this.modelElement;
}
public ShapePart getShapePart()
{
return this.shapePart;
}
public List<TextPart> getContainedTextParts()
{
ShapePart shapePart = getShapePart();
if (shapePart instanceof TextPart)
{
return Collections.singletonList((TextPart)shapePart);
}
else
{
return ShapePart.getContainedShapeParts(shapePart, TextPart.class);
}
}
public List<ImagePart> getContainedImageParts()
{
ShapePart shapePart = getShapePart();
if (shapePart instanceof ImagePart)
{
return Collections.singletonList((ImagePart)shapePart);
}
else
{
return ShapePart.getContainedShapeParts(shapePart, ImagePart.class);
}
}
@Override
public Set<String> getActionContexts()
{
Set<String> contextSet = new HashSet<String>();
contextSet.add(SapphireActionSystem.CONTEXT_DIAGRAM_NODE);
contextSet.add(SapphireActionSystem.CONTEXT_DIAGRAM_NODE_HIDDEN);
return contextSet;
}
public SapphireActionHandler getDefaultActionHandler()
{
if (this.defaultAction == null)
{
// Default Action handler
this.defaultAction = getAction(DEFAULT_ACTION_ID);
this.defaultActionHandler = this.defaultAction.getFirstActiveHandler();
}
return this.defaultActionHandler;
}
@Override
public void dispose()
{
super.dispose();
if (this.idFunctionResult != null)
{
this.idFunctionResult.dispose();
}
this.shapePart.dispose();
}
@Override
protected Status computeValidation()
{
final Status.CompositeStatusFactory factory = Status.factoryForComposite();
factory.merge(getShapePart().validation());
if (getPropertiesViewContribution() != null)
factory.merge(getPropertiesViewContribution().validation());
return factory.create();
}
// private void notifyShapeUpdate(ShapePart shapePart)
// {
// DiagramShapeEvent event = new DiagramShapeEvent(this, shapePart);
// event.setShapeEventType(ShapeEventType.ShapeUpdate);
// this.broadcast(event);
// }
//
// private void notifyTextChange(ShapePart shapePart)
// {
// DiagramShapeEvent event = new DiagramShapeEvent(this, shapePart);
// event.setShapeEventType(ShapeEventType.TextChange);
// this.broadcast(event);
// }
//
// private void notifyShapeVisibility(ShapePart shapePart)
// {
// DiagramShapeEvent event = new DiagramShapeEvent(this, shapePart);
// event.setShapeEventType(ShapeEventType.ShapeVisibilityUpdate);
// this.broadcast(event);
// }
//
// private void notifyShapeAdd(ShapePart shapePart)
// {
// DiagramShapeEvent event = new DiagramShapeEvent(this, shapePart);
// event.setShapeEventType(ShapeEventType.ShapeAdd);
// this.broadcast(event);
// }
//
// private void notifyShapeDelete(ShapePart shapePart)
// {
// DiagramShapeEvent event = new DiagramShapeEvent(this, shapePart);
// event.setShapeEventType(ShapeEventType.ShapeDelete);
// this.broadcast(event);
// }
//
// private void notifyShapeReorder(ShapeFactoryPart shapeFactory)
// {
// DiagramShapeEvent event = new DiagramShapeEvent(this, shapeFactory);
// event.setShapeEventType(ShapeEventType.ShapeReorder);
// this.broadcast(event);
// }
public String getNodeTypeId()
{
return this.definition.getId().content();
}
public String getInstanceId()
{
String id = null;
if( this.idFunctionResult != null )
{
id = (String) this.idFunctionResult.value();
}
return id;
}
public String getId()
{
StringBuffer buffer = new StringBuffer(getNodeTypeId());
buffer.append(NODE_ID_SEPARATOR);
String instanceId = getInstanceId();
if (instanceId != null && instanceId.length() > 0)
{
buffer.append(getInstanceId());
buffer.append(NODE_ID_SEPARATOR);
}
List<DiagramNodePart> nodeParts = getDiagramNodeTemplate().getDiagramNodes();
int index = nodeParts.indexOf(this);
buffer.append(index);
return buffer.toString();
}
public boolean canResizeShape()
{
return this.definition.isResizable().content();
}
public DiagramNodeBounds getNodeBounds()
{
DiagramNodeBounds bounds = new DiagramNodeBounds(this.nodeBounds);
if (bounds.getWidth() < 0 && this.definition.getWidth().content() != null )
{
bounds.setWidth(this.definition.getWidth().content());
}
if (bounds.getHeight() < 0 && this.definition.getHeight().content() != null)
{
bounds.setHeight(this.definition.getHeight().content());
}
return bounds;
}
public void setNodeBounds(int x, int y)
{
setNodeBounds(new DiagramNodeBounds(x, y, -1, -1, false, false));
}
public void setNodeBounds(int x, int y, int width, int height)
{
setNodeBounds(new DiagramNodeBounds(x, y, width, height, false, false));
}
public void setNodeBounds(int x, int y, boolean autoLayout, boolean defaultPosition)
{
setNodeBounds(new DiagramNodeBounds(x, y, -1, -1, autoLayout, defaultPosition));
}
public void setNodeBounds(Bounds bounds)
{
DiagramNodeBounds nodeBounds = new DiagramNodeBounds(bounds);
setNodeBounds(nodeBounds);
}
public void setNodeBounds(DiagramNodeBounds bounds)
{
// TODO handle node resizing events - rename move?
if (!this.nodeBounds.equals(bounds))
{
this.nodeBounds.setX(bounds.getX());
this.nodeBounds.setY(bounds.getY());
this.nodeBounds.setWidth(bounds.getWidth());
this.nodeBounds.setHeight(bounds.getHeight());
this.nodeBounds.setAutoLayout(bounds.isAutoLayout());
this.nodeBounds.setDefaultPosition(bounds.isDefaultPosition());
notifyNodeMove();
}
}
private void notifyNodeMove()
{
DiagramNodeMoveEvent event = new DiagramNodeMoveEvent(this);
this.broadcast(event);
}
public PropertiesViewContributionPart getPropertiesViewContribution()
{
if( this.propertiesViewContributionManager == null )
{
this.propertiesViewContributionManager = new PropertiesViewContributionManager( this, getLocalModelElement() );
}
return this.propertiesViewContributionManager.getPropertiesViewContribution();
}
private void createShapePart()
{
ShapeDef shape = this.definition.getShape().content();
if (shape instanceof TextDef)
{
this.shapePart = new TextPart();
}
else if (shape instanceof ImageDef)
{
this.shapePart = new ImagePart();
}
else if (shape instanceof RectangleDef)
{
this.shapePart = new RectanglePart();
}
if (definition.getSelectionPresentation() != null)
{
this.shapePart.setSelectionPresentation(definition.getSelectionPresentation());
}
this.shapePart.init(this, this.modelElement, shape, Collections.<String,String>emptyMap());
this.shapePart.initialize();
this.shapePart.attach
(
new FilteredListener<PartValidationEvent>()
{
@Override
protected void handleTypedEvent( PartValidationEvent event )
{
refreshValidation();
}
}
);
}
}