package org.eclipse.papyrus.uml.diagram.statemachine.custom.policies; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.PositionConstants; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.PrecisionRectangle; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.gef.GraphicalEditPart; import org.eclipse.gef.Request; import org.eclipse.gef.editpolicies.ResizableEditPolicy; import org.eclipse.gef.handles.ResizableHandleKit; import org.eclipse.gef.requests.ChangeBoundsRequest; import org.eclipse.gmf.runtime.notation.View; import org.eclipse.papyrus.uml.diagram.statemachine.custom.figures.RegionFigure; import org.eclipse.papyrus.uml.diagram.statemachine.custom.helpers.Zone; import org.eclipse.papyrus.uml.diagram.statemachine.edit.parts.RegionEditPart; public class CustomRegionResizableEditPolicy extends ResizableEditPolicy { /** The associated request. */ private ChangeBoundsRequest request; /** * The target figure, i.e. the figure associated to the targetNode region . * Used to change of coordinates. */ private RegionFigure targetFig; /** Flag to indicate a change in the referent figure is needed. */ private boolean changeReferentFigure = false; /** * Provides a specific list of SelectionHandles. * * @return a list of the Handles */ @Override protected List createSelectionHandles() { List list = new ArrayList(); int dirs = getResizeDirections(); // by default (case when dirs == -1 or 0) // no move nor resize handles are provided // this is to cope with the default region included in the statemachine // for which it would not make any sense if(dirs > 0) { // resize handles are added depending on a bit-wise test of dirs // in any case we do not provide oblique resize handles if((dirs & PositionConstants.EAST) != 0) ResizableHandleKit.addHandle((GraphicalEditPart)getHost(), list, PositionConstants.EAST); if((dirs & PositionConstants.SOUTH) != 0) ResizableHandleKit.addHandle((GraphicalEditPart)getHost(), list, PositionConstants.SOUTH); if((dirs & PositionConstants.WEST) != 0) ResizableHandleKit.addHandle((GraphicalEditPart)getHost(), list, PositionConstants.WEST); if((dirs & PositionConstants.NORTH) != 0) { ResizableHandleKit.addHandle((GraphicalEditPart)getHost(), list, PositionConstants.NORTH); } } return list; } /** * This method is overridden to change the ghost figure shown when either * resizing a region or moving it around. In the case of a resize we need to * update the bounds of the ghost figure as the union of bounds of resized * regions. This is done by a check of the neighboring regions which share * the same border to be moved by the resize request. In the case of a move * we need to show what will happen to the region when dropped on a given * one: thus show a half-sized region located on one of the side of its * parent region depending on the position of the mouse. In the course of * this process we assign a corresponding drop location to the region. * * @param a * rectangle which is the bounds of the ghost figure to be shown */ @Override protected Rectangle getInitialFeedbackBounds() { Dimension sizeDelta = request.getSizeDelta(); // restore default flag value changeReferentFigure = false; // we test whether this is a move request or a resize request if((sizeDelta.width == 0) && (sizeDelta.height == 0)) { return super.getInitialFeedbackBounds(); } // this is a resize request else { // retrieve the direction of resize int direction = request.getResizeDirection(); // retrieve the edit part associated to the policy RegionEditPart regionEP = (RegionEditPart)getHost(); // the associated region view View region = (View)regionEP.getModel(); // a list to get all the nodes impacted by the resize List<View> nodes = new ArrayList<View>(); // test the direction and call the appropriate method if(direction == PositionConstants.NORTH) // retrieve the list of nodes that are at the BOTTOM of NORTH // border nodes = Zone.getRegionTopBorderInsideNeighbours(region); else if(direction == PositionConstants.SOUTH) // retrieve the list of nodes that are at the TOP of SOUTH // border nodes = Zone.getRegionBottomBorderInsideNeighbours(region); else if(direction == PositionConstants.EAST) // retrieve the list of nodes that are at the LEFT of EAST // border nodes = Zone.getRegionRightBorderInsideNeighbours(region); else if(direction == PositionConstants.WEST) // retrieve the list of nodes that are at the RIGHT of WEST // border nodes = Zone.getRegionLeftBorderInsideNeighbours(region); // now compute the bounds of the node union Rectangle rect = null; Iterator<View> it = nodes.iterator(); while(it.hasNext()) { View view = it.next(); Rectangle currentBounds = Zone.getBounds(view); if(rect == null) rect = currentBounds; else rect = rect.union(currentBounds); } return rect; } } /** * This method is overridden here to save the request and change the figure * used to perform coordinate change. * * @param request * the Request */ @Override protected void showChangeBoundsFeedback(ChangeBoundsRequest request) { this.request = (ChangeBoundsRequest)request; IFigure feedback = getDragSourceFeedbackFigure(); PrecisionRectangle rect = new PrecisionRectangle(getInitialFeedbackBounds().getCopy()); // if regions are part of different state machines or state if(changeReferentFigure) // use the target figure as referent for coordinate change targetFig.translateToAbsolute(rect); // both regions are part of the same state machine or state else // use default host figure getHostFigure().translateToAbsolute(rect); rect.translate(request.getMoveDelta()); rect.resize(request.getSizeDelta()); feedback.translateToRelative(rect); feedback.setBounds(rect); } @Override public void showSourceFeedback(Request request) { if(REQ_RESIZE.equals(request.getType())) showChangeBoundsFeedback((ChangeBoundsRequest)request); } }