/**********************************************
* Copyright (C) 2011 Lukas Laag
* This file is part of svgreal.
*
* svgreal 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 3 of the License, or
* (at your option) any later version.
*
* svgreal 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 svgreal. If not, see http://www.gnu.org/licenses/
**********************************************/
package org.vectomatic.svg.edit.client.command.dnd;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.vectomatic.dom.svg.OMSVGElement;
import org.vectomatic.dom.svg.itf.ISVGTransformable;
import org.vectomatic.dom.svg.utils.SVGConstants;
import org.vectomatic.svg.edit.client.command.CommandFactories;
import org.vectomatic.svg.edit.client.command.DndCommandFactory.DropGesture;
import org.vectomatic.svg.edit.client.engine.SVGModel;
import org.vectomatic.svg.edit.client.model.ModelConstants;
import org.vectomatic.svg.edit.client.model.svg.SVGElementModel;
import org.vectomatic.svg.edit.client.model.svg.SVGNamedElementModel;
import com.extjs.gxt.ui.client.data.BeanModel;
import com.extjs.gxt.ui.client.data.BeanModelLookup;
import com.extjs.gxt.ui.client.util.Format;
/**
* Class to represent a tree reordering command
* @author laaglu
*/
public class MoveCommand extends DndCommandBase {
/**
* The models to move, unsorted
*/
protected List<SVGElementModel> models;
/**
* The models to move, sorted
*/
protected List<SVGElementModel> sortedModels;
/**
* The parent models of the models to move, sorted
*/
protected List<SVGElementModel> parentModels;
/**
* The next non-null siblings of the models to move, sorted, or null if no such siblings exist
*/
protected List<SVGElementModel> refModels;
/**
* The transform attributes of the models to move, sorted
*/
protected List<String> xforms;
/**
* The target model
*/
protected SVGElementModel target;
/**
* The drop gesture
*/
protected DropGesture dropGesture;
public MoveCommand(List<SVGElementModel> models, SVGElementModel target, DropGesture dropGesture) {
super(CommandFactories.getDndCommandFactory());
this.target = target;
this.dropGesture = dropGesture;
this.models = new ArrayList<SVGElementModel>(models);
// Sort nodes according to their order in the tree.
Collections.<SVGElementModel>sort(models, SVGElementModel.getAscendingCompataror());
this.sortedModels = models;
Set<SVGElementModel> modelSet = new HashSet<SVGElementModel>(models);
parentModels = new ArrayList<SVGElementModel>();
refModels = new ArrayList<SVGElementModel>();
xforms = new ArrayList<String>();
for (SVGElementModel model : sortedModels) {
SVGElementModel parentModel = (SVGElementModel) model.getParent();
parentModels.add(parentModel);
// Retrieve the next sibling not in the set of models to delete
SVGElementModel siblingModel = model;
while (((siblingModel = siblingModel.getNextSibling()) != null) && modelSet.contains(siblingModel)) {
}
refModels.add(siblingModel);
xforms.add(model.<String>get(SVGConstants.SVG_TRANSFORM_ATTRIBUTE));
}
}
@Override
public String getDescription() {
String message = null;
switch(dropGesture) {
case OnNode:
message = ModelConstants.INSTANCE.dndReorderCmdIn();
break;
case BeforeNode:
message = ModelConstants.INSTANCE.dndReorderCmdBefore();
break;
case AfterNode:
message = ModelConstants.INSTANCE.dndReorderCmdAfter();
break;
}
return Format.substitute(message, SVGNamedElementModel.getNames(models), target.toString(), target.getOwner().getRoot().get(SVGConstants.SVG_TITLE_TAG));
}
@Override
public void commit() {
SVGModel owner = target.getOwner();
SVGElementModel parentModel = null;
SVGElementModel refModel = null;
if (dropGesture == DropGesture.OnNode) {
// target is a folder
parentModel = target;
} else {
// target is a leaf in the same document
parentModel = (SVGElementModel) target.getParent();
if (dropGesture == DropGesture.BeforeNode) {
refModel = target;
} else if (dropGesture == DropGesture.AfterNode) {
refModel = target.getNextSibling();
}
}
for (SVGElementModel model : models) {
// Update the transform of a model which is going to be
// attached to a parent model so that the model appears
// unchanged to the end-user. This is done by applying
// a counter transform (the inverse of the transform
// of the parent) to to nullify its effect
OMSVGElement element = model.getElementWrapper();
OMSVGElement parentElement = parentModel.getElementWrapper();
model.updateTransform(((ISVGTransformable)element).getTransformToElement(parentElement));
owner.insertBefore(parentModel, model, refModel);
}
}
@Override
public void rollback() {
SVGModel owner = target.getOwner();
for (int i = 0, size = sortedModels.size(); i < size; i++) {
SVGElementModel model = sortedModels.get(i);
SVGElementModel parentModel = parentModels.get(i);
SVGElementModel refModel = refModels.get(i);
owner.insertBefore(parentModel, model, refModel);
String matrix = xforms.get(i);
if (matrix == null || matrix.length() == 0) {
model.remove(SVGConstants.SVG_TRANSFORM_ATTRIBUTE);
} else {
model.set(SVGConstants.SVG_TRANSFORM_ATTRIBUTE, matrix);
}
}
}
@Override
public BeanModel asModel() {
return BeanModelLookup.get().getFactory(MoveCommand.class).createModel(this);
}
}