/*******************************************************************************
* Copyright (c) 2000, 2005 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.gef.editpolicies;
import java.util.List;
import org.eclipse.draw2d.IFigure;
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.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.RequestConstants;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.requests.AlignmentRequest;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gef.requests.CreateRequest;
/**
* For use with <code>LayoutManager</code> that require a <i>constraint</i>.
* ConstrainedLayoutEditPolicy understands {@link RequestConstants#REQ_ALIGN_CHILDREN}
* in addition to the Requests handled in the superclass.
* @author hudsonr
* @since 2.0
*/
public abstract class ConstrainedLayoutEditPolicy
extends LayoutEditPolicy
{
/**
* Returns the <code>Command</code> to perform an Add with the specified child and
* constraint. The constraint has been converted from a draw2d constraint to an object
* suitable for the model by calling {@link #translateToModelConstraint(Object)}.
* @param child the EditPart of the child being added
* @param constraint the model constraint, after being {@link
* #translateToModelConstraint(Object) translated}
* @return the Command to add the child
*/
protected Command createAddCommand(EditPart child, Object constraint) {
return null;
}
/**
* The request is now made available when creating the change constraint command. By
* default, this method invokes the old
* {@link ConstrainedLayoutEditPolicy#createChangeConstraintCommand(EditPart, Object) method}.
*
* @param request the ChangeBoundsRequest
* @param child the EditPart of the child being changed
* @param constraint the new constraint, after being {@link
* #translateToModelConstraint(Object) translated}
* @return A Command to change the constraints of the given child as specified in the
* given request
* @see ConstrainedLayoutEditPolicy#createChangeConstraintCommand(EditPart, Object)
* @since 3.0
*/
protected Command createChangeConstraintCommand(ChangeBoundsRequest request,
EditPart child, Object constraint) {
return createChangeConstraintCommand(child, constraint);
}
/**
* Returns the <code>Command</code> to change the specified child's constraint. The
* constraint has been converted from a draw2d constraint to an object suitable for the
* model
* @param child the EditPart of the child being changed
* @param constraint the new constraint, after being {@link
* #translateToModelConstraint(Object) translated}
* @return Command
* @see #createChangeConstraintCommand(ChangeBoundsRequest, EditPart, Object)
*/
protected abstract Command createChangeConstraintCommand(
EditPart child, Object constraint);
/**
* A {@link ResizableEditPolicy} is used by default for children. Subclasses may
* override this method to supply a different EditPolicy.
*
* @see org.eclipse.gef.editpolicies.LayoutEditPolicy#createChildEditPolicy(EditPart)
*/
protected EditPolicy createChildEditPolicy(EditPart child) {
return new ResizableEditPolicy();
}
/**
* Overrides <code>getAddCommand()</code> to generate the proper constraint for each child
* being added. Once the constraint is calculated, {@link
* #createAddCommand(EditPart,Object)} is called. Subclasses must implement this method.
* @see org.eclipse.gef.editpolicies.LayoutEditPolicy#getAddCommand(Request)
*/
protected Command getAddCommand(Request generic) {
ChangeBoundsRequest request = (ChangeBoundsRequest)generic;
List editParts = request.getEditParts();
CompoundCommand command = new CompoundCommand();
command.setDebugLabel("Add in ConstrainedLayoutEditPolicy");//$NON-NLS-1$
GraphicalEditPart childPart;
Rectangle r;
Object constraint;
for (int i = 0; i < editParts.size(); i++) {
childPart = (GraphicalEditPart)editParts.get(i);
r = childPart.getFigure().getBounds().getCopy();
//convert r to absolute from childpart figure
childPart.getFigure().translateToAbsolute(r);
r = request.getTransformedRectangle(r);
//convert this figure to relative
getLayoutContainer().translateToRelative(r);
getLayoutContainer().translateFromParent(r);
r.translate(getLayoutOrigin().getNegated());
constraint = getConstraintFor(r);
/*
* @TODO:Pratik Should create a new createAddCommand(...) which is given the
* request so that attaching to/detaching from guides can be taken care of.
*/
command.add(createAddCommand(childPart,
translateToModelConstraint(constraint)));
}
return command.unwrap();
}
/**
* Returns the command to align a group of children. By default, this is treated the same
* as a resize, and {@link #getResizeChildrenCommand(ChangeBoundsRequest)} is returned.
* @param request the AligmentRequest
* @return the command to perform aligment
*/
protected Command getAlignChildrenCommand(AlignmentRequest request) {
return getResizeChildrenCommand(request);
}
/**
* Factors out RESIZE and ALIGN requests, otherwise calls <code>super</code>.
* @see org.eclipse.gef.EditPolicy#getCommand(Request)
*/
public Command getCommand(Request request) {
if (REQ_RESIZE_CHILDREN.equals(request.getType()))
return getResizeChildrenCommand((ChangeBoundsRequest)request);
if (REQ_ALIGN_CHILDREN.equals(request.getType()))
return getAlignChildrenCommand((AlignmentRequest)request);
return super.getCommand(request);
}
/**
* Generates a draw2d constraint object derived from the specified child EditPart using
* the provided Request. The returned constraint will be translated to the application's
* model later using {@link #translateToModelConstraint(Object)}.
* @param request the ChangeBoundsRequest
* @param child the child EditPart for which the constraint should be generated
* @return the draw2d constraint
*/
protected Object getConstraintFor (ChangeBoundsRequest request, GraphicalEditPart child) {
Rectangle rect = new PrecisionRectangle(child.getFigure().getBounds());
child.getFigure().translateToAbsolute(rect);
rect = request.getTransformedRectangle(rect);
child.getFigure().translateToRelative(rect);
rect.translate(getLayoutOrigin().getNegated());
return getConstraintFor(rect);
}
/**
* Generates a draw2d constraint given a <code>Point</code>. This method is called during
* creation, when only a mouse location is available.
* @param point the Point relative to the {@link #getLayoutOrigin() layout origin}
* @return the constraint
*/
protected abstract Object getConstraintFor (Point point);
/**
* Generates a draw2d constraint given a <code>Rectangle</code>. This method is called
* during most operations.
* @param rect the Rectangle relative to the {@link #getLayoutOrigin() layout origin}
* @return the constraint
*/
protected abstract Object getConstraintFor (Rectangle rect);
/**
* Generates a draw2d constraint for the given <code>CreateRequest</code>. If the
* CreateRequest has a size, {@link #getConstraintFor(Rectangle)} is called with a
* Rectangle of that size and the result is returned. This is used during size-on-drop
* creation. Otherwise, {@link #getConstraintFor(Point)} is returned.
* <P>
* The CreateRequest's location is relative the Viewer. The location is made
* layout-relative before calling one of the methods mentioned above.
* @param request the CreateRequest
* @return a draw2d constraint
*/
protected Object getConstraintFor(CreateRequest request) {
IFigure figure = getLayoutContainer();
Point where = request.getLocation().getCopy();
Dimension size = request.getSize();
figure.translateToRelative(where);
figure.translateFromParent(where);
where.translate(getLayoutOrigin().getNegated());
if (size == null || size.isEmpty())
return getConstraintFor(where);
else {
//$TODO Probably should use PrecisionRectangle at some point instead of two
// geometrical objects
size = size.getCopy();
figure.translateToRelative(size);
figure.translateFromParent(size);
return getConstraintFor(new Rectangle(where, size));
}
}
/**
* Returns the correct rectangle bounds for the new clone's location.
*
* @param part the graphical edit part representing the object to be cloned.
* @param request the changeboundsrequest that knows where to place the new
* object.
* @return the bounds that will be used for the new object.
*/
protected Object getConstraintForClone(GraphicalEditPart part, ChangeBoundsRequest request) {
IFigure figure = part.getFigure();
Rectangle bounds = new PrecisionRectangle(figure.getBounds());
figure.translateToAbsolute(bounds);
bounds = request.getTransformedRectangle(bounds);
((GraphicalEditPart)getHost()).getContentPane().translateToRelative(bounds);
bounds.translate(getLayoutOrigin().getNegated());
return getConstraintFor(bounds);
}
/**
* Converts a constraint from the format used by LayoutManagers,
* to the form stored in the model.
* @param figureConstraint the draw2d constraint
* @return the model constraint
*/
protected Object translateToModelConstraint(Object figureConstraint) {
return figureConstraint;
}
/**
* Returns the <code>Command</code> to resize a group of children.
* @param request the ChangeBoundsRequest
* @return the Command
*/
protected Command getResizeChildrenCommand(ChangeBoundsRequest request) {
CompoundCommand resize = new CompoundCommand();
Command c;
GraphicalEditPart child;
List children = request.getEditParts();
for (int i = 0; i < children.size(); i++) {
child = (GraphicalEditPart)children.get(i);
c = createChangeConstraintCommand(request, child,
translateToModelConstraint(
getConstraintFor(request, child)));
resize.add(c);
}
return resize.unwrap();
}
/**
* Returns the <code>Command</code> to move a group of children. By default, move is
* treated the same as a resize.
* @see org.eclipse.gef.editpolicies.LayoutEditPolicy#getMoveChildrenCommand(Request)
*/
protected Command getMoveChildrenCommand(Request request) {
//By default, move and resize are treated the same for constrained layouts.
return getResizeChildrenCommand((ChangeBoundsRequest)request);
}
/**
* Returns the layout's origin relative to the {@link
* LayoutEditPolicy#getLayoutContainer()}. In other words, what Point on the parent Figure
* does the LayoutManager use a reference when generating the child figure's bounds from
* the child's constraint.
* <P>
* By default, it is assumed that the layout manager positions children relative to the
* client area of the layout container. Thus, when processing Viewer-relative Points or
* Rectangles, the clientArea's location (top-left corner) will be subtracted from the
* Point/Rectangle, resulting in an offset from the LayoutOrigin.
* @return Point
*/
protected Point getLayoutOrigin() {
return getLayoutContainer().getClientArea().getLocation();
}
}