/**********************************************
* 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.gxt.widget;
import java.util.ArrayList;
import java.util.List;
import org.vectomatic.dom.svg.OMSVGElement;
import org.vectomatic.dom.svg.OMSVGMatrix;
import org.vectomatic.dom.svg.OMSVGPoint;
import org.vectomatic.dom.svg.OMSVGRect;
import org.vectomatic.dom.svg.OMSVGSVGElement;
import org.vectomatic.dom.svg.impl.SVGSVGElement;
import org.vectomatic.dom.svg.itf.ISVGLocatable;
import org.vectomatic.dom.svg.utils.SVGConstants;
import org.vectomatic.svg.edit.client.AppBundle;
import org.vectomatic.svg.edit.client.AppCss;
import org.vectomatic.svg.edit.client.command.dnd.IDndHandler;
import org.vectomatic.svg.edit.client.model.svg.SVGElementModel;
import com.extjs.gxt.ui.client.event.DNDEvent;
import com.extjs.gxt.ui.client.event.DragEvent;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.user.client.Element;
/**
* Class to give users visual feedback on the drag operation
* about to be performed
* @author laaglu
*/
public class DNDGhost {
private static final String ATT_OPERATION = "operation";
/**
* To update the DND operation
*/
private static SVGSVGElement operationElement;
private static DivElement messageElement;
private DivElement ghostElement;
private List<SVGElementModel> sourceElements;
public DNDGhost(List<SVGElementModel> sourceElements, DNDEvent event) {
/*
* The custom ghost has the following css structure
* custom ghost container div (ghost)
* source div (ghost-source)
* operation div (ghost-operation)
* message div (ghost-message)
*/
this.sourceElements = sourceElements;
// Create the SVG miniature of the first element in the source
AppCss css = AppBundle.INSTANCE.css();
OMSVGSVGElement svg = new OMSVGSVGElement();
OMSVGElement svgElement = sourceElements.get(0).getElementWrapper();
ISVGLocatable locatable = (ISVGLocatable) svgElement;
OMSVGRect bbox = getScreenBBox(svg, locatable);
OMSVGRect viewBox = svg.getViewBox().getBaseVal();
bbox.assignTo(viewBox);
OMSVGElement clone = (OMSVGElement) svgElement.cloneNode(true);
OMSVGMatrix m = locatable.getCTM();
if (!m.isIdentity()) {
StringBuilder builder = new StringBuilder();
builder.append(SVGConstants.TRANSFORM_MATRIX + "(");
builder.append(m.getDescription());
builder.append(")");
clone.setAttribute(SVGConstants.SVG_TRANSFORM_ATTRIBUTE, builder.toString());
} else {
clone.removeAttribute(SVGConstants.SVG_TRANSFORM_ATTRIBUTE);
}
svg.appendChild(clone);
svg.setClassNameBaseVal(css.ghostSource());
// Create the operation icon
if (operationElement == null) {
operationElement = (SVGSVGElement) AppBundle.INSTANCE.dndIcons().getSvg().getElement();
operationElement.getClassName_().setBaseVal(css.ghostOperation());
}
// Create the DND message
if (messageElement == null) {
messageElement = Document.get().createDivElement();
messageElement.setClassName(css.ghostMessage());
}
// Create the ghost
ghostElement = Document.get().createDivElement();
ghostElement.setClassName(css.ghost());
ghostElement.appendChild(svg.getElement());
ghostElement.appendChild(operationElement);
ghostElement.appendChild(messageElement);
DragEvent dragEvent = event.getDragEvent();
dragEvent.setWidth(120);
dragEvent.setHeight(94);
}
public void update(IDndHandler dndHandler) {
operationElement.setAttribute(ATT_OPERATION, dndHandler.getOperationCssAttr());
messageElement.setInnerText(dndHandler.getMessage(sourceElements));
}
public Element getElement() {
return (com.google.gwt.user.client.Element) ghostElement.cast();
}
private static OMSVGRect getScreenBBox(OMSVGSVGElement svg, ISVGLocatable locatable) {
OMSVGRect bbox = locatable.getBBox();
OMSVGMatrix m = locatable.getCTM();
List<OMSVGPoint> list = new ArrayList<OMSVGPoint>();
list.add(svg.createSVGPoint(bbox.getX(), bbox.getY()).matrixTransform(m));
list.add(svg.createSVGPoint(bbox.getMaxX(), bbox.getY()).matrixTransform(m));
list.add(svg.createSVGPoint(bbox.getX(), bbox.getMaxY()).matrixTransform(m));
list.add(svg.createSVGPoint(bbox.getMaxX(), bbox.getMaxY()).matrixTransform(m));
float xmin = list.get(0).getX(), ymin = list.get(0).getY(), xmax = list.get(0).getX(), ymax = list.get(0).getY();
for (int i = 1, size = list.size(); i < size; i++) {
OMSVGPoint p = list.get(i);
xmin = Math.min(p.getX(), xmin);
ymin = Math.min(p.getY(), ymin);
xmax = Math.max(p.getX(), xmax);
ymax = Math.max(p.getY(), ymax);
}
OMSVGRect screenBBox = svg.createSVGRect(xmin, ymin, xmax - xmin, ymax - ymin);
GWT.log("m="+m.getDescription());
GWT.log("bbox="+bbox.getDescription());
GWT.log("screenBBox="+screenBBox.getDescription());
return screenBBox;
}
}