/***************************************************************************** * Copyright (c) 2010-2011 CEA LIST. * * * 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: * * Arthur daussy (Atos) arthur.daussy@atos.net - Bug : 365404: [State Machine Diagram] Internal transition should be displayed as label on the state figure * *****************************************************************************/ package org.eclipse.papyrus.uml.diagram.statemachine.custom.policies; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.draw2d.ColorConstants; import org.eclipse.draw2d.FigureUtilities; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.PositionConstants; import org.eclipse.draw2d.RectangleFigure; import org.eclipse.draw2d.Shape; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.PrecisionRectangle; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.emf.ecore.ENamedElement; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.gef.EditPart; import org.eclipse.gef.Request; import org.eclipse.gef.commands.Command; import org.eclipse.gef.commands.CompoundCommand; import org.eclipse.gef.commands.UnexecutableCommand; import org.eclipse.gef.requests.ChangeBoundsRequest; import org.eclipse.gmf.runtime.common.core.command.CommandResult; import org.eclipse.gmf.runtime.common.core.command.CompositeCommand; import org.eclipse.gmf.runtime.diagram.core.commands.SetPropertyCommand; import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil; import org.eclipse.gmf.runtime.diagram.ui.commands.CreateCommand; import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; import org.eclipse.gmf.runtime.diagram.ui.commands.SetBoundsCommand; import org.eclipse.gmf.runtime.diagram.ui.editparts.CompartmentEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; import org.eclipse.gmf.runtime.diagram.ui.l10n.DiagramUIMessages; import org.eclipse.gmf.runtime.diagram.ui.requests.CreateConnectionViewRequest; import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest.ViewDescriptor; import org.eclipse.gmf.runtime.diagram.ui.requests.DropObjectsRequest; import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter; import org.eclipse.gmf.runtime.emf.core.util.PackageUtil; import org.eclipse.gmf.runtime.emf.type.core.IElementType; import org.eclipse.gmf.runtime.emf.type.core.IHintedType; import org.eclipse.gmf.runtime.notation.Node; import org.eclipse.gmf.runtime.notation.View; import org.eclipse.papyrus.uml.diagram.common.commands.CommonDeferredCreateConnectionViewCommand; import org.eclipse.papyrus.uml.diagram.common.commands.SemanticAdapter; import org.eclipse.papyrus.uml.diagram.common.editpolicies.OldCommonDiagramDragDropEditPolicy; import org.eclipse.papyrus.uml.diagram.common.util.DiagramEditPartsUtil; import org.eclipse.papyrus.uml.diagram.statemachine.custom.commands.CreateViewCommand; import org.eclipse.papyrus.uml.diagram.statemachine.custom.commands.CustomCompositeStateSetBoundsCommand; import org.eclipse.papyrus.uml.diagram.statemachine.custom.commands.CustomCompositeStateWithDefaultRegionCreateNodeCommand; import org.eclipse.papyrus.uml.diagram.statemachine.custom.commands.CustomFirstRegionInCompositeStateCreateElementCommand; import org.eclipse.papyrus.uml.diagram.statemachine.custom.commands.CustomRegionCreateElementCommand; import org.eclipse.papyrus.uml.diagram.statemachine.custom.commands.CustomRegionMoveCommand; import org.eclipse.papyrus.uml.diagram.statemachine.custom.commands.CustomStateMachineSetBoundsCommand; import org.eclipse.papyrus.uml.diagram.statemachine.custom.commands.CustomStateMachineWithDefaultRegionCreateNodeCommand; import org.eclipse.papyrus.uml.diagram.statemachine.custom.edit.part.CustomRegionEditPart; import org.eclipse.papyrus.uml.diagram.statemachine.custom.figures.RegionFigure; import org.eclipse.papyrus.uml.diagram.statemachine.custom.helpers.StateMachineLinkMappingHelper; import org.eclipse.papyrus.uml.diagram.statemachine.custom.helpers.Zone; import org.eclipse.papyrus.uml.diagram.statemachine.custom.locators.CustomEntryExitPointPositionLocator; import org.eclipse.papyrus.uml.diagram.statemachine.edit.parts.ConnectionPointReferenceEditPart; import org.eclipse.papyrus.uml.diagram.statemachine.edit.parts.PseudostateEntryPointEditPart; import org.eclipse.papyrus.uml.diagram.statemachine.edit.parts.PseudostateExitPointEditPart; import org.eclipse.papyrus.uml.diagram.statemachine.edit.parts.RegionCompartmentEditPart; import org.eclipse.papyrus.uml.diagram.statemachine.edit.parts.RegionEditPart; import org.eclipse.papyrus.uml.diagram.statemachine.edit.parts.StateCompartmentEditPart; import org.eclipse.papyrus.uml.diagram.statemachine.edit.parts.StateEditPart; import org.eclipse.papyrus.uml.diagram.statemachine.edit.parts.StateMachineCompartmentEditPart; import org.eclipse.papyrus.uml.diagram.statemachine.edit.parts.StateMachineEditPart; import org.eclipse.papyrus.uml.diagram.statemachine.edit.parts.TransitionEditPart; import org.eclipse.papyrus.uml.diagram.statemachine.part.UMLVisualIDRegistry; import org.eclipse.papyrus.uml.diagram.statemachine.providers.UMLElementTypes; import org.eclipse.uml2.uml.ConnectionPointReference; import org.eclipse.uml2.uml.Element; import org.eclipse.uml2.uml.Pseudostate; import org.eclipse.uml2.uml.PseudostateKind; import org.eclipse.uml2.uml.Region; import org.eclipse.uml2.uml.State; import org.eclipse.uml2.uml.StateMachine; import org.eclipse.uml2.uml.Transition; import org.eclipse.uml2.uml.TransitionKind; import org.eclipse.uml2.uml.Vertex; public class CustomStateMachineDiagramDragDropEditPolicy extends OldCommonDiagramDragDropEditPolicy { IFigure sizeOnDropFeedback = null; String dropLocation = Zone.RIGHT; boolean fromOutline = false; /** * Instantiates a new state machine diagram drag drop edit policy with the * right link mapping helper */ public CustomStateMachineDiagramDragDropEditPolicy() { super(StateMachineLinkMappingHelper.getInstance()); } /** * <pre> * Returns the drop command for Affixed nodes (Pseudostate entry/exitPoint, ConnectionPointReference). * </pre> * * @param dropRequest * the drop request * @param location * the location to drop the element * @param droppedElement * the element to drop * @param nodeVISUALID * the visual identifier of the EditPart of the dropped element * @return the drop command */ protected Command dropAffixedNode(DropObjectsRequest dropRequest, Element droppedElement, int nodeVISUALID) { // The dropped element must be a Pseudostate or ConnectionPointReference if(!((droppedElement instanceof Pseudostate) || (droppedElement instanceof ConnectionPointReference))) { return UnexecutableCommand.INSTANCE; } if(droppedElement instanceof Pseudostate) { Pseudostate ps = (Pseudostate)droppedElement; // The dropped element must be an entry or exitPoint PseudostateKind kind = ps.getKind(); if(!(kind.equals(PseudostateKind.ENTRY_POINT_LITERAL) || kind.equals(PseudostateKind.EXIT_POINT_LITERAL))) { return UnexecutableCommand.INSTANCE; } } // Manage Element drop in compartment Boolean isCompartmentTarget = false; // True if the target is a ShapeCompartmentEditPart GraphicalEditPart graphicalParentEditPart = (GraphicalEditPart)getHost(); // Default drop location Point dropLocation = dropRequest.getLocation().getCopy(); // Detect if the drop target is a compartment if(graphicalParentEditPart instanceof RegionCompartmentEditPart) { isCompartmentTarget = true; RegionFigure regionFigure = (RegionFigure)((RegionEditPart)graphicalParentEditPart.getParent()).getPrimaryShape(); // Replace compartment edit part by its ancestor StateMachineEditPart or StateEditPart graphicalParentEditPart = (GraphicalEditPart)graphicalParentEditPart.getParent().getParent().getParent(); dropLocation.translate(regionFigure.getLocation()); if(graphicalParentEditPart instanceof StateMachineEditPart) { regionFigure.translateToAbsolute(dropLocation); } } else if(graphicalParentEditPart instanceof StateCompartmentEditPart) { isCompartmentTarget = true; // Replace compartment edit part by its ancestor StateMachineEditPart or StateEditPart graphicalParentEditPart = (GraphicalEditPart)graphicalParentEditPart.getParent(); } // Manage Element drop in compartment // Create proposed creation bounds and use the locator to find the expected position Point parentLoc = graphicalParentEditPart.getFigure().getBounds().getLocation().getCopy(); CustomEntryExitPointPositionLocator locator = new CustomEntryExitPointPositionLocator(graphicalParentEditPart.getFigure(), PositionConstants.NONE); Rectangle proposedBounds = new Rectangle(dropLocation, new Dimension(20, 20)); Rectangle preferredBounds = locator.getPreferredLocation(proposedBounds); // Convert the calculated preferred bounds as relative to parent location Rectangle creationBounds = preferredBounds.getTranslated(parentLoc.getNegated()); if(graphicalParentEditPart instanceof StateMachineEditPart) { dropLocation = creationBounds.getLocation(); } EObject graphicalParentObject = graphicalParentEditPart.resolveSemanticElement(); if((graphicalParentObject instanceof StateMachine) && (((StateMachine)graphicalParentObject).getConnectionPoints().contains((Pseudostate)droppedElement))) { // Drop Pseudostate on StateMachine if(isCompartmentTarget) { return getDropAffixedNodeInCompartmentCommand(nodeVISUALID, dropLocation, droppedElement); } return new ICommandProxy(getDefaultDropNodeCommand(nodeVISUALID, dropLocation, droppedElement)); } if(graphicalParentObject instanceof State) { if((droppedElement instanceof Pseudostate) && (((State)graphicalParentObject).getConnectionPoints().contains((Pseudostate)droppedElement)) || (droppedElement instanceof ConnectionPointReference) && (((State)graphicalParentObject).getConnections().contains((ConnectionPointReference)droppedElement))) { // Drop Pseudostate or ConnectionPointReference on State if(isCompartmentTarget) { return getDropAffixedNodeInCompartmentCommand(nodeVISUALID, dropLocation, droppedElement); } return new ICommandProxy(getDefaultDropNodeCommand(nodeVISUALID, dropLocation, droppedElement)); } } return UnexecutableCommand.INSTANCE; } /** * Returns the drop command for StateMachine nodes. * * @param dropRequest * the drop request * @param location * the location to drop the element * @param droppedElement * the element to drop * @param nodeVISUALID * the visual identifier of the EditPart of the dropped element * @return the drop command */ protected Command dropRegion(DropObjectsRequest dropRequest, Region droppedElement, int nodeVISUALID) { GraphicalEditPart graphicalParentEditPart = (GraphicalEditPart)getHost(); EObject graphicalParentObject = graphicalParentEditPart.resolveSemanticElement(); if(graphicalParentObject instanceof Region) { Region region = (Region)graphicalParentObject; if(((region.getStateMachine() != null) && region.getStateMachine().getRegions().contains(droppedElement) && !region.equals(droppedElement)) || ((region.getState() != null) && region.getState().getRegions().contains(droppedElement) && !region.equals(droppedElement))) { CompositeCommand cc = new CompositeCommand("Drop"); // get an adaptable for the dropped region IAdaptable adaptableForDroppedRegion = (IAdaptable)new SemanticAdapter(droppedElement, null); // get the existing region view View existingRegionView = (View)graphicalParentEditPart.getParent().getModel(); // get and adaptable for it, to pass on to commands IAdaptable adaptableForExistingRegionView = (IAdaptable)new SemanticAdapter(null, existingRegionView); // check whether the dropped region is already shown in the state // machine or state compartment View compartment = (View)existingRegionView.eContainer(); View alreadyShown = null; Iterator<View> it = compartment.getChildren().iterator(); while((alreadyShown == null) && it.hasNext()) { View current = it.next(); if(current.getElement().equals(droppedElement)) { alreadyShown = current; } } if(alreadyShown != null) { if(fromOutline) { return UnexecutableCommand.INSTANCE; } IAdaptable adaptableForRegionToMove = (IAdaptable)new SemanticAdapter(null, alreadyShown); // specific command to move the already shown region CustomRegionMoveCommand moveCommand = new CustomRegionMoveCommand(adaptableForExistingRegionView, adaptableForRegionToMove, ((IGraphicalEditPart)getHost()).getDiagramPreferencesHint(), getEditingDomain(), DiagramUIMessages.CreateCommand_Label, dropLocation); cc.compose(moveCommand); } else { // do the whole job CustomRegionCreateElementCommand createNewRegion = new CustomRegionCreateElementCommand(adaptableForExistingRegionView, adaptableForDroppedRegion, ((IGraphicalEditPart)getHost()).getDiagramPreferencesHint(), getEditingDomain(), DiagramUIMessages.CreateCommand_Label, dropLocation); cc.compose(createNewRegion); } return new ICommandProxy(cc.reduce()); } } else if(graphicalParentObject instanceof State) { State state = (State)graphicalParentObject; if(state.getRegions().contains(droppedElement)) { // get the state view View stateView = (View)graphicalParentEditPart.getModel(); // check whether any region is already shown in the state compartment if(stateView.getChildren().size() < 2) return UnexecutableCommand.INSTANCE; View compartment = (View)stateView.getChildren().get(1); if(!compartment.getChildren().isEmpty()) //then do not allow the drag and drop on state, this forces the drag and drop on an displayed region (see above) return UnexecutableCommand.INSTANCE; CompositeCommand cc = new CompositeCommand("Drop"); // get an adaptable for the dropped region IAdaptable adaptableForDroppedRegion = (IAdaptable)new SemanticAdapter(droppedElement, null); // get and adaptable for the compartmentView, to pass on to commands IAdaptable adaptableForCompartment = (IAdaptable)new SemanticAdapter(null, compartment); // do the whole job CustomFirstRegionInCompositeStateCreateElementCommand createNewRegion = new CustomFirstRegionInCompositeStateCreateElementCommand(adaptableForCompartment, adaptableForDroppedRegion, ((IGraphicalEditPart)getHost()).getDiagramPreferencesHint(), getEditingDomain(), DiagramUIMessages.CreateCommand_Label, dropLocation); SetPropertyCommand showCompartment = new SetPropertyCommand(getEditingDomain(), adaptableForCompartment, "notation.View.visible", "Visibility", true); cc.compose(createNewRegion); cc.compose(showCompartment); return new ICommandProxy(cc.reduce()); } } return UnexecutableCommand.INSTANCE; } /** * Returns the drop command for StateMachine nodes. * * @param dropRequest * the drop request * @param location * the location to drop the element * @param droppedElement * the element to drop * @param nodeVISUALID * the visual identifier of the EditPart of the dropped element * @return the drop command */ protected Command dropStateMachine(DropObjectsRequest dropRequest, Point location, StateMachine droppedElement, int nodeVISUALID) { GraphicalEditPart graphicalParentEditPart = (GraphicalEditPart)getHost(); EObject graphicalParentObject = graphicalParentEditPart.resolveSemanticElement(); if((graphicalParentObject instanceof org.eclipse.uml2.uml.StateMachine)) { CompositeCommand cc = new CompositeCommand("Drop"); IAdaptable elementAdapter = new EObjectAdapter(droppedElement); ViewDescriptor descriptor = new ViewDescriptor(elementAdapter, Node.class, ((IHintedType)getUMLElementType(nodeVISUALID)).getSemanticHint(), ViewUtil.APPEND, false, getDiagramPreferencesHint()); CreateCommand createStateMachine = new CreateCommand(getEditingDomain(), descriptor, (View)(getHost().getModel())); CustomStateMachineWithDefaultRegionCreateNodeCommand createRegion = new CustomStateMachineWithDefaultRegionCreateNodeCommand((IAdaptable)createStateMachine.getCommandResult().getReturnValue(), ((IGraphicalEditPart)getHost()).getDiagramPreferencesHint(), getEditingDomain(), DiagramUIMessages.CreateCommand_Label, createStateMachine.getAffectedFiles()); CustomStateMachineSetBoundsCommand setBoundsCommand = new CustomStateMachineSetBoundsCommand(getEditingDomain(), null, descriptor, new Rectangle(location.x, location.y, -1, -1)); cc.compose(createStateMachine); cc.compose(createRegion); cc.compose(setBoundsCommand); return new ICommandProxy(cc.reduce()); } return UnexecutableCommand.INSTANCE; } /** * Returns the drop command for State nodes. * * @param dropRequest * the drop request * @param location * the location to drop the element * @param droppedElement * the element to drop * @param nodeVISUALID * the visual identifier of the EditPart of the dropped element * @return the drop command */ protected Command dropState(DropObjectsRequest dropRequest, Point location, State droppedElement, int nodeVISUALID) { GraphicalEditPart graphicalParentEditPart = (GraphicalEditPart)getHost(); EObject graphicalParentObject = graphicalParentEditPart.resolveSemanticElement(); if((graphicalParentObject instanceof org.eclipse.uml2.uml.Region) && droppedElement.eContainer().equals(graphicalParentObject)) { CompositeCommand cc = new CompositeCommand("Drop"); IAdaptable elementAdapter = new EObjectAdapter(droppedElement); ViewDescriptor descriptor = new ViewDescriptor(elementAdapter, Node.class, ((IHintedType)getUMLElementType(nodeVISUALID)).getSemanticHint(), ViewUtil.APPEND, false, getDiagramPreferencesHint()); CreateCommand createState = new CreateCommand(getEditingDomain(), descriptor, (View)(getHost().getModel())); CustomCompositeStateWithDefaultRegionCreateNodeCommand createRegion = new CustomCompositeStateWithDefaultRegionCreateNodeCommand((IAdaptable)createState.getCommandResult().getReturnValue(), ((IGraphicalEditPart)getHost()).getDiagramPreferencesHint(), getEditingDomain(), DiagramUIMessages.CreateCommand_Label, createState.getAffectedFiles()); CustomCompositeStateSetBoundsCommand setBoundsCommand; cc.compose(createState); cc.compose(createRegion); //take care of the case when a simple state is dropped, then we should provide a reasonable size if(droppedElement.getRegions().isEmpty()) { setBoundsCommand = new CustomCompositeStateSetBoundsCommand(getEditingDomain(), null, descriptor, new Rectangle(location.x, location.y, 40, 40), false); cc.compose(setBoundsCommand); } else { setBoundsCommand = new CustomCompositeStateSetBoundsCommand(getEditingDomain(), null, descriptor, new Rectangle(location.x, location.y, -1, -1), true); cc.compose(setBoundsCommand); //force compartment to be shown SetPropertyCommand showCompartment = new SetPropertyCommand(getEditingDomain(), (IAdaptable)createState.getCommandResult().getReturnValue(), "notation.View.visible", "Visibility", true) { protected CommandResult doExecuteWithResult(IProgressMonitor progressMonitor, IAdaptable info) throws ExecutionException { View view = (View)getViewAdapter().getAdapter(View.class); if((view != null) && (view.getChildren().size() >= 2) && (view.getChildren().get(1) != null)) { ENamedElement namedElement = PackageUtil.getElement((String)getPropertyId()); if(namedElement instanceof EStructuralFeature) ViewUtil.setStructuralFeatureValue((View)view.getChildren().get(1), (EStructuralFeature)namedElement, getNewValue()); } return CommandResult.newOKCommandResult(); } }; cc.compose(showCompartment); } return new ICommandProxy(cc.reduce()); } return UnexecutableCommand.INSTANCE; } /** * Returns the drop command for Transition links. * * @param dropRequest * the drop request * @param semanticLink * the element to drop * @param linkVISUALID * the visual identifier of the EditPart of the dropped element * @return the drop command */ protected Command dropTransition(DropObjectsRequest dropRequest, Transition droppedElement, int linkVISUALID) { //we restrict drop to be over the owning region GraphicalEditPart graphicalParentEditPart = (GraphicalEditPart)getHost(); EObject graphicalParentObject = graphicalParentEditPart.resolveSemanticElement(); if(!(graphicalParentObject instanceof Region) || !((Region)graphicalParentObject).getTransitions().contains(droppedElement)) { // if it's not over the owing region then the transition might be a internal transition in dropping in the Internal compartment region if(TransitionKind.INTERNAL == droppedElement.getKind().getValue()) { return dropInternalTransition(dropRequest, droppedElement, getNodeVisualID(((IGraphicalEditPart)getHost()).getNotationView(), droppedElement)); } return UnexecutableCommand.INSTANCE; } Vertex source = droppedElement.getSource(); Vertex target = droppedElement.getTarget(); if((source != null) && (target != null)) { // look for editpart GraphicalEditPart sourceEditPart = (GraphicalEditPart)lookForEditPart(source); GraphicalEditPart targetEditPart = (GraphicalEditPart)lookForEditPart(target); // when the vertex are not represented on the diagram, we look for their parents. DiagramEditPart diagram = DiagramEditPartsUtil.getDiagramEditPart(getHost()); // the parents of the vertex, we use them when the VertexEditPart are not on the diagram EditPart sourceParent = null; EditPart targetParent = null; if(sourceEditPart == null || targetEditPart == null) { List<IGraphicalEditPart> AllEP = DiagramEditPartsUtil.getAllEditParts(diagram); EObject srcParent = source.eContainer(); EObject tgtParent = target.eContainer(); for(IGraphicalEditPart iGraphicalEditPart : AllEP) { EObject object = ViewUtil.resolveSemanticElement((View)(iGraphicalEditPart).getModel());//method getHostObject if(object == srcParent && !(iGraphicalEditPart instanceof CompartmentEditPart)) { sourceParent = iGraphicalEditPart; } if(object == tgtParent && !(iGraphicalEditPart instanceof CompartmentEditPart)) { targetParent = iGraphicalEditPart; } if(targetParent != null && sourceParent != null) { sourceParent = (EditPart)sourceParent.getChildren().get(0); targetParent = (EditPart)targetParent.getChildren().get(0); break; } } // the parent of the vertex shall be present in the diagram otherwise we do not support drag and drop if((targetParent == null) || (sourceParent == null)) { return UnexecutableCommand.INSTANCE; } } return new ICommandProxy(dropBinaryLink(new CompositeCommand("drop Transition"), source, target, //$NON-NLS-1$ linkVISUALID, dropRequest.getLocation(), droppedElement, sourceParent, targetParent)); } else { return UnexecutableCommand.INSTANCE; } } /** * handle drop of transition with kindTransition set as internal * * @param dropRequest * @param droppedElement * @param nodeVisualID * @return */ protected Command dropInternalTransition(DropObjectsRequest dropRequest, Transition droppedElement, int nodeVisualID) { if(nodeVisualID != -1) { CompositeCommand cc = getDefaultDropNodeCommand(nodeVisualID, dropRequest.getLocation().getCopy(), droppedElement, dropRequest); if(cc != null) { cc.reduce(); if(!cc.isEmpty() && cc.canExecute()) { return new ICommandProxy(cc); } } } return UnexecutableCommand.INSTANCE; } /** * the method provides command to create the binary link into the diagram. If the source and the * target views do not exist, these views will be created. * * @param cc * the composite command that will contain the set of command to create the binary * link * @param source * the source the element source of the link * @param target * the target the element target of the link * @param linkVISUALID * the link VISUALID used to create the view * @param location * the location the location where the view will be be created * @param semanticLink * the semantic link that will be attached to the view * @param sourceParent * the editPart of the source parent * @param targetParent * the editPart of the target parent * * @return the composite command */ public CompositeCommand dropBinaryLink(CompositeCommand cc, Element source, Element target, int linkVISUALID, Point location, Element semanticLink, EditPart sourceParent, EditPart targetParent) { // look for editpart GraphicalEditPart sourceEditPart = (GraphicalEditPart)lookForEditPart(source); GraphicalEditPart targetEditPart = (GraphicalEditPart)lookForEditPart(target); // descriptor of the link CreateConnectionViewRequest.ConnectionViewDescriptor linkdescriptor = new CreateConnectionViewRequest.ConnectionViewDescriptor(getUMLElementType(linkVISUALID), ((IHintedType)getUMLElementType(linkVISUALID)).getSemanticHint(), getDiagramPreferencesHint()); IAdaptable sourceAdapter = null; IAdaptable targetAdapter = null; if(sourceEditPart == null) { // creation of the node ViewDescriptor descriptor = new ViewDescriptor(new EObjectAdapter(source), Node.class, null, ViewUtil.APPEND, false, ((IGraphicalEditPart)getHost()).getDiagramPreferencesHint()); // get the command and execute it. CreateCommand nodeCreationCommand = new CreateCommand(((IGraphicalEditPart)getHost()).getEditingDomain(), descriptor, (View)sourceParent.getModel()); cc.compose(nodeCreationCommand); SetBoundsCommand setBoundsCommand = new SetBoundsCommand(getEditingDomain(), "move", (IAdaptable)nodeCreationCommand.getCommandResult().getReturnValue(), new Point(location.x, location.y + 30)); //$NON-NLS-1$ cc.compose(setBoundsCommand); sourceAdapter = (IAdaptable)nodeCreationCommand.getCommandResult().getReturnValue(); } else { sourceAdapter = new SemanticAdapter(null, sourceEditPart.getModel()); } //additional check to ensure we do not create twice the same node when links are "loops" on the same element if((target != null) && !target.equals(source)) { if(targetEditPart == null) { // creation of the node ViewDescriptor descriptor = new ViewDescriptor(new EObjectAdapter(target), Node.class, null, ViewUtil.APPEND, false, ((IGraphicalEditPart)getHost()).getDiagramPreferencesHint()); // get the command and execute it. CreateCommand nodeCreationCommand = new CreateCommand(((IGraphicalEditPart)getHost()).getEditingDomain(), descriptor, ((View)targetParent.getModel())); cc.compose(nodeCreationCommand); //take care of the location for the cases when the target is not in the same container as the source SetBoundsCommand setBoundsCommand; if((targetParent != null) && !targetParent.equals(sourceParent)) { setBoundsCommand = new SetBoundsCommand(getEditingDomain(), "move", (IAdaptable)nodeCreationCommand.getCommandResult().getReturnValue(), new Point(10, 10)); //$NON-NLS-1$ } else { setBoundsCommand = new SetBoundsCommand(getEditingDomain(), "move", (IAdaptable)nodeCreationCommand.getCommandResult().getReturnValue(), new Point(location.x, location.y - 30)); //$NON-NLS-1$ } cc.compose(setBoundsCommand); targetAdapter = (IAdaptable)nodeCreationCommand.getCommandResult().getReturnValue(); } else { targetAdapter = new SemanticAdapter(null, targetEditPart.getModel()); } } //in case of loop links (see above) we pass on the same adapter for both source and target if((target != null) && target.equals(source)) { targetAdapter = sourceAdapter; } CommonDeferredCreateConnectionViewCommand aLinkCommand = new CommonDeferredCreateConnectionViewCommand(getEditingDomain(), ((IHintedType)getUMLElementType(linkVISUALID)).getSemanticHint(), sourceAdapter, targetAdapter, getViewer(), getDiagramPreferencesHint(), linkdescriptor, null); aLinkCommand.setElement(semanticLink); cc.compose(aLinkCommand); return cc; } @Override public void eraseTargetFeedback(Request request) { if(sizeOnDropFeedback != null) { removeFeedback(sizeOnDropFeedback); sizeOnDropFeedback = null; } } /** * <pre> * This method returns the drop command for AffixedNode (Pseudostate, ConnectionPointReference) * in case the node is dropped on a ShapeCompartmentEditPart. * </pre> * * @param nodeVISUALID * the node visual identifier * @param location * the drop location * @param droppedObject * the object to drop * @return a CompositeCommand for Drop */ protected CompoundCommand getDropAffixedNodeInCompartmentCommand(int nodeVISUALID, Point location, EObject droppedObject) { CompoundCommand cc = new CompoundCommand("Drop"); IAdaptable elementAdapter = new EObjectAdapter(droppedObject); ViewDescriptor descriptor = new ViewDescriptor(elementAdapter, Node.class, ((IHintedType)getUMLElementType(nodeVISUALID)).getSemanticHint(), ViewUtil.APPEND, false, getDiagramPreferencesHint()); // Create the command targeting host parent (owner of the ShapeCompartmentEditPart) CreateViewCommand createCommand = null; if(nodeVISUALID != ConnectionPointReferenceEditPart.VISUAL_ID) { createCommand = new CreateViewCommand(getEditingDomain(), descriptor, ((View)(getHost().getParent().getParent().getParent().getModel()))); cc.add(new ICommandProxy(createCommand)); } else { createCommand = new CreateViewCommand(getEditingDomain(), descriptor, ((View)(getHost().getParent().getModel()))); cc.add(new ICommandProxy(createCommand)); } SetBoundsCommand setBoundsCommand = new SetBoundsCommand(getEditingDomain(), "move", (IAdaptable)createCommand.getCommandResult().getReturnValue(), location); cc.add(new ICommandProxy(setBoundsCommand)); return cc; } /** * {@inheritDoc} */ @Override protected Set<Integer> getDroppableElementVisualId() { Set<Integer> droppableElementsVisualId = new HashSet<Integer>(); droppableElementsVisualId.add(StateMachineEditPart.VISUAL_ID); droppableElementsVisualId.add(StateEditPart.VISUAL_ID); droppableElementsVisualId.add(RegionEditPart.VISUAL_ID); droppableElementsVisualId.add(PseudostateEntryPointEditPart.VISUAL_ID); droppableElementsVisualId.add(PseudostateExitPointEditPart.VISUAL_ID); droppableElementsVisualId.add(ConnectionPointReferenceEditPart.VISUAL_ID); droppableElementsVisualId.add(TransitionEditPart.VISUAL_ID); // droppableElementsVisualId.add(EntryStateBehaviorEditPart.VISUAL_ID); return droppableElementsVisualId; } @Override public int getLinkWithClassVisualID(EObject domainElement) { return UMLVisualIDRegistry.getLinkWithClassVisualID(domainElement); } @Override public int getNodeVisualID(View containerView, EObject domainElement) { if((domainElement instanceof Region) && (containerView.getElement() instanceof Region)) { return RegionEditPart.VISUAL_ID; } return UMLVisualIDRegistry.getNodeVisualID(containerView, domainElement); } protected IFigure getSizeOnDropFeedback() { if(sizeOnDropFeedback == null) { sizeOnDropFeedback = new RectangleFigure(); FigureUtilities.makeGhostShape((Shape)sizeOnDropFeedback); ((Shape)sizeOnDropFeedback).setLineStyle(Graphics.LINE_DASHDOT); sizeOnDropFeedback.setForegroundColor(ColorConstants.white); addFeedback(sizeOnDropFeedback); } return sizeOnDropFeedback; } /** * {@inheritedDoc} */ protected Command getSpecificDropCommand(DropObjectsRequest dropRequest, Element semanticElement, int nodeVISUALID, int linkVISUALID) { // Retrieve drop location Point location = dropRequest.getLocation().getCopy(); switch(linkVISUALID) { case TransitionEditPart.VISUAL_ID: return dropTransition(dropRequest, (Transition)semanticElement, linkVISUALID); default: // Switch test over nodeVISUALID switch(nodeVISUALID) { case StateMachineEditPart.VISUAL_ID: return dropStateMachine(dropRequest, location, (StateMachine)semanticElement, nodeVISUALID); case StateEditPart.VISUAL_ID: return dropState(dropRequest, location, (State)semanticElement, nodeVISUALID); case RegionEditPart.VISUAL_ID: return dropRegion(dropRequest, (Region)semanticElement, nodeVISUALID); case PseudostateEntryPointEditPart.VISUAL_ID: case PseudostateExitPointEditPart.VISUAL_ID: case ConnectionPointReferenceEditPart.VISUAL_ID: return dropAffixedNode(dropRequest, semanticElement, nodeVISUALID); default: return super.getSpecificDropCommand(dropRequest, semanticElement, nodeVISUALID, linkVISUALID); } } } @Override public IElementType getUMLElementType(int elementID) { return UMLElementTypes.getElementType(elementID); } @Override public void showTargetFeedback(Request request) { if((request instanceof ChangeBoundsRequest) && !REQ_RESIZE.equals(request.getType())) { fromOutline = false; ChangeBoundsRequest changeBoundsRequest = (ChangeBoundsRequest)request; for(Iterator iter = changeBoundsRequest.getEditParts().iterator(); iter.hasNext();) { GraphicalEditPart element = (GraphicalEditPart)iter.next(); if(element instanceof RegionEditPart) { CustomRegionEditPart regionEditPart = (CustomRegionEditPart)element; View compartment = (View)((View)element.getModel()).eContainer(); if(compartment.getChildren().size() == 1) return; CustomRegionDragTracker dragTracker = regionEditPart.getRegionDragTracker(); RegionEditPart targetEP = dragTracker.getTargetRegionEditPart(); if(targetEP != null) { RegionFigure targetFig = targetEP.getPrimaryShape(); // make a local copy Rectangle targetFigBounds = targetFig.getBounds().getCopy(); // transform the coordinates to absolute targetFig.translateToAbsolute(targetFigBounds); // retrieve mouse location Point mouseLocation = changeBoundsRequest.getLocation(); // get the drop location, i.e. RIGHT, LEFT, TOP, BOTTOM dropLocation = Zone.getZoneFromLocationInRectangleWithAbsoluteCoordinates(mouseLocation, targetFigBounds); // perform corresponding change (scaling, translation) // on // targetFigBounds // and updates the graph node drop location property if(Zone.isTop(dropLocation)) { targetFigBounds.setSize(targetFigBounds.getSize().scale(1.0, 0.5)); } else if(Zone.isLeft(dropLocation)) { targetFigBounds.setSize(targetFigBounds.getSize().scale(0.5, 1.0)); } else if(Zone.isRight(dropLocation)) { targetFigBounds.setSize(targetFigBounds.getSize().scale(0.5, 1.0)); targetFigBounds.translate(targetFigBounds.width, 0); } else if(Zone.isBottom(dropLocation)) { targetFigBounds.setSize(targetFigBounds.getSize().scale(1.0, 0.5)); targetFigBounds.translate(0, targetFigBounds.height); } getSizeOnDropFeedback().setBounds(new PrecisionRectangle(targetFigBounds)); } else { Rectangle targetFigBounds = dragTracker.getRegionFigureBounds(); // retrieve mouse move Point mouseMove = changeBoundsRequest.getMoveDelta(); targetFigBounds.translate(mouseMove); getSizeOnDropFeedback().setBounds(new PrecisionRectangle(targetFigBounds)); } } } } if(request instanceof DropObjectsRequest) { fromOutline = true; DropObjectsRequest dropRequest = (DropObjectsRequest)request; for(Iterator iter = dropRequest.getObjects().iterator(); iter.hasNext();) { EObject element = (EObject)iter.next(); if((element instanceof Region) && (getHost().getParent() instanceof RegionEditPart)) { // check whether the dropped region is already shown in the // state machine View compartment = null; if(getHost().getParent().getParent() instanceof StateMachineCompartmentEditPart) compartment = (View)((StateMachineCompartmentEditPart)getHost().getParent().getParent()).getModel(); else if(getHost().getParent().getParent() instanceof StateCompartmentEditPart) compartment = (View)((StateCompartmentEditPart)getHost().getParent().getParent()).getModel(); View alreadyShown = null; Iterator<View> it = compartment.getChildren().iterator(); while((alreadyShown == null) && it.hasNext()) { View current = it.next(); if(current.getElement().equals(element)) { alreadyShown = current; } } if(alreadyShown == null) { RegionFigure targetFig = ((RegionEditPart)getHost().getParent()).getPrimaryShape(); // make a local copy Rectangle targetFigBounds = targetFig.getBounds().getCopy(); // transform the coordinates to absolute targetFig.translateToAbsolute(targetFigBounds); // retrieve mouse location Point mouseLocation = dropRequest.getLocation().getCopy(); // get the drop location, i.e. RIGHT, LEFT, TOP, BOTTOM dropLocation = Zone.getZoneFromLocationInRectangleWithAbsoluteCoordinates(mouseLocation, targetFigBounds); // perform corresponding change (scaling, translation) // on // targetFigBounds // and updates the graph node drop location property if(Zone.isTop(dropLocation)) { targetFigBounds.setSize(targetFigBounds.getSize().scale(1.0, 0.5)); } else if(Zone.isLeft(dropLocation)) { targetFigBounds.setSize(targetFigBounds.getSize().scale(0.5, 1.0)); } else if(Zone.isRight(dropLocation)) { targetFigBounds.setSize(targetFigBounds.getSize().scale(0.5, 1.0)); targetFigBounds.translate(targetFigBounds.width, 0); } else if(Zone.isBottom(dropLocation)) { targetFigBounds.setSize(targetFigBounds.getSize().scale(1.0, 0.5)); targetFigBounds.translate(0, targetFigBounds.height); } getSizeOnDropFeedback().setBounds(new PrecisionRectangle(targetFigBounds)); } } } } } }