/*****************************************************************************
* Copyright (c) 2011 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.commands;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.Request;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.gmf.runtime.common.core.command.CommandResult;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewAndElementRequest;
import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest;
import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand;
import org.eclipse.gmf.runtime.notation.LayoutConstraint;
import org.eclipse.gmf.runtime.notation.Location;
import org.eclipse.gmf.runtime.notation.Node;
import org.eclipse.gmf.runtime.notation.View;
import org.eclipse.papyrus.uml.diagram.common.groups.core.utils.Utils;
import org.eclipse.papyrus.uml.diagram.common.groups.edit.policies.XYLayoutEditGroupPolicy;
import org.eclipse.papyrus.uml.diagram.common.groups.utils.GroupRequestConstants;
/**
* This command change the graphical parent of an edit part without changing the absolute position of the element
*
* @author arthur daussy
*
*/
public class ChangeGraphicalParentCommand extends AbstractTransactionalCommand {
public enum Mode {
/**
* Case when the parent is in creation.
*/
CREATION_PARENT,
/**
* Case when the child is in creation
*/
CREATION_CHILD,
/**
* Case when the child is moving (ChangeBoundsRequest)
*/
MOVE_CHILD,
/**
* case when the parent is moving
*/
MOVE_PARENT,
/**
* Case when there is no need of a request. All elements has been placed annd all element ared fixed
*/
NORMAL
}
/** child part */
private EditPart child;
/** parent part */
private EditPart parent;
private Request request;
private IGraphicalEditPart host;
/**
* Current mode @see #mode
*/
private Mode mode;
/**
*
* Command constructor.
* Create a command from to existing EditPart which exist at the time of the creation of the command
*
* @param domain
* editing domain
* @param label
* command label
* @param parent
* new parent edit part
* @param child
* child edit part to reroute parent
*/
public ChangeGraphicalParentCommand(TransactionalEditingDomain domain, String label, EditPart parent, EditPart child, IGraphicalEditPart getHost) {
super(domain, label, null);
this.parent = parent;
this.child = child;
this.request = null;
this.host = getHost;
this.mode = Mode.NORMAL;
}
/**
*
* Command constructor.
* Create a command from to existing EditPart which exist at the time of the creation of the command.
* In this constructor the request is needed if any translation has to be made (e.g {@link ChangeBoundsRequest} @see
* {@link XYLayoutEditGroupPolicy#getCommand(Request)}
*
* @param domain
* editing domain
* @param label
* command label
* @param parent
* new parent edit part
* @param child
* child edit part to reroute parent
*/
public ChangeGraphicalParentCommand(TransactionalEditingDomain domain, String label, EditPart parent, EditPart child, IGraphicalEditPart getHost, Request request) {
super(domain, label, null);
this.parent = parent;
this.child = child;
this.request = request;
this.host = getHost;
this.mode = Mode.MOVE_PARENT;
}
/**
*
* Constructor.
*
* @param domain
* editing domain
* @param label
* command label
* @param request
* Create element request to be able to find the IGraphicalEditPart of the create element
* ( WARNING the IGraphicalEditPart has to be already created at the execution time of this command and it has to be have it's bounds set)
* @param child
* IGraphicalEditPart of the child (has to be available a the creation time of the request)
*/
public ChangeGraphicalParentCommand(TransactionalEditingDomain domain, String label, CreateRequest request, EditPart child, IGraphicalEditPart getHost) {
super(domain, label, null);
this.parent = null;
this.child = child;
this.request = (CreateViewAndElementRequest)request;
this.host = getHost;
this.mode = Mode.CREATION_PARENT;
}
/**
* This command change the view of the parent and the child edit part to make all the changes needed.
*
* @see org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand#doExecuteWithResult(org.eclipse.core.runtime.IProgressMonitor,
* org.eclipse.core.runtime.IAdaptable)
*/
protected CommandResult doExecuteWithResult(IProgressMonitor arg0, IAdaptable arg1) throws ExecutionException {
//Get the IGraphical edit part of the old parent
IGraphicalEditPart oldParentPart = (IGraphicalEditPart)child.getParent();
Map<?, ?> editPartRegistry = getEditPartRegistery();
if(editPartRegistry == null) {
return CommandResult.newErrorCommandResult(CommandResultMessagesError.THE_EDIT_PART_REGISTERY_WAS_NOT_FOUND);
}
//Get the view of the new parent, old parent and the child
// If the parent is null then check if the request is an creation request
if(parent == null) {
if(request instanceof CreateViewAndElementRequest) {
//If true try to get the EditPart of the element in creation
parent = getEditPartFromViewDescriptor((IGraphicalEditPart)child, (CreateViewAndElementRequest)request);
}
} else {
parent = Utils.getCompartementEditPartFromMainEditPart(editPartRegistry, parent);
}
if(parent == null) {
//If no parent is found then an error is thrown
return CommandResult.newErrorCommandResult(CommandResultMessagesError.IGRAPHICAL_EDIT_PART_NOT_CREATED_YET);
}
View newParentView = (View)parent.getModel();
View childView = (View)child.getModel();
/*
* Translate the new node to the corresponding location in its new father.
*/
CommandResult cmdChangeCoordinate = changeCoordinateInNewFather(oldParentPart, childView);
if(cmdChangeCoordinate != null) {
return cmdChangeCoordinate;
}
newParentView.insertChild(childView);
return CommandResult.newOKCommandResult();
}
/**
* This method will change the coordinate of the element on which we are changing it's parent.
* This method is not generic. It will handle differently if the command is came from a createRequest or the cahgneBounds request or without any
* request.
* This method should be reviewed later.(TODO)
*
* @param oldParentPart
* {@link IGraphicalEditPart} of the old paret
* @param childView
* View of the child
* @return
*/
private CommandResult changeCoordinateInNewFather(IGraphicalEditPart oldParentPart, View childView) {
/*
* Deleted because do not find the case where should it should be applied
* if(host != parent) {
*/
if(childView instanceof Node) {
IFigure parentCompartmentFigure = ((AbstractGraphicalEditPart)parent).getFigure();
/*
* If the change graphical parent come after a move
*/
if(Mode.MOVE_CHILD.equals(mode) || Mode.MOVE_PARENT.equals(mode)) {
Rectangle newDimension = computeDeltaToChangeParent((IGraphicalEditPart)parent);
LayoutConstraint layoutConstraint = ((Node)childView).getLayoutConstraint();
if(layoutConstraint instanceof Location) {
Location location = (Location)layoutConstraint;
location.setX(newDimension.x);
location.setY(newDimension.y);
} else {
return CommandResult.newErrorCommandResult("The layoutConstraint is not an instance of Location");
}
/*
* If the change come after a creation or after nothing
*/
} else if(Mode.CREATION_CHILD.equals(mode) || Mode.CREATION_PARENT.equals(mode) || Mode.NORMAL.equals(mode)) {
/*
* Handle change graphical parent from a create request or with no request
* 1 - If the group in creation is the parent then
* 1.1 Used the Hack to force the figure to draw (in order to have it's position)
* 2 - Compute delta from computeDeltaToChangeParent
* 3 - Translate the location of the constraint
*/
Dimension newDimension;
if(parentCompartmentFigure.getBounds().isEmpty()) {
Rectangle oldBounds = parentCompartmentFigure.getBounds().getCopy();
Point compLoc = emulateFigureCreation(parentCompartmentFigure);
newDimension = Utils.computeDeltaToChangeParent(oldParentPart, (IGraphicalEditPart)parent.getParent());
reajustNewDimensionIfNeeded(parentCompartmentFigure, compLoc, newDimension);
parentCompartmentFigure.getBounds().setBounds(oldBounds);
} else {
newDimension = Utils.computeDeltaToChangeParent(oldParentPart, (IGraphicalEditPart)parent);
}
LayoutConstraint layoutConstraint = ((Node)childView).getLayoutConstraint();
if(layoutConstraint instanceof Location) {
Location location = (Location)layoutConstraint;
Point newLocation = new Point(newDimension.width + location.getX(), newDimension.height + location.getY());
location.setX(newLocation.x);
location.setY(newLocation.y);
} else {
return CommandResult.newErrorCommandResult("The layoutConstraint is not an instance of Location");
}
}
} else {
/*
* Error in group framework usage
*/
return CommandResult.newErrorCommandResult("The new containing edit part should be compartment node. The extension point org.eclipse.papyrus.uml.diagram.common.groups.groupcontainment may have been incorrectly used.");
}
return null;
}
/**
* Compute the new rectangle of the child from the original position set in the request and with the newParent {@link IGraphicalEditPart}
*
* @param newParent
* {@link IGraphicalEditPart} of the new parent
* @return new rectangle of the child
*/
private Rectangle computeDeltaToChangeParent(IGraphicalEditPart newParent) {
/*
* Get the absolute bound of a child
*/
Rectangle childAbsoluteRectangle = getChildAbsoluteBounds();
if(childAbsoluteRectangle instanceof Rectangle) {
IFigure newPartFigure = ((IGraphicalEditPart)parent).getContentPane();
newPartFigure.translateToRelative(childAbsoluteRectangle);
newPartFigure.translateFromParent(childAbsoluteRectangle);
Point negatedLayoutOrigin = newPartFigure.getClientArea().getLocation().getNegated();
childAbsoluteRectangle.performTranslate(negatedLayoutOrigin.x, negatedLayoutOrigin.y);
/*
* Correct the position of the child in the parent if any @see getChildCorrectionFromParent
*/
Point negatedMoved = getChildCorrectionFromParent();
childAbsoluteRectangle.performTranslate(negatedMoved.x, negatedMoved.y);
return childAbsoluteRectangle;
}
return null;
}
/**
* Get the EditpartRegistery
*
* @return Map if found null is not found
*/
private Map<?, ?> getEditPartRegistery() {
Map<?, ?> editPartRegistry = null;
//Check if the parent in valid compartment EditPart
if(parent != null) {
EditPartViewer parentViewer = parent.getViewer();
if(parentViewer != null) {
editPartRegistry = parentViewer.getEditPartRegistry();
}
}
if(editPartRegistry == null && child != null && child.getViewer() != null) {
editPartRegistry = child.getViewer().getEditPartRegistry();
}
return editPartRegistry;
}
/**
* Used to readjusts the coordinate of the child if the parentComptmentFigure is not well set yet.
* Used to hack the system if the editPart is not properly set
*
* @param parentCompartmentFigure
* The compartment figure of the parent
* @param compLoc
* @see {@link #emulateFigureCreation(IFigure)}
* @param correctionDimension
* New {@link Dimension} which are going to modify in order to correctt the coordinatte of the child
*/
private void reajustNewDimensionIfNeeded(IFigure parentCompartmentFigure, Point compLoc, Dimension correctionDimension) {
if(compLoc != null) {
correctionDimension.shrink(compLoc.x + parentCompartmentFigure.getInsets().left, compLoc.y + parentCompartmentFigure.getInsets().top);
}
}
/**
* Emulate the figure creation it's not already done. This allow the system to know the absolute solution even if the edit part of the parent is
* not yet finish
*
* @return null is the figure is already done. Return the translation needed to take into account the child position in it's parent ( e.g
* ActivityPartitionCompartement from its ActivityPartitionEditPart)
*/
private Point emulateFigureCreation(IFigure parentCompartmentFigure) {
if(parentCompartmentFigure.getBounds().isEmpty()) {
// layout parent node correctly to recover the correct location of compartment which has not been set previously
IFigure parentNodeFigure = parentCompartmentFigure.getParent();
if(parentNodeFigure != null) {
IFigure grandParentFigure = parentNodeFigure.getParent();
if(grandParentFigure != null) {
grandParentFigure.getLayoutManager().layout(grandParentFigure);
}
parentNodeFigure.getLayoutManager().layout(parentNodeFigure);
}
Point compLoc = parentCompartmentFigure.getBounds().getLocation();
return compLoc;
}
return null;
}
/**
* This method is going to get the edit part from the request.
* 1 - Get descriptors
* 2 - Get View (adapter)
* 3 - Get IGraphicalEditPart if it exists
*
* @param anyPart
* Any IGraphicalEditPart in order to get the EditPartRegistery
* @return true if it as found the edit part
*/
private EditPart getEditPartFromViewDescriptor(IGraphicalEditPart anyPart, CreateViewAndElementRequest _request) {
EditPart result = null;
Iterator<? extends CreateViewRequest.ViewDescriptor> descriptors = _request.getViewDescriptors().iterator();
Map<?, ?> editPartRegistry = anyPart.getViewer().getEditPartRegistry();
while(descriptors.hasNext()) {
CreateViewRequest.ViewDescriptor descriptor = (CreateViewRequest.ViewDescriptor)descriptors.next();
Object view = descriptor.getAdapter(View.class);
if(view instanceof View) {
View childView = (View)view;
result = getCompartmentEditPartFromView(editPartRegistry, childView);
}
}
return result;
}
/**
* Get a compartment editPart from a view and the editPartRegistery
*
* @param editPartRegistry
* EditPartRegistery
* @param view
* View of the element we want to find the compartment
* @return the EdiPart of compartment and null if not found
*/
public EditPart getCompartmentEditPartFromView(Map<?, ?> editPartRegistry, View view) {
EditPart resultCompartmentEditPart = null;
Object editPartAux = editPartRegistry.get(view);
if(editPartAux instanceof EditPart) {
EditPart _editPart = (EditPart)editPartAux;
resultCompartmentEditPart = Utils.getCompartementEditPartFromMainEditPart(editPartRegistry, _editPart);
}
return resultCompartmentEditPart;
}
public Mode getMode() {
return mode;
}
public void setMode(Mode mode) {
this.mode = mode;
}
/**
* Give the absolute rectangle of the child. (Depending of the mode)
*
* @return {@link Rectangle} of the child
*/
private Rectangle getChildAbsoluteBounds() {
Rectangle result = null;
if(child instanceof IGraphicalEditPart) {
switch(mode) {
case MOVE_CHILD:
if(request instanceof ChangeBoundsRequest) {
Object _origineConstraint = request.getExtendedData().get(GroupRequestConstants.CONSTRAINT_AFTER_MOVING);
if(_origineConstraint instanceof Rectangle) {
result = (Rectangle)_origineConstraint;
}
}
break;
case MOVE_PARENT:
result = Utils.getAbsoluteBounds((IGraphicalEditPart)child).getCopy();
break;
default:
result = Utils.getAbsoluteBounds((IGraphicalEditPart)child).getCopy();
break;
}
}
return result;
}
/**
* Get the correction to be applied to the child in function of the mode
*
* @return {@link Point} to translate the child
*/
private Point getChildCorrectionFromParent() {
Point result = null;
if(child instanceof IGraphicalEditPart) {
switch(mode) {
case MOVE_PARENT:
result = ((ChangeBoundsRequest)request).getMoveDelta().getNegated();
break;
default:
result = new Point(0, 0);
break;
}
}
return result;
}
}