/*****************************************************************************
* Copyright (c) 2010 Atos Origin.
*
*
* 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:
* Atos Origin - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.uml.diagram.common.groups.edit.policies;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.gmf.runtime.common.core.command.CompositeCommand;
import org.eclipse.gmf.runtime.diagram.ui.commands.CommandProxy;
import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy;
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.editpolicies.XYLayoutEditPolicy;
import org.eclipse.papyrus.uml.diagram.common.groups.commands.ChangeGraphicalParentCommand;
import org.eclipse.papyrus.uml.diagram.common.groups.commands.ChangeGraphicalParentCommand.Mode;
import org.eclipse.papyrus.uml.diagram.common.groups.commands.ChangeModelParentCommand;
import org.eclipse.papyrus.uml.diagram.common.groups.commands.SetUpReferencesCommand;
import org.eclipse.papyrus.uml.diagram.common.groups.commands.UpdateReferencesCommand;
import org.eclipse.papyrus.uml.diagram.common.groups.commands.utlis.CommandsUtils;
import org.eclipse.papyrus.uml.diagram.common.groups.core.groupcontainment.GroupContainmentRegistry;
import org.eclipse.papyrus.uml.diagram.common.groups.core.utils.DefaultModelParent;
import org.eclipse.papyrus.uml.diagram.common.groups.core.utils.Utils;
import org.eclipse.papyrus.uml.diagram.common.groups.groupcontainment.AbstractContainerNodeDescriptor;
import org.eclipse.papyrus.uml.diagram.common.groups.utils.GroupRequestConstants;
import org.eclipse.papyrus.uml.diagram.common.util.DiagramEditPartsUtil;
/**
* This edit policy is used to handle node positioning inside a group after creation or after a {@link ChangeBoundsRequest} You can find example of
* uses of this policy in {@link org.eclipse.papyrus.uml.diagram.activity.edit.policies.CompartmentXYLayoutEditPolicy} or in
* {@link org.eclipse.papyrus.uml.diagram.activity.edit.parts.ActivityPartitionActivityPartitionContentCompartmentEditPart}
*
* @author arthur daussy
*/
public class XYLayoutEditGroupPolicy extends XYLayoutEditPolicy {
/**
* graphical parent of the edit part after moving
*/
private ArrayList<IGraphicalEditPart> graphicalParents;
/**
* Model parent of the edit part after moving
*/
private ArrayList<IGraphicalEditPart> modelParents;
/**
* Graphical parent of the edit part before moving
*/
private ArrayList<IGraphicalEditPart> oldGraphicalParents;
/**
* Model parent of the edit part before moving
*/
private ArrayList<IGraphicalEditPart> oldModelParents;
/**
* override this method in order to take into account the difference between graphical parent and model parent.
* If there are different there is a delta to compute and the figure has to be translated.
*
* @see org.eclipse.gmf.runtime.diagram.ui.editpolicies.XYLayoutEditPolicy#getConstraintFor(org.eclipse.gef.requests.CreateRequest)
* @param request
* @return
*/
@Override
protected Object getConstraintFor(CreateRequest request) {
if(request.getExtendedData().containsKey(CommandsUtils.GRAPHICAL_PARENT) && request.getExtendedData().containsKey(CommandsUtils.NEW_PARENT_HOST)) {
IGraphicalEditPart graphicalParent = (IGraphicalEditPart)request.getExtendedData().get(CommandsUtils.GRAPHICAL_PARENT);
IGraphicalEditPart newHost = (IGraphicalEditPart)request.getExtendedData().get(CommandsUtils.NEW_PARENT_HOST);
Dimension delta = Utils.computeDeltaToChangeParent(newHost, graphicalParent);
Rectangle rectangle = (Rectangle)super.getConstraintFor(request);
rectangle.translate(delta.width, delta.height);
return (Object)rectangle;
}
return super.getConstraintFor(request);
}
/**
* Override the super method in order to disable the command if the command has already been created on another edit part
*
* @see org.eclipse.gmf.runtime.diagram.ui.editpolicies.XYLayoutEditPolicy#getCommand(org.eclipse.gef.Request)
*
* @param request
* Global Request
* @return Command
*/
@Override
public Command getCommand(Request request) {
if(understandsRequest(request)) {
return super.getCommand(request);
}
return null;
}
/**
* Return the resize command chained with the group-related commands if needed
*
* @param request
* the change bounds request
*/
@Override
protected Command getResizeChildrenCommand(ChangeBoundsRequest request) {
Command superCommand = super.getResizeChildrenCommand(request);
if(superCommand != null) {
String label = superCommand.getLabel();
CompositeCommand commandWrapper = new CompositeCommand(label);
/*
* If there is several edit part affected by the request multiplex the request
*/
Command multiplexor = CommandsUtils.requestEditPartMultiplexor(request, label, (IGraphicalEditPart)getHost());
if(multiplexor != null) {
return multiplexor;
}
String labelHandleChildren = "XYLayout : Handle children";
//Thanks to requestEditPartMultiplexor its sure we only have only one editPart
Object _movingEditPart = request.getEditParts().get(0);
if(_movingEditPart instanceof IGraphicalEditPart) {
IGraphicalEditPart movingEditPart = (IGraphicalEditPart)_movingEditPart;
IGraphicalEditPart movingCompartmentEditPart = (IGraphicalEditPart)Utils.getCompartementEditPartFromMainEditPart(((IGraphicalEditPart)getHost()).getViewer().getEditPartRegistry(), movingEditPart);
if(movingCompartmentEditPart == null) {
movingCompartmentEditPart = movingEditPart;
}
/*
* Find the moving compartment editPart
*/
TransactionalEditingDomain editingDomain = ((GraphicalEditPart)getHost()).getEditingDomain();
/*
* Only handle element which are concerned by the framework else return the super method
*/
boolean isNodeConcernedByGroupFramework = GroupContainmentRegistry.isNodeConcerned(movingCompartmentEditPart);
boolean isGroupConcernedByGroupFramework = GroupContainmentRegistry.isContainerConcerned(movingCompartmentEditPart);
if(isNodeConcernedByGroupFramework || isGroupConcernedByGroupFramework) {
/*
* Handling parents
*/
DiagramEditPart diagramPart = DiagramEditPartsUtil.getDiagramEditPart(movingEditPart);
graphicalParents = new ArrayList<IGraphicalEditPart>();
modelParents = new ArrayList<IGraphicalEditPart>();
Utils.createComputedListsOfParents(graphicalParents, modelParents, movingEditPart, request, true);
oldGraphicalParents = new ArrayList<IGraphicalEditPart>();
oldModelParents = new ArrayList<IGraphicalEditPart>();
Utils.createComputedListsOfParents(oldGraphicalParents, oldModelParents, movingEditPart, request, false);
IGraphicalEditPart graphicalParent = CommandsUtils.setRequestParentsParameters(request, graphicalParents, modelParents, getHost());
/*
* The the original position of the element
*/
setOriginalAbsolutePositionPosition(request, movingEditPart);
//If the system has found model parent
Command relocateCommand = CommandsUtils.sendRequestSuitableHost(request, (IGraphicalEditPart)getHost(), modelParents, movingEditPart);
if(relocateCommand != null) {
return relocateCommand;
}
/*
* Execute the change bound request
*/
commandWrapper.compose(new CommandProxy(superCommand));
/*
* Update model
*/
updateModel(commandWrapper, movingEditPart, editingDomain);
/*
* Update the references
* 1 - Set up new references
* 2 - Withdraw old references
*/
updateMovingElementReferences(label, commandWrapper, movingEditPart, editingDomain);
/*
* Then change the graphical parent
*/
CommandsUtils.getChangeGraphicalParentCommand(label, commandWrapper, movingEditPart, editingDomain, graphicalParent, (IGraphicalEditPart)getHost(), request);
/*
* If the change bounds element is a group
*/
if(isGroupConcernedByGroupFramework) {
Command handleChildren = getHandleChildren(request, labelHandleChildren, movingEditPart, diagramPart, movingCompartmentEditPart, editingDomain);
if(handleChildren != null) {
commandWrapper.compose(new CommandProxy(handleChildren));
}
}
/*
* ChooseParentNotificationCommand
*/
CompositeCommand choiceCommand = CommandsUtils.getChooseParentNotification(editingDomain, request, graphicalParents, modelParents, (IGraphicalEditPart)getHost());
if(choiceCommand != null) {
commandWrapper.compose(choiceCommand);
}
return new ICommandProxy(commandWrapper);
} else {
return superCommand;
}
}
}
return superCommand;
}
/**
* Update the references of the movingElement
*
* @param label
* Label of the command
* @param commandWrapper
* {@link CompositeCommand} in which all the command will be composed ( warning should be initialised before)
* @param labelHandleChildren
* @param movingEditPart
* @param editingDomain
*/
private void updateMovingElementReferences(String label, CompositeCommand commandWrapper, IGraphicalEditPart movingEditPart, TransactionalEditingDomain editingDomain) {
SetUpReferencesCommand setUpReferences = new SetUpReferencesCommand(editingDomain, label + ": set up references", movingEditPart, graphicalParents);
if(setUpReferences != null) {
commandWrapper.compose(setUpReferences);
}
for(IGraphicalEditPart oldParent : oldGraphicalParents) {
if(!graphicalParents.contains(oldParent)) {
IGraphicalEditPart oldParentCompartmentEditPart = (IGraphicalEditPart)Utils.getCompartementEditPartFromMainEditPart(oldParent.getViewer().getEditPartRegistry(), oldParent);
AbstractContainerNodeDescriptor descriptor = GroupContainmentRegistry.getContainerDescriptor(oldParentCompartmentEditPart);
UpdateReferencesCommand withDrawReference = new UpdateReferencesCommand(editingDomain, label + ": withdraw old references", Collections.singletonList(movingEditPart), descriptor, oldParent, UpdateReferencesCommand.UNSET_MODE);
if(withDrawReference != null) {
commandWrapper.compose(withDrawReference);
}
}
}
}
/**
* This model update the model parent of the moving node
*
* @param commandWrapper
* Resulting command in wich the new command should be composed (should not be null)
* @param movingEditPart
* {@link IGraphicalEditPart} of the moving element
* @param editingDomain
* {@link EditingDomain}
*/
private void updateModel(CompositeCommand commandWrapper, IGraphicalEditPart movingEditPart, TransactionalEditingDomain editingDomain) {
EditPart host = getHost();
//CHANGE
if(!movingEditPart.getParent().equals(host)) {
if(host != null) {
EditPart compartmentEditPartHost = Utils.getCompartementEditPartFromMainEditPart(movingEditPart.getViewer().getEditPartRegistry(), host);
if(compartmentEditPartHost instanceof IGraphicalEditPart) {
AbstractContainerNodeDescriptor group = GroupContainmentRegistry.getContainerDescriptor((IGraphicalEditPart)compartmentEditPartHost);
EReference ref = null;
Map<EObject, EReference> child = null;
EObject childEObject = movingEditPart.resolveSemanticElement();
if(group != null) {
EObject movingEObject = childEObject;
if(movingEObject != null) {
if(group.canIBeModelParentOf(movingEObject.eClass())) {
ref = group.getContainmentReferenceFor(movingEObject.eClass());
child = Collections.singletonMap(movingEObject, ref);
}
}
} else {
DefaultModelParent defaultModelContainer = Utils.getDefaultModelParent(childEObject.eClass(), (IGraphicalEditPart)getHost());
ref = defaultModelContainer.geteReference();
child = Collections.singletonMap(childEObject, defaultModelContainer.geteReference());
}
if(ref != null && child != null) {
ChangeModelParentCommand changeModelParent = new ChangeModelParentCommand(editingDomain, compartmentEditPartHost, child, movingEditPart);
if(changeModelParent != null) {
commandWrapper.compose(changeModelParent);
}
}
}
}
}
}
/**
* Set in the request the absolute coordinate of the moving element
*
* @param request
* @param movingEditPart
*/
@SuppressWarnings("unchecked")
private void setOriginalAbsolutePositionPosition(ChangeBoundsRequest request, IGraphicalEditPart movingEditPart) {
//if(!request.getExtendedData().containsKey(GroupRequestConstants.CONSTRAINT_AFTER_MOVING) && movingEditPart instanceof IGraphicalEditPart) {
if(movingEditPart instanceof IGraphicalEditPart) {
Rectangle bounds = ((Rectangle)getConstraintFor(request, movingEditPart)).getCopy();
translateFromLayoutRelativeToAbsolute(bounds);
request.getExtendedData().put(GroupRequestConstants.CONSTRAINT_AFTER_MOVING, bounds);
}
}
/**
* Command to handle all children of the moving element
*
* @param request
* The {@link ChangeBoundsRequest}
* @param label
* A label fro the request
* @param movingEditPart
* The {@link IGraphicalEditPart} of the moving element
* @param diagramPart
* {@link IGraphicalEditPart} of the diagram
* @param movingCompartmentEditPart
* The compartment {@link IGraphicalEditPart} of the moving node
* @param editingDomain
* {@link EditingDomain}
* @param compartmentMovingEditPart
* @return the resulting command or null if nothing to do
*/
private Command getHandleChildren(ChangeBoundsRequest request, String label, IGraphicalEditPart movingEditPart, DiagramEditPart diagramPart, IGraphicalEditPart movingCompartmentEditPart, TransactionalEditingDomain editingDomain) {
CompositeCommand result = new CompositeCommand(label);
if(movingCompartmentEditPart != null && GroupContainmentRegistry.isContainerConcerned(movingCompartmentEditPart)) {
CompositeCommand handleChildren = null;
/*
* handling sons if the creation is a group
*/
Set<AbstractContainerNodeDescriptor> descriptors = GroupContainmentRegistry.getDescriptorsWithContainerEClass(movingEditPart.resolveSemanticElement().eClass());
handleChildren = CommandsUtils.getHandleChildrenCommand(descriptors, request, diagramPart, editingDomain, movingEditPart, modelParents, (IGraphicalEditPart)getHost());
if(handleChildren != null) {
result.compose(handleChildren);
}
/*
* Change the the graphical parent of the GroupRequestConstants.GRAPHICAL_CHILDREN) set in the getHandleChildrenCommand
*/
if(request.getExtendedData().containsKey(GroupRequestConstants.GRAPHICAL_CHILDREN)) {
List<IGraphicalEditPart> automaticChildren = (List<IGraphicalEditPart>)request.getExtendedData().get(GroupRequestConstants.GRAPHICAL_CHILDREN);
for(IGraphicalEditPart child : automaticChildren) {
if(child instanceof IGraphicalEditPart) {
ChangeGraphicalParentCommand cmd = new ChangeGraphicalParentCommand(editingDomain, label, movingEditPart, child, (IGraphicalEditPart)getHost(), request);
if(cmd != null) {
cmd.setMode(Mode.MOVE_PARENT);
result.compose(cmd);
}
}
}
}
result.reduce();
if(!result.isEmpty()) {
return new ICommandProxy(result);
} else {
return null;
}
}
return null;
}
/**
* Override to be able to prevent several execution of the CreateCommand
* The Create command will only be used if the new host parent is in the request
*
* @see org.eclipse.gef.editpolicies.AbstractEditPolicy#understandsRequest(org.eclipse.gef.Request)
*
* @param req
* Global request
* @return
*/
@Override
public boolean understandsRequest(Request req) {
return canHandleCreateRequestOnlyIfNewParent(req, (IGraphicalEditPart)getHost());
}
/**
* Return true except if the request is a create request in which {@link CreateInGroupEditPolicy#OLD_PARENT} and
* {@link CommandsUtils#NEW_PARENT_HOST} are set and are different
*
* @param req
* The global request
* @param getHost
* The host on the edit policy
* @return True if can execute
*/
public static boolean canHandleCreateRequestOnlyIfNewParent(Request req, IGraphicalEditPart getHost) {
//If there is a NEW_PARENT_HOST data then its means than the command has been asked to another edit part before
// If this host is the new host then it executes the create command
// Else do nothing
//Else the command will not be asked to another EditPart so I have to execute it
if(req.getExtendedData().containsKey(CommandsUtils.NEW_PARENT_HOST)) {
IGraphicalEditPart newParent = (IGraphicalEditPart)req.getExtendedData().get(CommandsUtils.NEW_PARENT_HOST);
if(((getHost).equals(newParent))) {
return true;
} else {
return false;
}
}
return true;
}
/**
* Get the command to response of CreateRequest request.
* Override in order to set the graphical children of the created element
*
* @see org.eclipse.gmf.runtime.diagram.ui.editpolicies.XYLayoutEditPolicy#getCreateCommand(org.eclipse.gef.requests.CreateRequest)
*
* @param request
* @return
*/
@Override
protected Command getCreateCommand(CreateRequest request) {
if(request.getExtendedData().containsKey(GroupRequestConstants.GRAPHICAL_CHILDREN)) {
TransactionalEditingDomain editingDomain = ((IGraphicalEditPart)getHost()).getEditingDomain();
Command superCreateCommand = super.getCreateCommand(request);
CompositeCommand compositeCreateCmd = new CompositeCommand(superCreateCommand.getLabel());
compositeCreateCmd.compose(new CommandProxy(superCreateCommand));
List<IGraphicalEditPart> automaticChildren = (List<IGraphicalEditPart>)request.getExtendedData().get(GroupRequestConstants.GRAPHICAL_CHILDREN);
for(IGraphicalEditPart child : automaticChildren) {
if(child instanceof IGraphicalEditPart) {
String label = superCreateCommand + ": Change child graphical parent";
ChangeGraphicalParentCommand cmd = new ChangeGraphicalParentCommand(editingDomain, label, request, child, (IGraphicalEditPart)getHost());
if(cmd != null) {
cmd.setMode(Mode.CREATION_CHILD);
compositeCreateCmd.compose(cmd);
}
}
}
return new ICommandProxy(compositeCreateCmd);
}
return super.getCreateCommand(request);
}
}