/******************************************************************************* * Copyright (c) 2010-2015 Henshin developers. 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: * TU Berlin, University of Luxembourg, SES S.A. *******************************************************************************/ package de.tub.tfs.henshin.tggeditor.editpolicies.graphical; import java.lang.reflect.Field; import java.util.HashMap; import java.util.List; import org.eclipse.draw2d.FigureCanvas; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.henshin.model.Graph; import org.eclipse.emf.henshin.model.Node; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPolicy; import org.eclipse.gef.Request; import org.eclipse.gef.commands.Command; import org.eclipse.gef.commands.CompoundCommand; import org.eclipse.gef.editpolicies.XYLayoutEditPolicy; import org.eclipse.gef.requests.AlignmentRequest; import org.eclipse.gef.requests.ChangeBoundsRequest; import org.eclipse.gef.requests.CreateRequest; import de.tub.tfs.henshin.tgg.TGG; import de.tub.tfs.henshin.tgg.TNode; import de.tub.tfs.henshin.tgg.TripleComponent; import de.tub.tfs.henshin.tgg.TripleGraph; import de.tub.tfs.henshin.tgg.interpreter.util.ExceptionUtil; import de.tub.tfs.henshin.tgg.interpreter.util.NodeUtil; import de.tub.tfs.henshin.tggeditor.commands.create.CreateNodeCommand; import de.tub.tfs.henshin.tggeditor.commands.move.MoveDividerCommand; import de.tub.tfs.henshin.tggeditor.commands.move.MoveManyNodeObjectsCommand; import de.tub.tfs.henshin.tggeditor.commands.move.MoveNodeObjectCommand; import de.tub.tfs.henshin.tggeditor.editparts.graphical.DividerEditPart; import de.tub.tfs.henshin.tggeditor.editparts.graphical.GraphEditPart; import de.tub.tfs.henshin.tggeditor.editparts.graphical.TNodeObjectEditPart; import de.tub.tfs.henshin.tggeditor.editpolicies.TggNonResizableEditPolicy; import de.tub.tfs.henshin.tggeditor.util.GraphUtil; import de.tub.tfs.henshin.tggeditor.util.GraphicalNodeUtil; public class GraphXYLayoutEditPolicy extends XYLayoutEditPolicy implements EditPolicy { DividerEditPart div; int dview; private final static int MINIMAL_DIV_DISTANCE = 150; boolean failed; protected EditPolicy createChildEditPolicy(EditPart child) { return new TggNonResizableEditPolicy(); } protected Command createChangeConstraintCommand(EditPart child, Object constraint) { Rectangle bounds = ((FigureCanvas)child.getViewer().getControl()).getViewport().getBounds(); Rectangle newBounds = (Rectangle) constraint; // this causes a lot of shaking scroll bars, thus, deactivated /* if ((newBounds.x < bounds.x || newBounds.y < bounds.y) || (newBounds.x + newBounds.width > bounds.width || newBounds.y + newBounds.width> bounds.height)){ ((FigureCanvas)child.getViewer().getControl()).scrollSmoothTo(((Rectangle)constraint).x + ((Rectangle)constraint).width + 20, ((Rectangle)constraint).y + ((Rectangle)constraint).height + 20); ((FigureCanvas)child.getViewer().getControl()).getViewport().repaint(); }*/ MoveNodeObjectCommand c = null; if(child instanceof TNodeObjectEditPart) { TNode node = ((TNodeObjectEditPart) child).getCastedModel(); c = new MoveNodeObjectCommand(node,(TNodeObjectEditPart) child,newBounds.x,newBounds.y); } return c; } @Override protected Command getCreateCommand(CreateRequest request) { Object newObject = request.getNewObject(); if (newObject instanceof TNode){ TripleGraph graph = (TripleGraph) getHost().getModel(); Rectangle constraint = (Rectangle) getConstraintFor(request); Point location = new Point(constraint.x,constraint.y); TripleComponent nodeTripleComponent = GraphUtil.getTripleComponentForXCoordinate(((GraphEditPart)this.getHost()),location.x); CreateNodeCommand c = new CreateNodeCommand((TNode)newObject,graph, location, nodeTripleComponent); return c; } return null; } @Override protected Command getMoveChildrenCommand(Request request) { GraphEditPart gep = (GraphEditPart)this.getHost(); TripleGraph tripleGraph = ((GraphEditPart)this.getHost()).getTripleGraph(); dview = ((FigureCanvas)gep.getViewer().getControl()).getViewport().getClientArea().getLocation().x; ChangeBoundsRequest req = (ChangeBoundsRequest) request; List<?> editparts = req.getEditParts(); if (!editparts.isEmpty()) { // move nodes if (editparts.get(0) instanceof TNodeObjectEditPart) { // target component: add divider offset TNodeObjectEditPart nep = (TNodeObjectEditPart) req.getEditParts().get(0); TNode node = nep.getCastedModel(); TGG tgg = GraphicalNodeUtil.getLayoutSystem((Graph)this.getHost().getModel()); return new MoveNodeObjectCommand(nep, req); /*if (NodeUtil.isTargetNode(node)){ int posX = req.getMoveDelta().x; int offset = tripleGraph.getDividerCT_X(); if (node.getX()+posX < offset) req.getMoveDelta().setX(posX+offset); } if (canMoveNode(req)) { CompoundCommand cc = new MoveManyNodeObjectsCommand(editparts, req); // check divider location div = null; MoveDividerCommand c = makeMoveDividerCommand(cc.getCommands(), req); if (c != null) { cc.add(c); if (div != null) { makeMoveNodeCommand(div, c.getX(), cc); } } else if (failed) { cc.dispose(); cc = null; } return cc; }*/ } // move divider else if (editparts.get(0) instanceof DividerEditPart) { DividerEditPart divEdPart = (DividerEditPart)editparts.get(0); this.translateFromAbsoluteToLayoutRelative(req.getLocation()); int reqX = req.getLocation().x; MoveDividerCommand c = new MoveDividerCommand( divEdPart.getCastedModel(), reqX, divEdPart.getCastedModel().getTripleGraph().getDividerMaxY()); return c; /* CompoundCommand cc = new CompoundCommand(); DividerEditPart divEdPart = (DividerEditPart)editparts.get(0); if (divEdPart.isSC()) { int reqX = req.getLocation().x + dview; //DividerEditPart divCT = ((GraphEditPart)this.getHost()).getDividerCTpart(); if ((tripleGraph.getDividerCT_X() - reqX) >= MINIMAL_DIV_DISTANCE) { // && (reqX < divCT.getCastedModel().getDividerX())) { MoveDividerCommand c = new MoveDividerCommand( divEdPart.getCastedModel(), reqX, divEdPart.getCastedModel().getTripleGraph().getDividerMaxY()); cc.add(c); // check nodes to move and add to cc makeMoveNodeCommand(divEdPart, c.getX(), cc); } else { MoveDividerCommand c = new MoveDividerCommand( divEdPart.getCastedModel(), tripleGraph.getDividerCT_X() - MINIMAL_DIV_DISTANCE, tripleGraph.getDividerMaxY()); cc.add(c); // check nodes to move and add to cc makeMoveNodeCommand(divEdPart, c.getX(), cc); } } else { // divEdPart.getCastedModel().isIsCT() int reqX = req.getLocation().x + dview; DividerEditPart divSC = ((GraphEditPart)this.getHost()).getDividerSCpart(); if ((reqX - tripleGraph.getDividerSC_X()) >= MINIMAL_DIV_DISTANCE) { // && (reqX > divSC.getCastedModel().getDividerX())) { MoveDividerCommand c = new MoveDividerCommand( divEdPart.getCastedModel(), reqX, tripleGraph.getDividerMaxY()); cc.add(c); // check nodes to move and add to cc makeMoveNodeCommand(divEdPart, c.getX(), cc); } else { MoveDividerCommand c = new MoveDividerCommand( divEdPart.getCastedModel(), tripleGraph.getDividerSC_X() + MINIMAL_DIV_DISTANCE, tripleGraph.getDividerMaxY()); cc.add(c); // check nodes to move and add to cc makeMoveNodeCommand(divEdPart, c.getX(), cc); } } return cc; */ } } return null; } private MoveDividerCommand makeMoveDividerCommand( List<?> commands, ChangeBoundsRequest req) { TNodeObjectEditPart nodeEdPart = null; int maxX = 0; int maxW = 0; for (Object co : commands) { MoveNodeObjectCommand mnc = (MoveNodeObjectCommand) co; TNodeObjectEditPart nep = this.getNodeEditPart(mnc.getNode()); if (nodeEdPart == null) { nodeEdPart = nep; maxX = mnc.getX(); maxW = nep.getFigure().getSize().width; } else if ((mnc.getX() + nep.getFigure().getSize().width) > (maxX + maxW)) { nodeEdPart = nep; maxX = mnc.getX(); maxW = nep.getFigure().getSize().width; } } // check dividerSC MoveDividerCommand c = makeMoveDividerSCCommand( nodeEdPart, maxX, maxW, ((GraphEditPart)this.getHost()).getDividerSCpart()); div = (c != null)? ((GraphEditPart)this.getHost()).getDividerSCpart(): null; if (c == null) { // check dividerCT c = makeMoveDividerCTCommand( nodeEdPart, maxX, maxW, ((GraphEditPart)this.getHost()).getDividerCTpart()); div = (c != null)? ((GraphEditPart)this.getHost()).getDividerCTpart(): null; } return c; } private MoveDividerCommand makeMoveDividerSCCommand( TNodeObjectEditPart nodeEdPart, int maxX, int maxW, DividerEditPart divSCEdPart) { // TODO: check when this divSCEdPart can be null if (divSCEdPart==null) {ExceptionUtil.error("Divider SC edit part is missing for move"); return null;} MoveDividerCommand c = null; TripleGraph tripleGraph = divSCEdPart.getCastedModel().getTripleGraph(); TNode node = nodeEdPart.getCastedModel(); int divSC_X = tripleGraph.getDividerSC_X(); if (NodeUtil.isSourceNode(node)) { int x = maxX + maxW;// + reqX; failed = !(x < (tripleGraph.getDividerCT_X()-MINIMAL_DIV_DISTANCE)); if ((x > divSC_X) && !failed) { c = new MoveDividerCommand( divSCEdPart.getCastedModel(), (x+5), tripleGraph.getDividerMaxY()); } } else if (NodeUtil.isCorrespondenceNode(node)) { int x = maxX; failed = !(x > MINIMAL_DIV_DISTANCE); if ((x < divSC_X) && !failed) { c = new MoveDividerCommand( divSCEdPart.getCastedModel(), (x-5) , tripleGraph.getDividerMaxY()); } } return c; } private MoveDividerCommand makeMoveDividerCTCommand( TNodeObjectEditPart nodeEdPart, int maxX, int maxW, DividerEditPart divCTEdPart) { // TODO: check when this divCTEdPart can be null if (divCTEdPart==null) {ExceptionUtil.error("Divider CT edit part is missing for move"); return null;} MoveDividerCommand c = null; TripleGraph tripleGraph = divCTEdPart.getCastedModel().getTripleGraph(); TNode node = nodeEdPart.getCastedModel(); int divCT_X = tripleGraph.getDividerCT_X(); if (NodeUtil.isCorrespondenceNode(node)) { int x = maxX + maxW; if (x > divCT_X) { c = new MoveDividerCommand( divCTEdPart.getCastedModel(), (x+5), tripleGraph.getDividerMaxY()); } } else if (NodeUtil.isTargetNode(node)) { int x = maxX; failed = !(x > (tripleGraph.getDividerSC_X()+MINIMAL_DIV_DISTANCE)); if ((x < divCT_X) && !failed) { c = new MoveDividerCommand( divCTEdPart.getCastedModel(), (x-5), tripleGraph.getDividerMaxY()); } } return c; } private CompoundCommand makeMoveNodeCommand(DividerEditPart dep, int x, CompoundCommand cc) { TripleGraph tripleGraph = dep.getCastedModel().getTripleGraph(); HashMap<TripleComponent,List<TNode>> nodeSets = GraphUtil.getDistinguishedNodeSets(tripleGraph); if (dep.isSC()) { List<TNode> sourceNodes = nodeSets.get(TripleComponent.SOURCE); List<TNode> correspondenceNodes = nodeSets.get(TripleComponent.CORRESPONDENCE); if (correspondenceNodes.size() > 0) { for (TNode n: correspondenceNodes) { if (n.getX() < x) { cc.add(new MoveNodeObjectCommand(n,getNodeEditPart(n), x+5, n.getY())); } } } // handle source nodes for (TNode n: sourceNodes) { TNodeObjectEditPart nodeEdPart = getNodeEditPart(n); if (nodeEdPart != null) { int w = nodeEdPart.getFigure().getSize().width; if (n.getX()+w > x) { cc.add(new MoveNodeObjectCommand(n,getNodeEditPart(n), (x-5 -w), n.getY())); } } } } else { List<TNode> correspondenceNodes = nodeSets.get(TripleComponent.CORRESPONDENCE); List<TNode> targetNodes = nodeSets.get(TripleComponent.TARGET); if (targetNodes.size() > 0) { for (TNode n: targetNodes) { if (n.getX() < x) { cc.add(new MoveNodeObjectCommand(n,getNodeEditPart(n), (x+5), n.getY())); } } } // handle correspondence nodes for (TNode n: correspondenceNodes) { TNodeObjectEditPart nodeEdPart = getNodeEditPart(n); if (nodeEdPart != null) { int w = nodeEdPart.getFigure().getSize().width; if (n.getX()+w > x) { cc.add(new MoveNodeObjectCommand(n,getNodeEditPart(n), (x-5 -w), n.getY())); } } } } return cc; } private boolean canMoveNode(ChangeBoundsRequest req) { TGG tgg = GraphicalNodeUtil.getLayoutSystem((Graph)this.getHost().getModel()); int reqX; TNodeObjectEditPart nep = (TNodeObjectEditPart) req.getEditParts().get(0); TNode n = nep.getCastedModel(); if (req.getMoveDelta()!=null) { // automatic layouter: getMoveDelta reqX = n.getX() + req.getMoveDelta().x;// + dview; if (NodeUtil.isTargetNode(n)){ return true; } } else // request is not caused by automatic layouter, but manually reqX = req.getLocation().x;// + dview; // maxX is the maximal requested new X position int maxX = 0; // maxW is the maximal with under all nodes int maxW = 0; for (Object obj : req.getEditParts()) { if (nep == null) { nep = (TNodeObjectEditPart) obj; maxX = nep.getCastedModel().getX(); maxW = nep.getFigure().getSize().width; } else if ((nep.getCastedModel().getX() + nep.getFigure().getSize().width) > (maxX + maxW)){ nep = (TNodeObjectEditPart) obj; maxX = nep.getCastedModel().getX(); maxW = nep.getFigure().getSize().width; } } // NodeLayout nl = nep.getLayoutModel(); int divSCx = ((GraphEditPart)this.getHost()).getCastedModel().getDividerSC_X(); int divCTx = ((GraphEditPart)this.getHost()).getCastedModel().getDividerCT_X(); int divDistance = divCTx - divSCx; if (NodeUtil.isSourceNode(n)) { if ((divDistance > MINIMAL_DIV_DISTANCE) || (reqX + maxW*3/4) <= divSCx) return true; } else if (NodeUtil.isCorrespondenceNode(n)) { // node is right of source divider if (n.getX() > divSCx) { // new position is between source and target dividers if ((divSCx < reqX) && (reqX < divCTx)) return true; } } else if (NodeUtil.isTargetNode(n)) { if ((divDistance > MINIMAL_DIV_DISTANCE) || (reqX - maxW*3/4) >= divCTx) return true; } return false; } private TNodeObjectEditPart getNodeEditPart(Node n) { // Performance Hack try to find EditPart by Adapter for (Adapter a : n.eAdapters()) { try { Field field = a.getClass().getDeclaredField("this$0"); field.setAccessible(true); Object object = field.get(a); if (object instanceof TNodeObjectEditPart && ((TNodeObjectEditPart) object).getCastedModel() == n) return (TNodeObjectEditPart) object; } catch (Exception ex) { } } GraphEditPart gep = (GraphEditPart) this.getHost(); List<?> list = gep.getChildren(); for (Object child : list) { if (child instanceof TNodeObjectEditPart && ((TNodeObjectEditPart) child).getCastedModel() == n) { return (TNodeObjectEditPart) child; } } return null; } }