/**********************************************
* 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 org.vectomatic.svg.edit.client.SVGWindow;
import org.vectomatic.svg.edit.client.command.CommandFactories;
import org.vectomatic.svg.edit.client.command.DndCommandFactory;
import org.vectomatic.svg.edit.client.command.DndCommandFactory.DropGesture;
import org.vectomatic.svg.edit.client.engine.SVGModel;
import org.vectomatic.svg.edit.client.engine.SVGProcessor;
import org.vectomatic.svg.edit.client.model.svg.SVGElementModel;
import com.extjs.gxt.ui.client.core.El;
import com.extjs.gxt.ui.client.dnd.DropTarget;
import com.extjs.gxt.ui.client.dnd.Insert;
import com.extjs.gxt.ui.client.dnd.ScrollSupport;
import com.extjs.gxt.ui.client.event.DNDEvent;
import com.extjs.gxt.ui.client.util.Rectangle;
import com.extjs.gxt.ui.client.widget.treepanel.TreePanel.TreeNode;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Timer;
/**
* Drop target for SVGWindow
* @author laaglu
*/
public class SVGTreePanelDropTarget extends DropTarget {
protected DndCommandFactory dndCommandFactory;
protected TreePanelExt<SVGElementModel> tree;
protected TreeNode activeItem, appendItem;
protected DropGesture dropGesture;
private boolean restoreTrackMouse;
private ScrollSupport scrollSupport;
protected SVGModel svgModel;
public SVGTreePanelDropTarget(SVGWindow window) {
super(window.getTree());
this.tree = window.getTree();
svgModel = window.getSvgModel();
dndCommandFactory = CommandFactories.getDndCommandFactory();
}
protected void clearStyles(DNDEvent event) {
Insert.get().hide();
event.getStatus().setStatus(false);
if (activeItem != null) {
tree.getView().onDropChange(activeItem, false);
}
}
@Override
protected void onDragDrop(DNDEvent event) {
GWT.log("SVGTreePanelDropTarget.onDragDrop");
super.onDragDrop(event);
if (event.getData() == null)
return;
tree.getView().onDropChange(activeItem, false);
dndCommandFactory.processDragAndDrop(dropGesture);
tree.setTrackMouseOver(restoreTrackMouse);
activeItem = null;
appendItem = null;
scrollSupport.stop();
}
@Override
protected void onDragEnter(DNDEvent e) {
super.onDragEnter(e);
e.getStatus().setStatus(false);
restoreTrackMouse = tree.isTrackMouseOver();
tree.setTrackMouseOver(false);
if (scrollSupport == null) {
scrollSupport = new ScrollSupport(tree.el());
}
scrollSupport.start();
}
@Override
protected void onDragFail(DNDEvent event) {
super.onDragFail(event);
scrollSupport.stop();
}
@Override
protected void onDragLeave(DNDEvent e) {
super.onDragLeave(e);
if (activeItem != null) {
tree.getView().onDropChange(activeItem, false);
activeItem = null;
}
tree.setTrackMouseOver(restoreTrackMouse);
scrollSupport.stop();
}
@Override
protected void onDragMove(DNDEvent event) {
event.setCancelled(false);
}
@Override
protected void showFeedback(DNDEvent event) {
// GWT.log("showFeedback");
// Determine the tree node over which the cursor is hovering, if any
final TreeNode overItem = tree.findNode(event.getTarget());
if (!dndCommandFactory.isValidDropTarget(getModel(overItem))) {
// Not over a tree item
clearStyles(event);
return;
}
handleInsert(event, overItem);
}
protected void handleInsert(DNDEvent event, final TreeNode item) {
// Determine the position of the cursor with regards to the drop item
// to find out if the drag source should be inserted before or after
// the drop item
int height = item.getElement().getOffsetHeight();
int mid = height / 2;
int top = item.getElement().getAbsoluteTop();
mid += top;
int y = event.getClientY();
boolean before = y < mid;
if ((!item.isLeaf() || SVGProcessor.isGroupElement(getModel(item).getElement()))
&& ((before && y > top + 4) || (!before && y < top + height - 4))) {
handleAppend(event, item);
return;
}
dropGesture = before ? DropGesture.BeforeNode : DropGesture.AfterNode;
// clear any active append item
if (activeItem != null) {
tree.getView().onDropChange(activeItem, false);
}
appendItem = null;
activeItem = item;
int idx = -1;
if (activeItem.getParent() == null) {
idx = tree.getStore().getRootItems().indexOf(activeItem);
} else {
idx = activeItem.getParent().indexOf(item);
}
event.getStatus().setStatus(true);
showInsert(event, item.getElement(), before);
}
protected void handleAppend(DNDEvent event, final TreeNode item) {
dropGesture = DropGesture.OnNode;
Insert.get().hide();
event.getStatus().setStatus(true);
// clear any active append item
if (activeItem != null) {
tree.getView().onDropChange(activeItem, false);
}
if (item != null && item != appendItem && !item.isExpanded()) {
Timer t = new Timer() {
@Override
public void run() {
if (item == appendItem) {
item.setExpanded(true);
} else {
}
}
};
// auto-expand delay
t.schedule(800);
}
appendItem = item;
activeItem = item;
if (activeItem != null) {
tree.getView().onDropChange(activeItem, true);
}
}
private void showInsert(DNDEvent event, Element elem, boolean before) {
Insert insert = Insert.get();
insert.show(elem);
Rectangle rect = El.fly(elem).getBounds();
int y = before ? rect.y - 2 : (rect.y + rect.height - 4);
insert.setBounds(rect.x, y, rect.width, 6);
}
public SVGElementModel getActiveItem() {
return getModel(activeItem);
}
public SVGElementModel getModel(TreeNode treeNode) {
if (treeNode != null) {
return (SVGElementModel)treeNode.getModel();
}
return null;
}
public SVGModel getSvgModel() {
return svgModel;
}
}