/************************************************************************************
* Copyright (c) 2008 William Chen. *
* *
* 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 *
* *
* Use is subject to the terms of Eclipse Public License v1.0. *
* *
* Contributors: *
* William Chen - initial API and implementation. *
************************************************************************************/
package org.dyno.visual.swing.editors;
import java.awt.Component;
import java.awt.Container;
import javax.swing.SwingUtilities;
import org.dyno.visual.swing.designer.VisualDesigner;
import org.dyno.visual.swing.plugin.spi.CompositeAdapter;
import org.dyno.visual.swing.plugin.spi.WidgetAdapter;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.TreeDropTargetEffect;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
class OutlineViewDnD extends DropTargetAdapter implements DragSourceListener {
private TreeViewer treeView;
private Tree tree;
private TreeItem[] treeItems;
private Display display;
private VisualDesigner designer;
public OutlineViewDnD(VisualDesigner designer) {
this.designer = designer;
}
public void attach(TreeViewer treeView) {
this.treeView = treeView;
this.tree = treeView.getTree();
display = tree.getDisplay();
Transfer[] types = new Transfer[] { new JComponentTransfer() };
int operations = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK;
final DragSource source = new DragSource(tree, operations);
source.setTransfer(types);
treeItems = new TreeItem[1];
source.addDragListener(this);
DropTarget target = new DropTarget(tree, operations);
target.setDropTargetEffect(new MyDropTargetEffect(tree));
target.setTransfer(types);
target.addDropListener(this);
}
public void dragFinished(DragSourceEvent event) {
if (event.detail == DND.DROP_MOVE) {
for (TreeItem item : treeItems)
item.dispose();
treeView.refresh();
designer.repaint();
designer.publishSelection();
}
treeItems = null;
}
public void dragSetData(DragSourceEvent event) {
Component[] components = new Component[treeItems.length];
for (int i = 0; i < treeItems.length; i++) {
components[i] = (Component) treeItems[i].getData();
}
event.data = components;
}
public void dragStart(DragSourceEvent event) {
TreeItem[] selection = tree.getSelection();
if (selection.length > 0) {
Container parent = null;
for (TreeItem item : selection) {
Object object = item.getData();
if (!(object instanceof Component)) {
event.doit = false;
return;
} else {
Component comp = (Component) object;
WidgetAdapter adapter = WidgetAdapter
.getWidgetAdapter(comp);
if (adapter.isRoot()) {
event.doit = false;
return;
} else {
if (parent == null)
parent = comp.getParent();
else if (parent != comp.getParent()) {
event.doit = false;
return;
}
}
}
}
treeItems = selection;
event.doit = true;
} else {
event.doit = false;
}
}
public void dragOver(DropTargetEvent event) {
event.feedback = DND.FEEDBACK_EXPAND | DND.FEEDBACK_SCROLL;
if (event.item != null) {
TreeItem item = (TreeItem) event.item;
Point pt = display.map(null, tree, event.x, event.y);
Rectangle bounds = item.getBounds();
if (pt.y < bounds.y + bounds.height / 4) {
event.feedback |= DND.FEEDBACK_INSERT_BEFORE;
} else if (pt.y > bounds.y + 3 * bounds.height / 4) {
event.feedback |= DND.FEEDBACK_INSERT_AFTER;
} else {
event.feedback |= DND.FEEDBACK_SELECT;
}
}
}
private void moveToSelectedNode(CompositeAdapter parent_adapter,
Component target_comp, DropTargetEvent event, Component[] components) {
if (parent_adapter.getWidget() == target_comp) {
event.detail = DND.DROP_NONE;
} else {
WidgetAdapter target_adapter = WidgetAdapter
.getWidgetAdapter(target_comp);
CompositeAdapter target_parent = target_adapter.getParentAdapter();
if (target_parent == parent_adapter) {
if (!(target_adapter instanceof CompositeAdapter)) {
event.detail = DND.DROP_NONE;
return;
}
}
for (Component component : components) {
if (component == target_comp) {
event.detail = DND.DROP_NONE;
return;
}
if (SwingUtilities.isDescendingFrom(target_comp, component)) {
event.detail = DND.DROP_NONE;
return;
}
}
for (Component component : components) {
parent_adapter.removeChild(component);
if (target_adapter instanceof CompositeAdapter)
((CompositeAdapter) target_adapter).addChild(component);
}
target_adapter.getRootAdapter().getWidget().validate();
}
}
private void moveBeforeSelectedNode(CompositeAdapter parent_adapter,
Component target_comp, DropTargetEvent event, Component[] components) {
if (parent_adapter.getWidget() == target_comp) {
event.detail = DND.DROP_NONE;
} else {
WidgetAdapter target_adapter = WidgetAdapter
.getWidgetAdapter(target_comp);
CompositeAdapter target_parent = target_adapter.getParentAdapter();
for (Component component : components) {
if (component == target_comp) {
event.detail = DND.DROP_NONE;
return;
}
if (SwingUtilities.isDescendingFrom(target_comp, component)) {
event.detail = DND.DROP_NONE;
return;
}
}
for (Component component : components) {
parent_adapter.removeChild(component);
target_parent.addBefore(target_comp, component);
}
}
}
private void moveAfterSelectedNode(CompositeAdapter parent_adapter,
Component target_comp, DropTargetEvent event, Component[] components) {
if (parent_adapter.getWidget() == target_comp) {
event.detail = DND.DROP_NONE;
} else {
WidgetAdapter target_adapter = WidgetAdapter
.getWidgetAdapter(target_comp);
CompositeAdapter target_parent = target_adapter.getParentAdapter();
for (Component component : components) {
if (component == target_comp) {
event.detail = DND.DROP_NONE;
return;
}
if (SwingUtilities.isDescendingFrom(target_comp, component)) {
event.detail = DND.DROP_NONE;
return;
}
}
for (Component component : components) {
parent_adapter.removeChild(component);
target_parent.addAfter(target_comp, component);
}
}
}
public void drop(DropTargetEvent event) {
if (event.data == null) {
event.detail = DND.DROP_NONE;
return;
} else if (event.item == null) {
event.detail = DND.DROP_NONE;
} else {
TreeItem target_item = (TreeItem) event.item;
Object data = target_item.getData();
if (!(data instanceof Component)) {
event.detail = DND.DROP_NONE;
} else {
Component[] components = (Component[]) event.data;
if (components.length == 0) {
event.detail = DND.DROP_NONE;
} else {
Component first = components[0];
WidgetAdapter first_adapter = WidgetAdapter
.getWidgetAdapter(first);
CompositeAdapter parent_adapter = first_adapter
.getParentAdapter();
if ((feedback & DND.FEEDBACK_SELECT) != 0) {
moveToSelectedNode(parent_adapter, target_comp, event,
components);
} else if ((feedback & DND.FEEDBACK_INSERT_BEFORE) != 0) {
moveBeforeSelectedNode(parent_adapter, target_comp,
event, components);
} else if ((feedback & DND.FEEDBACK_INSERT_AFTER) != 0) {
moveAfterSelectedNode(parent_adapter, target_comp,
event, components);
} else {
event.detail = DND.DROP_NONE;
}
}
}
}
}
private int feedback;
private Component target_comp;
private class MyDropTargetEffect extends TreeDropTargetEffect {
public MyDropTargetEffect(Tree tree) {
super(tree);
}
@Override
public void dragOver(DropTargetEvent event) {
feedback = event.feedback;
if (event.item.getData() instanceof Component)
target_comp = (Component) event.item.getData();
super.dragOver(event);
}
}
}