/******************************************************************************* * 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.ColorConstants; import org.eclipse.draw2d.FigureUtilities; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.RectangleFigure; import org.eclipse.draw2d.Shape; import org.eclipse.draw2d.geometry.Insets; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPartListener; 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.requests.ChangeBoundsRequest; import org.eclipse.gef.requests.CreateRequest; /** * Provides support for interacting with children <code>GraphicalEditParts</code> with the * host figure's current {@link org.eclipse.draw2d.LayoutManager}. * <P> * LayoutEditPolicies are responsible for moving, resizing, reparenting, and creating * children. The should provide <code>Commands</code> for all of these operations. * Feedback on the container can also be useful for some layouts like grids. * <P> * LayoutEditPolicies will decorate the host's children with "satellite" EditPolicies. * These policies are installed using the {@link EditPolicy#PRIMARY_DRAG_ROLE}. Simple * layouts will use either {@link ResizableEditPolicy} or {@link NonResizableEditPolicy}, * depending on how the LayoutManager works, and/or attributes of the child EditPart. */ public abstract class LayoutEditPolicy extends GraphicalEditPolicy { private IFigure sizeOnDropFeedback; private EditPartListener listener; /** * Extends activate() to allow proper decoration of children. * @see org.eclipse.gef.EditPolicy#activate() */ public void activate() { setListener(createListener()); decorateChildren(); super.activate(); } /** * Returns the "satellite" EditPolicy used to decorate the child. * @param child the child EditPart * @return an EditPolicy to be installed as the {@link EditPolicy#PRIMARY_DRAG_ROLE} */ protected abstract EditPolicy createChildEditPolicy(EditPart child); /** * creates the EditPartListener for observing when children are added to the host. * @return EditPartListener */ protected EditPartListener createListener() { return new EditPartListener.Stub() { public void childAdded(EditPart child, int index) { decorateChild(child); } }; } /** * Override to provide custom feedback figure for the given create request. * @param createRequest the create request * @return custom feedback figure */ protected IFigure createSizeOnDropFeedback(CreateRequest createRequest) { return null; } /** * Overrides deactivate to remove the EditPartListener. * @see org.eclipse.gef.EditPolicy#deactivate() */ public void deactivate() { if (sizeOnDropFeedback != null) { removeFeedback(sizeOnDropFeedback); sizeOnDropFeedback = null; } setListener(null); super.deactivate(); } /** * Decorates the child with a {@link EditPolicy#PRIMARY_DRAG_ROLE} such as {@link * ResizableEditPolicy}. * @param child the child EditPart being decorated */ protected void decorateChild(EditPart child) { EditPolicy policy = createChildEditPolicy(child); child.installEditPolicy(EditPolicy.PRIMARY_DRAG_ROLE, policy); } /** * Decorates all existing children. This method is called on activation. */ protected void decorateChildren() { List children = getHost().getChildren(); for (int i = 0; i < children.size(); i++) decorateChild((EditPart)children.get(i)); } /** * Erases target layout feedback. This method is the inverse of {@link * #showLayoutTargetFeedback(Request)}. * @param request the Request */ protected void eraseLayoutTargetFeedback(Request request) { } /** * Erases size-on-drop feedback used during creation. * @param request the Request */ protected void eraseSizeOnDropFeedback(Request request) { if (sizeOnDropFeedback != null) { removeFeedback(sizeOnDropFeedback); sizeOnDropFeedback = null; } } /** * Calls two more specific methods depending on the Request. * @see org.eclipse.gef.EditPolicy#eraseTargetFeedback(Request) */ public void eraseTargetFeedback(Request request) { if (REQ_ADD.equals(request.getType()) || REQ_MOVE.equals(request.getType()) || REQ_RESIZE_CHILDREN.equals(request.getType()) || REQ_CREATE.equals(request.getType()) || REQ_CLONE.equals(request.getType())) eraseLayoutTargetFeedback(request); if (REQ_CREATE.equals(request.getType())) eraseSizeOnDropFeedback(request); } /** * Override to return the <code>Command</code> to perform an {@link * RequestConstants#REQ_ADD ADD}. By default, <code>null</code> is returned. * @param request the ADD Request * @return A command to perform the ADD. */ protected Command getAddCommand(Request request) { return null; } /** * Override to contribute to clone requests. * @param request the clone request * @return the command contribution to the clone */ protected Command getCloneCommand(ChangeBoundsRequest request) { return null; } /** * Factors incoming requests into various specific methods. * @see org.eclipse.gef.EditPolicy#getCommand(Request) */ public Command getCommand(Request request) { if (REQ_DELETE_DEPENDANT.equals(request.getType())) return getDeleteDependantCommand(request); if (REQ_ADD.equals(request.getType())) return getAddCommand(request); if (REQ_ORPHAN_CHILDREN.equals(request.getType())) return getOrphanChildrenCommand(request); if (REQ_MOVE_CHILDREN.equals(request.getType())) return getMoveChildrenCommand(request); if (REQ_CLONE.equals(request.getType())) return getCloneCommand((ChangeBoundsRequest)request); if (REQ_CREATE.equals(request.getType())) return getCreateCommand((CreateRequest)request); return null; } /** * Returns the <code>Command</code> to perform a create. * @param request the CreateRequest * @return a Command to perform a create */ protected abstract Command getCreateCommand(CreateRequest request); /** * Returns any insets that need to be applied to the creation feedback's bounds. * @param request the create request * @return insets, if necessary */ protected Insets getCreationFeedbackOffset(CreateRequest request) { return new Insets(); } /** * Returns the <code>Command</code> to delete a child. This method does not get called * unless the child forwards an additional request to the container editpart. * @param request the Request * @return the Command to delete the child */ protected Command getDeleteDependantCommand(Request request) { return null; } /** * Returns the host's {@link GraphicalEditPart#getContentPane() contentPane}. The * contentPane is the Figure which parents the childrens' figures. It is also the figure * which has the LayoutManager that corresponds to this EditPolicy. All operations should * be interpreted with respect to this figure. * @return the Figure that owns the corresponding <code>LayoutManager</code> */ protected IFigure getLayoutContainer() { return ((GraphicalEditPart)getHost()).getContentPane(); } /** * Returns the <code>Command</code> to move a group of children. * @param request the Request * @return the Command to perform the move */ protected abstract Command getMoveChildrenCommand(Request request); /** * Returns the <code>Command</code> to orphan a group of children. The contribution to * orphan might contain two parts, both of which are optional. The first part is to * actually remove the children from their existing parent. Some application models will * perform an orphan implicitly when the children are added to their new parent. The * second part is to perform some adjustments on the remaining children. For example, a * Table layout might simplify itself by collapsing any unused columns and rows. * @param request the Request * @return <code>null</code> or a Command to perform an orphan */ protected Command getOrphanChildrenCommand(Request request) { return null; } /** * Lazily creates and returns the Figure to use for size-on-drop feedback. * @param createRequest the createRequest * @return the size-on-drop feedback figure */ protected IFigure getSizeOnDropFeedback(CreateRequest createRequest) { if (sizeOnDropFeedback == null) sizeOnDropFeedback = createSizeOnDropFeedback(createRequest); return getSizeOnDropFeedback(); } /** * Lazily creates and returns the Figure to use for size-on-drop feedback. * @return the size-on-drop feedback figure */ 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; } /** * Returns the <i>host</i> if the Request is an ADD, MOVE, or CREATE. * @see org.eclipse.gef.EditPolicy#getTargetEditPart(Request) */ public EditPart getTargetEditPart(Request request) { if (REQ_ADD.equals(request.getType()) || REQ_MOVE.equals(request.getType()) || REQ_CREATE.equals(request.getType()) || REQ_CLONE.equals(request.getType())) return getHost(); return null; } /** * Sets the EditPartListener used to decorate new children. If the listener is currently * set, it will be unhooked. If the new value is not <code>null</code>, it will be hooked. * <P> * The listener must be remembered in case this EditPolicy is removed from the host and * replaced with another LayoutEditPolicy. * @param listener <code>null</code> or the listener. */ protected void setListener(EditPartListener listener) { if (this.listener != null) getHost().removeEditPartListener(this.listener); this.listener = listener; if (this.listener != null) getHost().addEditPartListener(this.listener); } /** * Shows target layout feedback. During <i>moves</i>, <i>reparents</i>, and * <i>creation</i>, this method is called to allow the LayoutEditPolicy to temporarily * show features of its layout that will help the User understand what will happen if the * operation is performed in the current location. * <P> * By default, no feedback is shown. * @param request the Request * @see #eraseLayoutTargetFeedback(Request) */ protected void showLayoutTargetFeedback(Request request) { } /** * Shows size-on-drop feedback during creation. * @param request the CreateRequest */ protected void showSizeOnDropFeedback(CreateRequest request) { } /** * Factors feedback requests into two more specific methods. * @see org.eclipse.gef.EditPolicy#showTargetFeedback(Request) */ public void showTargetFeedback(Request request) { if (REQ_ADD.equals(request.getType()) || REQ_CLONE.equals(request.getType()) || REQ_MOVE.equals(request.getType()) || REQ_RESIZE_CHILDREN.equals(request.getType()) || REQ_CREATE.equals(request.getType())) showLayoutTargetFeedback(request); if (REQ_CREATE.equals(request.getType())) { CreateRequest createReq = (CreateRequest)request; if (createReq.getSize() != null) showSizeOnDropFeedback(createReq); } } /** * Removes the decoration added in {@link #decorateChild(EditPart)}. * @param child the child whose decoration is being removed. */ protected void undecorateChild(EditPart child) { child.removeEditPolicy(EditPolicy.PRIMARY_DRAG_ROLE); } /** * Removes all decorations added by {@link #decorateChildren()}. */ protected void undecorateChildren() { List children = getHost().getChildren(); for (int i = 0; i < children.size(); i++) undecorateChild((EditPart)children.get(i)); } }