/*******************************************************************************
* Copyright (c) 2007 Exadel, Inc. and Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is 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
*
* Contributors:
* Exadel, Inc. and Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.common.model.ui.dnd;
import java.util.*;
import org.eclipse.core.runtime.Platform;
import org.eclipse.swt.dnd.*;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.*;
import org.jboss.tools.common.model.options.impl.PaletteAdopt;
import org.jboss.tools.common.model.ui.navigator.TreeViewerDragDropProvider;
import org.jboss.tools.common.meta.XAdoptManager;
import org.jboss.tools.common.model.event.*;
import org.jboss.tools.common.model.util.ModelFeatureFactory;
import org.jboss.tools.common.model.XModelException;
import org.jboss.tools.common.model.XModelObject;
import org.jboss.tools.common.model.XModelTransferBuffer;
import org.jboss.tools.common.model.ui.ModelUIPlugin;
public class ControlDragDrop {
static XAdoptManager paletteAdopt;
static {
XAdoptManager[] ms = new PaletteAdopt().getManagers();
if(ms != null && ms.length > 0) {
paletteAdopt = ms[0];
}
}
protected IControlDragDropProvider provider;
public ControlDragDrop() {}
public Control getControl() {
return provider.getControl();
}
public void setProvider(IControlDragDropProvider provider) {
this.provider = provider;
}
protected XModelObject getModelObjectForWidget(Widget widget) {
return provider.getModelObjectForWidget(widget);
}
protected XModelObject getLeadSelection() {
Widget[] w = provider.getSelection();
return (w == null || w.length == 0) ? null : getModelObjectForWidget(w[0]);
}
protected XModelObject[] getMultiSelection() {
Widget[] w = provider.getSelection();
if(w == null || w.length < 2) return null;
XModelObject[] os = new XModelObject[w.length];
for (int i = 0; i < os.length; i++) os[i] = getModelObjectForWidget(w[i]);
return os;
}
public void enable() {
enableDrag();
enableDrop();
}
public void enableDrag() {
DragSource ds = new DragSource(getControl(), DND.DROP_MOVE | DND.DROP_COPY);
Transfer[] ts = new Transfer[]{ModelTransfer.getInstance(), TextTransfer.getInstance()};
ds.setTransfer(ts);
ds.addDragListener(new DSL());
}
public void enableDrop() {
DropTarget old = (DropTarget)getControl().getData("DropTarget"); //$NON-NLS-1$
if(old != null) {
old.setTransfer(new Transfer[]{ModelTransfer.getInstance(), TextTransfer.getInstance()});
old.addDropListener(new DTL());
} else {
DropTarget target = new DropTarget(getControl(), DND.DROP_MOVE | DND.DROP_COPY);
Transfer[] types = new Transfer[] {ModelTransfer.getInstance()};
target.setTransfer(types);
target.addDropListener(new DTL());
}
}
XModelObject draggedObject = null;
class DSL implements DragSourceListener {
public void dragStart(DragSourceEvent e) {
XModelObject o = getLeadSelection();
XModelObject[] targets = (o == null) ? null : getMultiSelection();
XModelTransferBuffer.getInstance().enable();
e.doit = (DnDUtil.isCopyEnabled(o, targets) && DnDUtil.copy(o, targets));
if(e.doit) {
draggedObject = o;
} else {
XModelTransferBuffer.getInstance().disable();
}
}
public void dragSetData(DragSourceEvent event) {
if (ModelTransfer.getInstance().isSupportedType(event.dataType)) {
XModelObject o = getLeadSelection();
/// event.data = (o == null) ? new String[0] : new String[]{o.getPresentationString()};
event.data = (o == null) ? "" : o.getPresentationString(); //$NON-NLS-1$
} else if(TextTransfer.getInstance().isSupportedType(event.dataType)) {
XModelObject o = getLeadSelection();
event.data = (o == null) ? "" : o.getPresentationString(); //$NON-NLS-1$
}
}
public void dragFinished(DragSourceEvent event) {
XModelTransferBuffer.getInstance().disable();
if (event.detail == DND.DROP_MOVE) {}
}
}
class Scroller implements Runnable {
private long border_time = -1;
private int border = -1;
private long item_time = -1;
private Widget item = null;
int status = 0;
boolean scrollPending = false;
boolean expandPending = false;
public void run() {
while(status == 1) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
ignore();
}
long t = System.currentTimeMillis();
if(item != null && t > item_time) {
expandPending = true;
item_time = -1;
}
if(border != -1 && t > border_time) {
border_time += 500;
scrollPending = true;
}
}
status = 0;
}
public void start() {
if(status == 0) {
status = 1;
new Thread(this).start();
}
}
public void stop() {
status = 2;
}
Widget lastItem = null;
public void update(Widget w, int x, int y) {
if (w != null && lastItem != w) lastItem = w;
if(scrollPending) fireScroll();
if(expandPending) fireExpand();
if(w != item) {
item = w;
if(item != null) item_time = System.currentTimeMillis() + 1500;
}
Scrollable sc = (provider.getControl() instanceof Scrollable) ? (Scrollable)provider.getControl() : null;
if(sc == null) return;
Rectangle c = sc.getClientArea();
int yp_t = sc.toDisplay(new Point(c.x, c.y)).y;
int yp_b = yp_t + c.height;
if(yp_t < y && yp_t + 20 > y) {
if(border != 1) {
border = 1;
border_time = System.currentTimeMillis() + 500;
}
} else if(y < yp_b && y + 20 > yp_b) {
if(border != 2) {
border = 2;
border_time = System.currentTimeMillis() + 500;
}
} else {
if(border != -1) {
border = -1;
border_time = -1;
}
}
}
protected void fireExpand() {
expandPending = false;
if(!(item instanceof TreeItem)) return;
if(provider instanceof TreeViewerDragDropProvider) {
TreeViewerDragDropProvider p = (TreeViewerDragDropProvider)provider;
p.expand(item);
}
}
protected void fireScroll() {
scrollPending = false;
if(!(provider.getControl() instanceof Tree) || lastItem == null) return;
Tree tree = (Tree)provider.getControl();
TreeItem i = (TreeItem)lastItem;
// Rectangle r = i.getBounds();
Scrollable sc = (provider.getControl() instanceof Scrollable) ? (Scrollable)provider.getControl() : null;
if(sc == null || sc.getVerticalBar() == null) return;
if(border == 1 && sc != null) {
TreeItem i2 = getPrevTreeItem(tree, i);
if(i2 != null) tree.showItem(i2);
} else if(border == 2 && sc != null) {
TreeItem i2 = getNextTreeItem(tree, i);
if(i2 != null) tree.showItem(i2);
}
}
private TreeItem getNextTreeItem (Tree tree, TreeItem item) {
TreeItem result = null;
Vector<TreeItem> rows = getTreeExpandedItems(tree);
int index = rows.indexOf(item);
if (index > -1) {
result = (index + 1 < rows.size() ? rows.get(index + 1) : item);
} else {
result = item;
}
return result;
}
private TreeItem getPrevTreeItem (Tree tree, TreeItem item) {
TreeItem result = null;
Vector<TreeItem> rows = getTreeExpandedItems(tree);
int index = rows.indexOf(item);
if (index > 0) {
result = (TreeItem)rows.get(index - 1);
} else {
result = item;
}
return result;
}
}
class DTL implements DropTargetListener {
private boolean isTextDrop = false;
private boolean isPaletteDrop = false;
private Scroller scroller = new Scroller();
private Widget lastItem = null;
public void dragEnter(DropTargetEvent event) {
scroller.start();
}
public void dragOver(DropTargetEvent event) {
if (event.item != null && lastItem != event.item)
lastItem = event.item;
scroller.update(event.item, event.x, event.y);
{
XModelObject o = getModelObjectForWidget(event.item);
if(o == null) {
event.detail = DND.DROP_NONE;
return;
}
boolean enabled = DnDUtil.isPasteEnabled(o);
isTextDrop = false;
isPaletteDrop = false;
Properties p = provider.getDropProperties(event.x, event.y);
if(p != null && ("none".equals(p.getProperty("text-context")))) { //$NON-NLS-1$ //$NON-NLS-2$
enabled = false;
} else if(!enabled && o.isObjectEditable()) {
if(p != null && "true".equals(p.getProperty("accepsAsString"))) { //$NON-NLS-1$ //$NON-NLS-2$
if(isAdoptableMacro(o)) {
isPaletteDrop = true;
} else {
isTextDrop = true;
}
enabled = true;
}
}
event.detail = (enabled) ? DND.DROP_MOVE : DND.DROP_NONE;
}
}
boolean isAdoptableMacro(XModelObject target) {
if(paletteAdopt == null) return false;
if(target.getFileType() != XModelObject.FILE) return false;
XModelObject source = target.getModel().getModelBuffer().source();
if(source == null) return false;
return paletteAdopt.isAdoptable(target, source);
}
public void dragLeave(DropTargetEvent event) {
scroller.stop();
}
public void dragOperationChanged(DropTargetEvent event) {};
public void dropAccept(DropTargetEvent event) {}
public void drop(DropTargetEvent event) {
scroller.stop();
// if(event.currentDataType.type != ModelTransfer.TYPE) {
// event.detail = DND.DROP_NONE;
// } else
{
Widget w = event.item;
if(provider instanceof TreeViewerDragDropProvider) {
w = lastItem;
}
XModelObject o = getModelObjectForWidget(w);
if(o == null) return;
try {
Properties p = provider.getDropProperties(event.x, event.y);
if(p == null) p = new Properties();
p.setProperty("isDrop", "true"); //$NON-NLS-1$ //$NON-NLS-2$
if(isTextDrop) {
XModelObject s = o.getModel().getModelBuffer().source();
if(s == null) return;
p.put("start text", "" + s.getPresentationString()); //$NON-NLS-1$ //$NON-NLS-2$
} else if(isPaletteDrop) {
paletteAdopt.adopt(o, o.getModel().getModelBuffer().source(), p);
} else if(DnDUtil.isPasteEnabled(o)) {
DnDUtil.paste(o, p);
}
if(provider instanceof IControlDropListener) {
p.put("drop.x", Integer.valueOf(event.x)); //$NON-NLS-1$
p.put("drop.y", Integer.valueOf(event.y)); //$NON-NLS-1$
if(draggedObject != null) p.put("draggedObject", draggedObject); //$NON-NLS-1$
((IControlDropListener)provider).drop(p);
}
} catch (ActionDeclinedException ade) {
ignore();
} catch (XModelException e) {
ModelUIPlugin.getPluginLog().logError(e);
}
}
}
}
void ignore() {
//do nothing
}
private static Vector<TreeItem> getTreeExpandedItems(Tree tree) {
TreeItem[] items = tree.getItems();
Vector<TreeItem> result = new Vector<TreeItem>();
for (int i = 0; items != null && i < items.length; i++) {
result.add(result.size(), items[i]);
if (items[i].getExpanded()) fillTreeItemRows(items[i].getItems(), result);
}
return result;
}
private static Vector<TreeItem> fillTreeItemRows(TreeItem[] items, Vector<TreeItem> result){
if (result == null) result = new Vector<TreeItem>();
for (int i = 0; items != null && i < items.length; i++) {
result.add(result.size(), items[i]);
if (items[i].getExpanded()) fillTreeItemRows(items[i].getItems(), result);
}
return result;
}
public Widget findTreeItem (int x, int y) {
Tree tree = (provider.getControl() instanceof Tree) ? (Tree)provider.getControl() : null;
Vector<TreeItem> items = getTreeExpandedItems(tree);
for (int i = 0; i < items.size(); i++) {
TreeItem item = items.get(i);
Rectangle bounds = item.getBounds();
Point p = tree.toDisplay(bounds.x, bounds.y);
if (/*x >= p.x && x <= p.x + bounds.width &&*/
y >= p.y && y <= p.y + bounds.height) {
return item;
}
}
return null;
}
}