/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.eclipse.org/org/documents/epl-v10.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.editors.layout.gscripts.DropZone;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.DropTargetListener;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.graphics.Point;
import java.util.ArrayList;
/**
* Handles drop operations on top of the canvas.
* <p/>
* Reference for d'n'd: http://www.eclipse.org/articles/Article-SWT-DND/DND-in-SWT.html
*/
/* package */ class CanvasDropListener implements DropTargetListener {
private final LayoutCanvas mCanvas;
private CanvasViewInfo mCurrentView;
private DropZone mCurrentZone;
private ArrayList<DropZone> mZones;
private NodeProxy mTargetNode;
private CanvasViewInfo mTargetView;
public CanvasDropListener(LayoutCanvas canvas) {
mCanvas = canvas;
}
public CanvasViewInfo getTargetView() {
return mTargetView;
}
public ArrayList<DropZone> getZones() {
return mZones;
}
public DropZone getCurrentZone() {
return mCurrentZone;
}
/*
* The cursor has entered the drop target boundaries.
* {@inheritDoc}
*/
public void dragEnter(DropTargetEvent event) {
AdtPlugin.printErrorToConsole("DEBUG", "drag enter", event);
checkDataType(event);
if (event.detail == DND.DROP_DEFAULT) {
if ((event.operations & DND.DROP_COPY) != 0) {
event.detail = DND.DROP_COPY;
}
}
if (event.detail == DND.DROP_COPY) {
processDropEvent(event);
} else {
event.detail = DND.DROP_NONE;
clearDropInfo();
}
}
/*
* The operation being performed has changed (e.g. modifier key).
* {@inheritDoc}
*/
public void dragOperationChanged(DropTargetEvent event) {
AdtPlugin.printErrorToConsole("DEBUG", "drag changed", event);
checkDataType(event);
if (event.detail == DND.DROP_DEFAULT) {
if ((event.operations & DND.DROP_COPY) != 0) {
event.detail = DND.DROP_COPY;
}
}
if (event.detail == DND.DROP_COPY) {
processDropEvent(event);
} else {
event.detail = DND.DROP_NONE;
clearDropInfo();
}
}
/*
* The cursor has left the drop target boundaries.
* {@inheritDoc}
*/
public void dragLeave(DropTargetEvent event) {
AdtPlugin.printErrorToConsole("DEBUG", "drag leave");
clearDropInfo();
}
/*
* The cursor is moving over the drop target.
* {@inheritDoc}
*/
public void dragOver(DropTargetEvent event) {
processDropEvent(event);
}
/*
* The drop is about to be performed.
* The drop target is given a last chance to change the nature of the drop.
* {@inheritDoc}
*/
public void dropAccept(DropTargetEvent event) {
AdtPlugin.printErrorToConsole("DEBUG", "drop accept");
checkDataType(event);
if (event.detail != DND.DROP_NONE) {
processDropEvent(event);
}
}
/*
* The data is being dropped.
* {@inheritDoc}
*/
public void drop(DropTargetEvent event) {
// TODO Auto-generated method stub
AdtPlugin.printErrorToConsole("DEBUG", "drop");
String viewFqcn = null;
ElementDescTransfer edt = ElementDescTransfer.getInstance();
if (edt.isSupportedType(event.currentDataType)) {
// DropTarget already invoked Tranfer#nativeToJava() and stored the result
// in event.data
if (event.data instanceof String) {
viewFqcn = (String) event.data;
}
}
if (viewFqcn == null) {
AdtPlugin.printErrorToConsole("DEBUG", "drop missing drop data");
return;
}
Point p = eventToCanvasPoint(event);
mCanvas.getRulesEngine().dropFinish(viewFqcn, mTargetNode, mCurrentZone,
new com.android.ide.eclipse.adt.editors.layout.gscripts.Point(p.x, p.y));
clearDropInfo();
}
/**
* Verifies that event.currentDataType is of type {@link ElementDescTransfer}.
* If not, try to find a valid data type.
* Otherwise set the drop to {@link DND#DROP_NONE} to cancel it.
*
* @return True if the data type is accepted.
*/
private boolean checkDataType(DropTargetEvent event) {
ElementDescTransfer edt = ElementDescTransfer.getInstance();
TransferData current = event.currentDataType;
if (edt.isSupportedType(current)) {
return true;
}
// We only support ElementDescTransfer and the current data type is not right.
// Let's see if we can find another one.
for (TransferData td : event.dataTypes) {
if (td != current && edt.isSupportedType(td)) {
// We like this type better.
event.currentDataType = td;
return true;
}
}
// We failed to find any good transfer type.
event.detail = DND.DROP_NONE;
return false;
}
private void processDropEvent(DropTargetEvent event) {
if (!mCanvas.isResultValid()) {
// We don't allow drop on an invalid layout, even if we have some obsolete
// layout info for it.
event.detail = DND.DROP_NONE;
clearDropInfo();
return;
}
Point p = eventToCanvasPoint(event);
int x = p.x;
int y = p.y;
CanvasViewInfo vi = mCanvas.findViewInfoAt(x, y);
boolean needRedraw = false;
if (vi != mCurrentView) {
setCurrentView(vi);
needRedraw = true;
}
if (mZones != null) {
if (mCurrentZone == null || !mCurrentZone.bounds.contains(x, y)) {
// If there is no current zone or it doesn't contain the current point,
// try to find one.
for (DropZone z : mZones) {
if (z.bounds.contains(x, y)) {
mCurrentZone = z;
needRedraw = true;
break;
}
}
}
}
if (needRedraw) {
mCanvas.redraw();
}
}
private Point eventToCanvasPoint(DropTargetEvent event) {
Point p = mCanvas.toControl(event.x, event.y);
p.x -= LayoutCanvas.IMAGE_MARGIN;
p.y -= LayoutCanvas.IMAGE_MARGIN;
return p;
}
private void setCurrentView(CanvasViewInfo vi) {
// We switched to a new view.
clearState();
mCurrentView = vi;
if (vi != null) {
// Query GRE for drop zones. If the view that we found reports null for drop
// zones, we move on the parent view till we find one that has drop zones.
for (; vi != null && mZones == null; vi = vi.getParent()) {
mTargetView = vi;
mTargetNode = new NodeProxy(vi.getUiViewKey(), vi.getAbsRect());
mZones = mCanvas.getRulesEngine().dropStart(mTargetNode);
}
if (mZones == null) {
mTargetView = null;
mTargetNode = null;
}
AdtPlugin.printErrorToConsole("ZONES", mZones);
}
mCanvas.redraw();
}
private void clearState() {
mCurrentView = null;
mTargetView = null;
mTargetNode = null;
mCurrentZone = null;
mZones = null;
}
private void clearDropInfo() {
if (mCurrentView != null) {
clearState();
mCanvas.redraw();
}
}
}