/* FeatureIDE - An IDE to support feature-oriented software development * Copyright (C) 2005-2009 FeatureIDE Team, University of Magdeburg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * * See http://www.fosd.de/featureide/ for further information. */ package featureide.fm.ui.editors.featuremodel.commands; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.gef.commands.Command; import featureide.fm.core.Feature; import featureide.fm.core.FeatureModel; import featureide.fm.ui.editors.FeatureUIHelper; /** * This command allows the user to move features at the feature diagram using * drag and drop. * * @author Thomas Thuem */ public class FeatureDragAndDropCommand extends Command { private final FeatureModel featureModel; private final Feature feature; private final Point newLocation; private final Feature oldParent; private final int oldIndex; private Feature newParent; private int newIndex; public FeatureDragAndDropCommand(FeatureModel featureModel, Feature feature, Point newLocation) { super("Moving " + feature.getName()); this.featureModel = featureModel; this.feature = feature; this.newLocation = newLocation; oldParent = feature.getParent(); oldIndex = oldParent != null ? oldParent.getChildIndex(feature) : 0; } @Override public boolean canExecute() { Point referencePoint = FeatureUIHelper.getSourceLocation(feature,newLocation); Feature next = calculateNext(featureModel.getRoot(), referencePoint); //calculate new parent (if exists) if (!calculateNewParentAndIndex(next)) return false; //no new positions possible next to same feature if (next == feature) return false; //not accept the same position if (oldParent == newParent && oldIndex == newIndex) return false; //not accept leaves as parent if (!newParent.canHaveChildren()) return false; //not accept moves to children positions return feature != newParent && !feature.isAncestorOf(newParent); } @Override public void execute() { boolean deleteParent = oldParent.isAbstract() && oldParent.getChildrenCount() == 1; oldParent.removeChild(feature); newParent.addChildAtPosition(newIndex, feature); if (deleteParent) featureModel.deleteFeature(oldParent); featureModel.handleModelDataChanged(); } @Override public void undo() { newParent.removeChild(feature); //TODO #46: implement undo for deleted compound features if (oldParent != null) oldParent.addChildAtPosition(oldIndex, feature); featureModel.handleModelDataChanged(); } private boolean calculateNewParentAndIndex(Feature next) { Point location = FeatureUIHelper.getSourceLocation(feature,newLocation); Point nextLocation = FeatureUIHelper.getTargetLocation(next); Dimension d = location.getDifference(nextLocation); // if (Math.abs(d.width) < d.height) { if (d.height > 0) { //insert below newParent = next; newIndex = 0; for (Feature child : next.getChildren()) { Dimension cd = FeatureUIHelper.getSourceLocation(child).getDifference(nextLocation); if (d.width / (double) d.height <= cd.width / (double) cd.height) break; else newIndex++; } } else { //insert left or right if (next.isRoot()) { //do not accept because root has no parent return false; } else { newParent = next.getParent(); if (d.width < 0) newIndex = newParent.getChildIndex(next); else newIndex = newParent.getChildIndex(next) + 1; } } if (newParent == oldParent && oldParent.getChildIndex(feature) < newIndex) newIndex--; return true; } public static Feature calculateNext(Feature feature, Point referencePoint) { if (feature == null) return null; Feature next = feature; double distance = FeatureUIHelper.getTargetLocation(next).getDistance(referencePoint); for (Feature child : feature.getChildren()) { Feature childsNext = calculateNext(child, referencePoint); double newDistance = FeatureUIHelper.getTargetLocation(childsNext).getDistance(referencePoint); if (newDistance > 0 && newDistance < distance) { next = childsNext; distance = newDistance; } } return next; } public Feature getFeature() { return feature; } public Feature getNewParent() { return newParent; } }