blob: 1fd6cb71d1784d6636afad6b8b445d31d7528323 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2016 THALES GLOBAL SERVICES 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.diagram.ui.edit.api.part;
import java.util.List;
import org.eclipse.draw2d.BendpointConnectionRouter;
import org.eclipse.draw2d.Connection;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.RotatableDecoration;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.Request;
import org.eclipse.gef.SnapToHelper;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.gef.requests.DirectEditRequest;
import org.eclipse.gef.requests.SelectionRequest;
import org.eclipse.gmf.runtime.diagram.core.listener.NotificationListener;
import org.eclipse.gmf.runtime.diagram.core.listener.NotificationPreCommitListener;
import org.eclipse.gmf.runtime.diagram.ui.editparts.AbstractBorderItemEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.ConnectionNodeEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editpolicies.EditPolicyRoles;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateUnspecifiedTypeConnectionRequest;
import org.eclipse.gmf.runtime.draw2d.ui.figures.PolylineConnectionEx;
import org.eclipse.gmf.runtime.draw2d.ui.internal.routers.ITreeConnection;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.sirius.common.tools.api.util.StringUtil;
import org.eclipse.sirius.diagram.DDiagramElement;
import org.eclipse.sirius.diagram.DEdge;
import org.eclipse.sirius.diagram.DiagramPackage;
import org.eclipse.sirius.diagram.EdgeStyle;
import org.eclipse.sirius.diagram.business.api.query.DDiagramElementQuery;
import org.eclipse.sirius.diagram.description.CenteringStyle;
import org.eclipse.sirius.diagram.description.tool.RequestDescription;
import org.eclipse.sirius.diagram.ui.edit.internal.part.CommonEditPartOperation;
import org.eclipse.sirius.diagram.ui.edit.internal.part.DiagramEdgeEditPartOperation;
import org.eclipse.sirius.diagram.ui.edit.internal.part.DiagramElementEditPartOperation;
import org.eclipse.sirius.diagram.ui.edit.internal.part.EditStatusUpdater;
import org.eclipse.sirius.diagram.ui.graphical.edit.policies.DEdgeSelectionFeedbackEditPolicy;
import org.eclipse.sirius.diagram.ui.graphical.edit.policies.LaunchToolEditPolicy;
import org.eclipse.sirius.diagram.ui.graphical.edit.policies.SiriusPropertyHandlerEditPolicy;
import org.eclipse.sirius.diagram.ui.internal.edit.policies.SiriusConnectionEditPolicy;
import org.eclipse.sirius.diagram.ui.tools.api.figure.SiriusWrapLabel;
import org.eclipse.sirius.diagram.ui.tools.api.permission.EditPartAuthorityListener;
import org.eclipse.sirius.diagram.ui.tools.api.policy.CompoundEditPolicy;
import org.eclipse.sirius.diagram.ui.tools.api.requests.RequestConstants;
import org.eclipse.sirius.diagram.ui.tools.internal.graphical.edit.policies.SiriusConnectionEndPointEditPolicy;
import org.eclipse.sirius.diagram.ui.tools.internal.routers.SiriusBendpointConnectionRouter;
import org.eclipse.sirius.diagram.ui.tools.internal.ruler.SiriusSnapToHelperUtil;
import org.eclipse.sirius.ext.gmf.runtime.editparts.GraphicalHelper;
import org.eclipse.sirius.ext.gmf.runtime.editpolicies.SiriusSnapFeedbackPolicy;
import org.eclipse.sirius.viewpoint.description.tool.AbstractToolDescription;
import org.eclipse.sirius.viewpoint.description.tool.PaneBasedSelectionWizardDescription;
import org.eclipse.sirius.viewpoint.description.tool.SelectionWizardDescription;
import org.eclipse.swt.graphics.Image;
/**
* Implementation of the default behaviors of edges.
*
* @author ymortier
*/
@SuppressWarnings("restriction")
public abstract class AbstractDiagramEdgeEditPart extends ConnectionNodeEditPart implements IDiagramEdgeEditPart {
/**
* The minimum length(width or height) the edge box must have to be
* considered as selectable.
*/
private static final int EDGE_MINIMUM_LENGTH = 20;
/**
* The minimum thickness the edge box at top, bottom, left or right position
* of the union of the nodes boxes must have to be considered as selectable
* zone.
*/
private static final int EDGE_MINIMUM_THICKNESS = 20;
/**
* Define the minimum selection box size for the source and target of the
* edge (if source or target is bordered node), to fix the horizontal and
* vertical increment around which the selection of this edge select the
* source or the target and not the edge.
*/
private static final int SOURCE_TARGET_MINIMUM_SIZE_SELECTION = 20;
/** The path router. */
private static final BendpointConnectionRouter ROUTER = new SiriusBendpointConnectionRouter();
/** The authority listener. */
protected EditPartAuthorityListener authListener = new EditPartAuthorityListener(this);
/** Listens the diagram element. */
private NotificationListener adapterDiagramElement;
/** Listens the routing style. */
private NotificationPreCommitListener adapterRoutingStyle;
/** listen to semantic elements container */
private NotificationListener editModeListener = new EditStatusUpdater(this);
/**
* Creates a new <code>AbstractDiagramEdgeEditPart</code>.
*
* @param view
* the GMF view.
*/
public AbstractDiagramEdgeEditPart(final View view) {
super(view);
}
@Override
protected void registerModel() {
super.registerModel();
DiagramElementEditPartOperation.registerModel(this);
}
@Override
protected void unregisterModel() {
super.unregisterModel();
DiagramElementEditPartOperation.unregisterModel(this);
}
@Override
protected void createDefaultEditPolicies() {
super.createDefaultEditPolicies();
// Remove the connection end points role to replace by viewpoint end
// points edit policy. it's for have the scroll diagram on reconnect
// edge.
removeEditPolicy(EditPolicy.CONNECTION_ENDPOINTS_ROLE);
installEditPolicy(EditPolicy.CONNECTION_ENDPOINTS_ROLE, new SiriusConnectionEndPointEditPolicy());
// Enables Font and Style action
removeEditPolicy(EditPolicyRoles.PROPERTY_HANDLER_ROLE);
installEditPolicy(EditPolicyRoles.PROPERTY_HANDLER_ROLE, new SiriusPropertyHandlerEditPolicy());
installEditPolicy(EditPolicy.CONNECTION_ROLE, new SiriusConnectionEditPolicy());
installEditPolicy(RequestConstants.REQ_LAUNCH_TOOL, new LaunchToolEditPolicy());
final EditPolicy currentSelectionEditPolicy = getEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE);
final EditPolicy selectionFeedBackEditPolicy = new DEdgeSelectionFeedbackEditPolicy();
if (currentSelectionEditPolicy != null) {
final CompoundEditPolicy selectionCompoundEditPolicy = new CompoundEditPolicy();
selectionCompoundEditPolicy.addEditPolicy(currentSelectionEditPolicy);
selectionCompoundEditPolicy.addEditPolicy(selectionFeedBackEditPolicy);
removeEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE);
installEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE, selectionCompoundEditPolicy);
} else {
installEditPolicy(EditPolicy.SELECTION_FEEDBACK_ROLE, selectionFeedBackEditPolicy);
}
// Replace the feedback policy to have a lighter color for guides
installEditPolicy(EditPolicyRoles.SNAP_FEEDBACK_ROLE, new SiriusSnapFeedbackPolicy());
}
@Override
public Command getCommand(final Request request) {
final Command cmd = super.getCommand(request);
return CommonEditPartOperation.handleAutoPinOnInteractiveMove(this, request, cmd);
}
/**
* <p>
* {@inheritDoc}
* </p>
* Tests that the semantic model is an Edge. Refresh the edit part if the
* event is :
* <ul>
* <li><code>Notification.SET</code></li>
* <li><code>Notification.UNSET</code></li>
* <li><code>Notification.ADD</code></li>
* </ul>
*
* @see org.eclipse.gmf.runtime.diagram.ui.editparts.ConnectionNodeEditPart#handleNotificationEvent(org.eclipse.emf.common.notify.Notification)
*/
@Override
protected void handleNotificationEvent(final Notification notification) {
final EditPart styleEditPart = getStyleEditPart();
// Refreshes edit part.
if (styleEditPart != null) {
final EObject element = ((IGraphicalEditPart) styleEditPart).resolveSemanticElement();
if (element != null && element.eResource() != null) {
styleEditPart.refresh();
}
}
final EObject element = resolveSemanticElement();
if (element != null && element.eResource() != null && getParent() != null) {
refresh();
}
if (element instanceof DEdge) {
updateCenteringProperty((DEdge) element, notification);
super.handleNotificationEvent(notification);
}
}
private void updateCenteringProperty(DEdge element, Notification notification) {
if (DiagramPackage.eINSTANCE.getEdgeStyle_Centered() == notification.getFeature()) {
EdgeStyle edgeStyle = element.getOwnedStyle();
if (edgeStyle != null) {
IFigure figure = getFigure();
if (figure instanceof ViewEdgeFigure) {
((ViewEdgeFigure) figure).setCentering(edgeStyle.getCentered());
}
}
}
}
@Override
public PolylineConnectionEx getPolylineConnectionFigure() {
final Connection connection = this.getConnectionFigure();
if (connection instanceof PolylineConnectionEx) {
return (PolylineConnectionEx) connection;
}
throw new IllegalStateException();
}
@Override
public void refreshVisuals() {
super.refreshVisuals();
DiagramEdgeEditPartOperation.refreshVisuals(this);
}
@Override
protected void refreshFont() {
super.refreshFont();
DiagramEdgeEditPartOperation.refreshFont(this);
}
/**
* {@inheritDoc} Replace the original refreshBendpoints to compute the
* bendpoints of the edge with path.
*
* @see org.eclipse.gmf.runtime.diagram.ui.editparts.ConnectionEditPart#refreshBendpoints()
*/
@Override
protected void refreshBendpoints() {
// Specific refresh for edge with path
DEdge edge = getEdgeWithPath();
if (edge == null) {
super.refreshBendpoints();
} else {
DiagramEdgeEditPartOperation.refreshBendpointsWithPath(this, edge);
}
}
/**
* @return an edge if the edge of this edit part has path, null otherwise
*/
private DEdge getEdgeWithPath() {
final EObject semanticElement = resolveSemanticElement();
if (semanticElement instanceof DEdge) {
final DEdge edge = (DEdge) semanticElement;
if (edge.getPath() != null && !edge.getPath().isEmpty()) {
return edge;
}
}
return null;
}
@Override
public void refreshForegroundColor() {
// We don't change the foregroundColor to keep the selected color.
if (!DiagramEdgeEditPartOperation.isSelected(this) && !DiagramEdgeEditPartOperation.isLabelSelected(this)) {
super.refreshForegroundColor();
DiagramEdgeEditPartOperation.refreshForegroundColor(this);
}
}
@Override
public void refreshSourceDecoration() {
DiagramEdgeEditPartOperation.refreshSourceDecoration(this);
}
@Override
public void refreshTargetDecoration() {
DiagramEdgeEditPartOperation.refreshTargetDecoration(this);
}
@Override
public void refreshLineStyle() {
DiagramEdgeEditPartOperation.refreshLineStyle(this);
}
@Override
public NotificationListener getEAdapterDiagramElement() {
if (this.adapterDiagramElement == null) {
this.adapterDiagramElement = DiagramElementEditPartOperation.createEApdaterDiagramElement(this);
}
return this.adapterDiagramElement;
}
@Override
public NotificationListener getEditModeListener() {
return this.editModeListener;
}
@Override
public NotificationPreCommitListener getEAdapterRoutingStyle() {
if (this.adapterRoutingStyle == null) {
this.adapterRoutingStyle = DiagramEdgeEditPartOperation.createEAdapterRoutingStyle(this);
}
return this.adapterRoutingStyle;
}
@Override
public EditPartAuthorityListener getEditPartAuthorityListener() {
return this.authListener;
}
@Override
public Class<?> getMetamodelType() {
return DEdge.class;
}
@Override
public IStyleEditPart getStyleEditPart() {
return DiagramElementEditPartOperation.getStyleEditPart(this);
}
@Override
public List<EObject> resolveAllSemanticElements() {
return DiagramElementEditPartOperation.resolveAllSemanticElements(this);
}
@Override
public DDiagramElement resolveDiagramElement() {
return DiagramElementEditPartOperation.resolveDiagramElement(this);
}
@Override
public EObject resolveTargetSemanticElement() {
return DiagramElementEditPartOperation.resolveTargetSemanticElement(this);
}
@Override
public void routingStyleChanged(final Notification message) {
DiagramEdgeEditPartOperation.routingStyleChanged(this, message);
}
@Override
public void activate() {
if (!isActive()) {
/*
* Fix bad behavior with hidden Edges while deleting ports.
*/
final EObject element = resolveSemanticElement();
if (element instanceof DEdge) {
super.activate();
DiagramElementEditPartOperation.activate(this);
DiagramEdgeEditPartOperation.activate(this);
}
}
installRouter();
}
@Override
public void deactivate() {
if (isActive()) {
DiagramEdgeEditPartOperation.deactivate(this);
DiagramElementEditPartOperation.deactivate(this);
super.deactivate();
}
}
@Override
public void enableEditMode() {
/*
* We want to be sure nobody is enabling the edit mode if the element is
* locked.
*/
if (!authListener.isLocked()) {
super.enableEditMode();
}
}
@Override
protected void installRouter() {
final EObject element = this.resolveSemanticElement();
if (element instanceof DEdge) {
final DEdge edge = (DEdge) element;
if (edge.getPath() != null && !edge.getPath().isEmpty()) {
this.getPolylineConnectionFigure().setConnectionRouter(ROUTER);
} else {
super.installRouter();
}
}
}
@Override
protected void refreshRoutingStyles() {
final EObject element = resolveSemanticElement();
if (element instanceof DEdge) {
super.refreshRoutingStyles();
}
}
@Override
protected void refreshRouterChange() {
final EObject element = resolveSemanticElement();
if (element instanceof DEdge) {
super.refreshRouterChange();
}
}
@Override
public Image getLabelIcon() {
return DiagramElementEditPartOperation.getLabelIcon(this);
}
@Override
protected Connection createConnectionFigure() {
return new ViewEdgeFigure();
}
/**
* The figure.
*/
public class ViewEdgeFigure extends PolylineConnectionEx implements ITreeConnection {
/**
* @was-generated
*/
private SiriusWrapLabel fFigureViewEdgeNameFigure;
private SiriusWrapLabel fFigureViewEdgeBeginNameFigure;
private SiriusWrapLabel fFigureViewEdgeEndNameFigure;
private CenteringStyle centeringStyle;
/**
* Constructor.
*/
public ViewEdgeFigure() {
createContents();
}
/**
* @not-generated : we don't want to create the label
*/
private void createContents() {
final EObject element = resolveSemanticElement();
// We must create the fFigureViewEdgeNameFigure to avoid NPE after
// (On some cases, like Undo of ContainerDrop operation, the element
// is DSemanticDiagram and not DEdge)
createCenterLabelFigure(element);
createBeginLabelFigure(element);
createEndLabelFigure(element);
initCentering(element);
}
private void initCentering(EObject element) {
if (element instanceof DEdge) {
EdgeStyle edgeStyle = ((DEdge) element).getOwnedStyle();
if (edgeStyle != null) {
setCentering(edgeStyle.getCentered());
}
}
}
private void setCentering(CenteringStyle centering) {
this.centeringStyle = centering;
}
/**
* Get the centeringStyle value.
*
* @return the {@link CenteringStyle}.
*/
public CenteringStyle getCenteringStyle() {
return this.centeringStyle;
}
/**
* Returns whether the connection is centered on its source or not.
*
* @return true if the connection is centered on its source or both.
* False otherwise.
*/
public boolean isSourceCentered() {
return CenteringStyle.BOTH == getCenteringStyle() || CenteringStyle.SOURCE == getCenteringStyle();
}
/**
* Returns whether the connection is centered on its target or not.
*
* @return true if the connection is centered on its target or both.
* False otherwise.
*/
public boolean isTargetCentered() {
return CenteringStyle.BOTH == getCenteringStyle() || CenteringStyle.TARGET == getCenteringStyle();
}
/**
* @param element
*/
private void createCenterLabelFigure(final EObject element) {
fFigureViewEdgeNameFigure = new SiriusWrapLabel();
if (element instanceof DEdge) {
DEdge edge = (DEdge) element;
fFigureViewEdgeNameFigure.setText(edge.getName());
fFigureViewEdgeNameFigure.setVisible(!StringUtil.isEmpty(edge.getName()));
} else {
fFigureViewEdgeNameFigure.setVisible(false);
}
fFigureViewEdgeNameFigure.setLabelAlignment(PositionConstants.CENTER);
fFigureViewEdgeNameFigure.setTextWrap(true);
fFigureViewEdgeNameFigure.setTextWrapAlignment(PositionConstants.CENTER);
this.add(fFigureViewEdgeNameFigure);
}
/**
* @param element
*/
private void createBeginLabelFigure(final EObject element) {
fFigureViewEdgeBeginNameFigure = new SiriusWrapLabel();
if (element instanceof DEdge) {
DEdge edge = (DEdge) element;
fFigureViewEdgeBeginNameFigure.setText(edge.getBeginLabel());
fFigureViewEdgeBeginNameFigure.setVisible(!StringUtil.isEmpty(edge.getBeginLabel()));
} else {
fFigureViewEdgeBeginNameFigure.setVisible(false);
}
fFigureViewEdgeBeginNameFigure.setLabelAlignment(PositionConstants.LEFT);
fFigureViewEdgeBeginNameFigure.setTextWrap(true);
fFigureViewEdgeBeginNameFigure.setTextWrapAlignment(PositionConstants.CENTER);
this.add(fFigureViewEdgeBeginNameFigure);
}
/**
* @param element
*/
private void createEndLabelFigure(final EObject element) {
fFigureViewEdgeEndNameFigure = new SiriusWrapLabel();
if (element instanceof DEdge) {
DEdge edge = (DEdge) element;
fFigureViewEdgeEndNameFigure.setText(edge.getEndLabel());
fFigureViewEdgeEndNameFigure.setVisible(!StringUtil.isEmpty(edge.getEndLabel()));
} else {
fFigureViewEdgeEndNameFigure.setVisible(false);
}
fFigureViewEdgeEndNameFigure.setLabelAlignment(PositionConstants.CENTER);
fFigureViewEdgeEndNameFigure.setTextWrap(true);
fFigureViewEdgeEndNameFigure.setTextWrapAlignment(PositionConstants.CENTER);
this.add(fFigureViewEdgeEndNameFigure);
}
@SuppressWarnings("deprecation")
@Override
public void layout() {
if (!isActive()) {
return;
}
final EObject element = resolveSemanticElement();
if (element != null && DEdge.class.isInstance(element)) {
final DEdge edge = (DEdge) element;
boolean needRefreshVisuals = false;
if (edge.getPath() != null && !edge.getPath().isEmpty()) {
if (AbstractDiagramEdgeEditPart.invalidPath(AbstractDiagramEdgeEditPart.this, edge)) {
if (getSelected() != EditPart.SELECTED_PRIMARY) {
needRefreshVisuals = true;
}
}
}
if (needRefreshVisuals || edge.isIsMockEdge()) {
refreshVisuals();
}
if (this.getBounds() != null && getSource() != null && getTarget() != null) {
super.layout();
}
if (edge.getName() == null || StringUtil.isEmpty(edge.getName())) {
fFigureViewEdgeNameFigure.setVisible(false);
}
if (edge.getName() != null && !StringUtil.isEmpty(edge.getName()) && !(new DDiagramElementQuery(edge).isLabelHidden()) && !fFigureViewEdgeNameFigure.isVisible()) {
fFigureViewEdgeNameFigure.setVisible(true);
}
if (edge.getEndLabel() == null || StringUtil.isEmpty(edge.getEndLabel())) {
fFigureViewEdgeEndNameFigure.setVisible(false);
}
if (edge.getEndLabel() != null && !StringUtil.isEmpty(edge.getEndLabel()) && !(new DDiagramElementQuery(edge).isLabelHidden()) && !fFigureViewEdgeEndNameFigure.isVisible()) {
fFigureViewEdgeEndNameFigure.setVisible(true);
}
if (edge.getBeginLabel() == null || StringUtil.isEmpty(edge.getBeginLabel())) {
fFigureViewEdgeBeginNameFigure.setVisible(false);
}
if (edge.getBeginLabel() != null && !StringUtil.isEmpty(edge.getBeginLabel()) && !(new DDiagramElementQuery(edge).isLabelHidden()) && !fFigureViewEdgeBeginNameFigure.isVisible()) {
fFigureViewEdgeBeginNameFigure.setVisible(true);
}
}
}
@SuppressWarnings("deprecation")
@Override
public void paintFigure(final Graphics graphics) {
if (!isActive()) {
return;
}
final EObject element = resolveSemanticElement();
if (element != null && DEdge.class.isInstance(element)) {
final DEdge viewEdge = (DEdge) element;
if (!viewEdge.isIsMockEdge() && viewEdge.isVisible()) {
super.paintFigure(graphics);
}
}
}
@Override
protected void paintChildren(Graphics graphics) {
if (!isActive()) {
return;
}
final EObject element = resolveSemanticElement();
if (element != null && DEdge.class.isInstance(element)) {
final DEdge viewEdge = (DEdge) element;
if (viewEdge.isVisible()) {
super.paintChildren(graphics);
}
}
}
/**
* Get the name figure.
*
* @return the name figure
*/
public SiriusWrapLabel getFigureViewEdgeNameFigure() {
return fFigureViewEdgeNameFigure;
}
/**
* Get the name figure.
*
* @return the name figure
*/
public SiriusWrapLabel getFigureViewBeginEdgeNameFigure() {
return fFigureViewEdgeBeginNameFigure;
}
/**
* Get the name figure.
*
* @return the name figure
*/
public SiriusWrapLabel getFigureViewEndEdgeNameFigure() {
return fFigureViewEdgeEndNameFigure;
}
/**
* Overridden to have public access to its method.
*
* {@inheritDoc}
*/
@Override
public RotatableDecoration getSourceDecoration() {
return super.getSourceDecoration();
}
/**
* Overridden to have public access to its method.
*
* {@inheritDoc}
*/
@Override
public RotatableDecoration getTargetDecoration() {
return super.getTargetDecoration();
}
@Override
public String getHint() {
if (AbstractDiagramEdgeEditPart.this.getTarget() != null) {
return AbstractDiagramEdgeEditPart.this.getTarget().toString();
} else {
return "base"; //$NON-NLS-1$
}
}
@Override
public Orientation getOrientation() {
return Orientation.VERTICAL;
}
/**
* Returns the edit part who owns this figure.
*
* @return the owner edit part.
*/
public IGraphicalEditPart getEditPart() {
return AbstractDiagramEdgeEditPart.this;
}
}
private static boolean invalidPath(final AbstractDiagramEdgeEditPart editPart, final DEdge edge) {
return true;
}
@Override
public void performRequest(final Request request) {
if (request instanceof DirectEditRequest || RequestConstants.REQ_DIRECT_EDIT.equals(request.getType())) {
this.performDirectEditRequest(request);
} else {
super.performRequest(request);
}
}
/**
* {@inheritDoc} <BR>
* Return the current edit part for CreateRequest with RequestDescription,
* SelectionWizardDescription or PaneBasedSelectionWizardDescription as new
* object.<BR>
* CreateUnspecifiedTypeConnectionRequest is used to create a NoteAtachment
* directly from the Note.
*
* The returned edit part can be this edge one or the target or source's one
* if this edge part is over the nodes ones.
*
* @see org.eclipse.gmf.runtime.diagram.ui.editparts.ConnectionNodeEditPart#getTargetEditPart(org.eclipse.gef.Request)
*
* T
*/
@Override
public EditPart getTargetEditPart(final Request request) {
EditPart result = null;
AbstractToolDescription tool = null;
// Attaching notes and robustness
if (request instanceof CreateRequest && !(request instanceof CreateUnspecifiedTypeConnectionRequest) && ((CreateRequest) request).getNewObject() instanceof AbstractToolDescription) {
tool = (AbstractToolDescription) ((CreateRequest) request).getNewObject();
}
if (tool instanceof RequestDescription || tool instanceof SelectionWizardDescription || tool instanceof PaneBasedSelectionWizardDescription) {
return this;
} else if (request instanceof SelectionRequest) {
boolean expandSource = false;
boolean expandTarget = false;
Rectangle sourceBoundsWithMargin = null;
Rectangle targetBoundsWithMargin = null;
int horizontalSourceIncrement = 0;
int verticalSourceIncrement = 0;
int horizontalTargetIncrement = 0;
int verticalTargetIncrement = 0;
Rectangle sourceBounds = null;
Rectangle targetBounds = null;
// We compute the bounds of the source edit part with margins if it
// is too small.
if (getSource() instanceof AbstractBorderItemEditPart) {
sourceBounds = GraphicalHelper.getAbsoluteBounds((IGraphicalEditPart) getSource());
// The method used to add margins doubles the given horizontal
// and vertical so we divide these values to always have the
// same minimum selection box.
horizontalSourceIncrement = SOURCE_TARGET_MINIMUM_SIZE_SELECTION > sourceBounds.width ? Math.round((SOURCE_TARGET_MINIMUM_SIZE_SELECTION - sourceBounds.width) / 2) : 0;
verticalSourceIncrement = SOURCE_TARGET_MINIMUM_SIZE_SELECTION > sourceBounds.height ? Math.round((SOURCE_TARGET_MINIMUM_SIZE_SELECTION - sourceBounds.height) / 2) : 0;
if (horizontalSourceIncrement > 0 || verticalSourceIncrement > 0) {
expandSource = true;
sourceBoundsWithMargin = sourceBounds.getExpanded(horizontalSourceIncrement, verticalSourceIncrement);
} else {
sourceBoundsWithMargin = sourceBounds;
}
}
// We compute the bounds of the target edit part with margins if it
// is too small.
if (getTarget() instanceof AbstractBorderItemEditPart) {
targetBounds = GraphicalHelper.getAbsoluteBounds((IGraphicalEditPart) getTarget());
horizontalTargetIncrement = SOURCE_TARGET_MINIMUM_SIZE_SELECTION > targetBounds.width ? Math.round((SOURCE_TARGET_MINIMUM_SIZE_SELECTION - targetBounds.width) / 2) : 0;
verticalTargetIncrement = SOURCE_TARGET_MINIMUM_SIZE_SELECTION > targetBounds.height ? Math.round((SOURCE_TARGET_MINIMUM_SIZE_SELECTION - targetBounds.height) / 2) : 0;
if (horizontalTargetIncrement > 0 || verticalTargetIncrement > 0) {
expandTarget = true;
targetBoundsWithMargin = targetBounds.getExpanded(horizontalTargetIncrement, verticalTargetIncrement);
} else {
targetBoundsWithMargin = targetBounds;
}
}
// We test if an edge selectable zone exists with the margined
// source and target edit part. Margins can be zero if parts are big
// enough.
boolean isEdgeSelectableZonePresent = false;
if (expandTarget || expandSource) {
Rectangle edgeBounds = GraphicalHelper.getAbsoluteBounds(this);
isEdgeSelectableZonePresent = isEdgeSelectableZonePresent(edgeBounds, sourceBoundsWithMargin, targetBoundsWithMargin);
}
Point location = ((SelectionRequest) request).getLocation();
if (location != null) {
boolean returnSource = isEdgeSelectableZonePresent && sourceBoundsWithMargin != null && sourceBoundsWithMargin.contains(location);
returnSource = returnSource || (sourceBounds != null && sourceBounds.contains(location));
if (returnSource) {
// The mouse is located in the margined source part and an
// edge selectable zone is present. So the considered
// selection is the source one.
result = getSource();
} else if (result == null) {
boolean returnTarget = isEdgeSelectableZonePresent && targetBoundsWithMargin != null && targetBoundsWithMargin.contains(location);
returnTarget = returnTarget || (targetBounds != null && targetBounds.contains(location));
if (returnTarget) {
// The mouse is located in the margined target part and
// an edge selectable zone is present. So the considered
// selection is the target one.
result = getTarget();
}
}
}
}
if (result == null) {
// The mouse is located in the edge bounds but not in the margined
// source and target one so we return it.
result = super.getTargetEditPart(request);
}
return result;
}
/**
* Returns true if the edge represented by the given bounds has a zone with
* the minimum width or height allowing its selection.
*
* Either the edge has a box outside the one formed by the union of the
* nodes boxes that meets the minimum requirement that is a thickness of
* {@link AbstractDiagramEdgeEditPart#EDGE_MINIMUM_THICKNESS}.
*
* Or it does not. In this case we compute the edge length from its box by
* taking in consideration the margin expansions of the nodes boxes. The
* computing is done based on the worst scenario of nodes occupation over
* the edge. The computing depends on the existence of source and target
* bounds. If both exist, then the worst scenario is the boxes of the two
* nodes are upon the edge's one. In this case, the edge's box is considered
* as selectable if its bound width or height is greater than the minimum
* considered when removed from the width or height of the margined nodes.
* If only one node exists, then the worst scenario is one node is
* completely over the edge. In this case, the edge's box is considered as
* selectable if its width or height is greater than the minimum considered
* when removed from the minimum width or height of the unique margined
* node.
*
* @param edgeBounds
* the {@link Rectangle} encapsulating the selected edge.
* @param sourceBoundsWithMargins
* the {@link Rectangle} encapsulating the source part of the
* selected edge.
* @param targetBoundsWithMargins
* the {@link Rectangle} encapsulating the target part of the
* selected edge.
* @return true if the edge represented by the given bounds has the minimum
* width or height allowing its selection. False otherwise.
*/
private boolean isEdgeSelectableZonePresent(Rectangle edgeBounds, Rectangle sourceBoundsWithMargins, Rectangle targetBoundsWithMargins) {
if (sourceBoundsWithMargins == null && targetBoundsWithMargins == null) {
return true;
} else {
boolean isEdgeSelectableZonePresent = false;
if (sourceBoundsWithMargins != null && targetBoundsWithMargins != null) {
// We check that the edge is selectable on the edge boxes not in
// the union of nodes boxes in all directions (top, bottom,
// left, right).
Rectangle sourceTargetUnionBox = sourceBoundsWithMargins.getUnion(targetBoundsWithMargins);
// compute left box
isEdgeSelectableZonePresent = isEdgeSelectableZonePresent || (sourceTargetUnionBox.x - edgeBounds.x > EDGE_MINIMUM_THICKNESS);
// compute right box
isEdgeSelectableZonePresent = isEdgeSelectableZonePresent || ((edgeBounds.x + edgeBounds.width) - (sourceTargetUnionBox.x + sourceTargetUnionBox.width) > EDGE_MINIMUM_THICKNESS);
// compute top box
isEdgeSelectableZonePresent = isEdgeSelectableZonePresent || (sourceTargetUnionBox.y - edgeBounds.y > EDGE_MINIMUM_THICKNESS);
// compute bottom box
isEdgeSelectableZonePresent = isEdgeSelectableZonePresent || ((edgeBounds.y + edgeBounds.height) - (sourceTargetUnionBox.y + sourceTargetUnionBox.height) > EDGE_MINIMUM_THICKNESS);
}
if (!isEdgeSelectableZonePresent) {
// there are no edge boxes selectable outside of the box made of
// the union of the nodes boxes. So we checks we have the
// minimum width or height between the two nodes boxes with
// maximum margins.
isEdgeSelectableZonePresent = sourceBoundsWithMargins == null || targetBoundsWithMargins == null
? edgeBounds.width - SOURCE_TARGET_MINIMUM_SIZE_SELECTION >= EDGE_MINIMUM_LENGTH || edgeBounds.height - SOURCE_TARGET_MINIMUM_SIZE_SELECTION > EDGE_MINIMUM_LENGTH
: edgeBounds.width - (SOURCE_TARGET_MINIMUM_SIZE_SELECTION * 2) >= EDGE_MINIMUM_LENGTH || edgeBounds.height - (SOURCE_TARGET_MINIMUM_SIZE_SELECTION * 2) >= EDGE_MINIMUM_LENGTH;
}
return isEdgeSelectableZonePresent;
}
}
@Override
public Object getAdapter(Class key) {
if (key == SnapToHelper.class) {
return SiriusSnapToHelperUtil.getSnapHelper((org.eclipse.gef.GraphicalEditPart) this.getSource());
}
return super.getAdapter(key);
}
}