/** * Copyright (c) 2013 committers of YAKINDU 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: * committers of YAKINDU - initial API and implementation * */ package org.yakindu.sct.ui.editor.policies; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; 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.Request; import org.eclipse.gef.RequestConstants; import org.eclipse.gef.commands.Command; import org.eclipse.gef.commands.CompoundCommand; import org.eclipse.gef.editpolicies.AbstractEditPolicy; import org.eclipse.gef.requests.ChangeBoundsRequest; 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.IGraphicalEditPart; import org.eclipse.gmf.runtime.diagram.ui.l10n.DiagramUIMessages; import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter; import org.yakindu.sct.ui.editor.editparts.RegionEditPart; import org.yakindu.sct.ui.editor.editparts.StateEditPart; /** * * @author andreas muelder - Initial contribution and API * */ public class EnlargeContainerEditPolicy extends AbstractEditPolicy { // Key for edit policy installation public static final Object ROLE = "ResizeContainer"; // Space between the border of the container and the moved figure public static final int SPACEING = 25; private Map<IFigure, Rectangle> boundsCache = new HashMap<IFigure, Rectangle>(); private List<IGraphicalEditPart> containerHierachy; @SuppressWarnings("unchecked") @Override public Command getCommand(Request request) { if (!RequestConstants.REQ_RESIZE.equals(request.getType()) && !RequestConstants.REQ_MOVE.equals(request.getType())) { return null; } if (request instanceof SetPreferredSizeRequest) { showSourceFeedback(request); } ChangeBoundsRequest cbr = (ChangeBoundsRequest) request; CompoundCommand result = new CompoundCommand(); // Update Bounds of the container hierachy for (IGraphicalEditPart currentContainer : containerHierachy) { IFigure figure = currentContainer.getFigure(); SetBoundsCommand boundsCommand = new SetBoundsCommand(getHost().getEditingDomain(), DiagramUIMessages.SetLocationCommand_Label_Resize, new EObjectAdapter( currentContainer.getNotationView()), figure.getBounds()); result.add(new ICommandProxy(boundsCommand)); // Update child bounds of elements that stand in the way... List<IGraphicalEditPart> children = currentContainer.getParent().getChildren(); for (IGraphicalEditPart childPart : children) { if (cbr.getEditParts().contains(childPart)) continue; IFigure childFigure = childPart.getFigure(); if (childPart == currentContainer) continue; SetBoundsCommand childBoundsCommand = new SetBoundsCommand(getHost().getEditingDomain(), DiagramUIMessages.SetLocationCommand_Label_Resize, new EObjectAdapter( childPart.getNotationView()), childFigure.getBounds()); result.add(new ICommandProxy(childBoundsCommand)); } } return result; } @Override public IGraphicalEditPart getHost() { return (IGraphicalEditPart) super.getHost(); } @Override public void showSourceFeedback(Request request) { if (containerHierachy == null) { containerHierachy = collectContainerHierachy(); } if (!RequestConstants.REQ_RESIZE.equals(request.getType()) && !RequestConstants.REQ_MOVE.equals(request.getType())) return; showContainerFeedback((ChangeBoundsRequest) request); } @Override public void eraseSourceFeedback(Request request) { boundsCache.clear(); containerHierachy = null; super.eraseSourceFeedback(request); } protected void showContainerFeedback(ChangeBoundsRequest request) { for (int level = 1; level <= containerHierachy.size(); level++) { IGraphicalEditPart containerEditPart = containerHierachy.get(level - 1); IFigure containerFigure = containerEditPart.getFigure(); Rectangle feedbackBounds = getOriginalBounds(containerFigure); containerFigure.getParent().translateToAbsolute(feedbackBounds); feedbackBounds = calculateFeedbackBounds(request, feedbackBounds, level, containerFigure); showChildrenFeedback(containerEditPart, containerFigure, feedbackBounds, request); containerFigure.translateToRelative(feedbackBounds); setBounds(containerFigure, feedbackBounds); } } protected List<IGraphicalEditPart> collectContainerHierachy() { List<IGraphicalEditPart> result = new ArrayList<IGraphicalEditPart>(); IGraphicalEditPart containerEditPart = (IGraphicalEditPart) getHost(); while (containerEditPart != null) { containerEditPart = getContainer(containerEditPart); if (containerEditPart != null) result.add(containerEditPart); } return result; } @SuppressWarnings("unchecked") /** * containerFeedbackBounds as absolute * * @param containerEditPart * @param containerFigure * @param containerFeedbackBounds */ protected void showChildrenFeedback(final IGraphicalEditPart containerEditPart, final IFigure containerFigure, final Rectangle containerFeedbackBounds, ChangeBoundsRequest request) { Rectangle originalBounds = getOriginalBounds(containerFigure); Point moveDelta = new Point(containerFeedbackBounds.width - originalBounds.width, containerFeedbackBounds.height - originalBounds.height); List<IGraphicalEditPart> children = containerEditPart.getParent().getChildren(); for (IGraphicalEditPart childPart : children) { if (request.getEditParts().contains(childPart)) { continue; } if (childPart == containerEditPart) continue; showChildFeedback(childPart, moveDelta, containerFeedbackBounds); } } protected void showChildFeedback(IGraphicalEditPart childPart, Point moveDelta, Rectangle containerFeedbackBounds) { IFigure childFigure = childPart.getFigure(); Rectangle originalChildBounds = getOriginalBounds(childFigure); childFigure.getParent().translateToAbsolute(originalChildBounds); boolean horizontalAffected = isHorizontalAffected(containerFeedbackBounds, moveDelta, originalChildBounds); boolean verticalAffected = isVerticalAffected(containerFeedbackBounds, moveDelta, originalChildBounds); if (!(horizontalAffected || verticalAffected)) { return; } if (horizontalAffected) { originalChildBounds.x += moveDelta.x; } if (verticalAffected) { originalChildBounds.y += moveDelta.y; } childFigure.getParent().translateToRelative(originalChildBounds); setBounds(childFigure, originalChildBounds); } /** * Return a copy of the bounds from the cache * * @param figure * @return */ private Rectangle getOriginalBounds(IFigure figure) { Rectangle originalContainerBounds = boundsCache.get(figure); if (originalContainerBounds == null) { originalContainerBounds = figure.getBounds().getCopy(); boundsCache.put(figure, originalContainerBounds); } return boundsCache.get(figure).getCopy(); } private IGraphicalEditPart getContainer(IGraphicalEditPart host) { IGraphicalEditPart containerEditPart = getParentState(host); if (containerEditPart == null) { containerEditPart = getParentRegion(host); if (containerEditPart == null) return null; } return containerEditPart; } private boolean isVerticalAffected(Rectangle newBounds, Point moveDelta, Rectangle bounds) { boolean verticalAffected = (bounds.x > newBounds.x || bounds.x + bounds.width > newBounds.x) && bounds.x < newBounds.x + newBounds.width || bounds.x + bounds.width < newBounds.x + newBounds.width; if (verticalAffected) { verticalAffected = bounds.y + moveDelta.y > newBounds.y + newBounds.height; } return verticalAffected; } private boolean isHorizontalAffected(Rectangle newBounds, Point moveDelta, Rectangle bounds) { boolean horizontalAffected = (bounds.y > newBounds.y || bounds.y + bounds.height > newBounds.y) && bounds.y < newBounds.y + newBounds.height || bounds.y + bounds.height < newBounds.y + newBounds.height; if (horizontalAffected) { horizontalAffected = bounds.x + moveDelta.x > newBounds.x + newBounds.width; } return horizontalAffected; } protected void setBounds(IFigure figure, Rectangle bounds) { figure.setBounds(bounds); figure.getParent().setConstraint(figure, bounds); } @SuppressWarnings({ "unchecked" }) private Rectangle calculateFeedbackBounds(ChangeBoundsRequest request, Rectangle feedbackBounds, int level, IFigure containerFigure) { Rectangle result = feedbackBounds.getCopy(); List<IGraphicalEditPart> editParts = request.getEditParts(); for (IGraphicalEditPart editPart : editParts) { PrecisionRectangle transformedRect = new PrecisionRectangle(editPart.getFigure().getBounds()); editPart.getFigure().translateToAbsolute(transformedRect); transformedRect.translate(request.getMoveDelta()); transformedRect.resize(request.getSizeDelta()); transformedRect.expand(SPACEING * level, SPACEING * level); result.union(transformedRect); Dimension preferredSize = containerFigure.getPreferredSize().getCopy(); editPart.getFigure().translateToAbsolute(preferredSize); Dimension max = Dimension.max(result.getSize(), preferredSize); result.setSize(max); if (result.x < feedbackBounds.x || result.y < feedbackBounds.y) { return feedbackBounds; } } return result; } protected IGraphicalEditPart getParentRegion(EditPart part) { part = part.getParent(); while (!(part instanceof RegionEditPart)) { part = part.getParent(); if (part == null) return null; } return (IGraphicalEditPart) part; } protected IGraphicalEditPart getParentState(EditPart part) { part = part.getParent(); while (!(part instanceof StateEditPart)) { part = part.getParent(); if (part == null) return null; } return (IGraphicalEditPart) part; } }