/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo 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.
*
* OpenFlexo 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 OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.fib.editor.controller;
import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openflexo.fib.editor.view.FIBEditableView;
import org.openflexo.fib.editor.view.FIBEditableViewDelegate;
import org.openflexo.fib.editor.view.FIBEditableViewDelegate.FIBDropTarget;
import org.openflexo.fib.editor.view.PlaceHolder;
import org.openflexo.fib.model.FIBContainer;
import org.openflexo.logging.FlexoLogger;
import org.openflexo.swing.Focusable;
/**
* DTListener a listener that tracks the state of the operation
*
* @see java.awt.dnd.DropTargetListener
* @see java.awt.dnd.DropTarget
*/
public class DropListener implements DropTargetListener {
static final Logger logger = FlexoLogger.getLogger(DropListener.class.getPackage().getName());
private int acceptableActions = DnDConstants.ACTION_COPY | DnDConstants.ACTION_MOVE;
private final FIBEditableView<?, ?> editableView;
private final PlaceHolder placeHolder;
public DropListener(FIBEditableView<?, ?> editableView, PlaceHolder placeHolder) {
this.editableView = editableView;
this.placeHolder = placeHolder;
}
public Focusable getTargetComponent() {
return placeHolder != null ? placeHolder : editableView.getDelegate();
}
/**
* Called by isDragOk Checks to see if the flavor drag flavor is acceptable
*
* @param e
* the DropTargetDragEvent object
* @return whether the flavor is acceptable
*/
private boolean isDragFlavorSupported(DropTargetDragEvent e) {
boolean ok = false;
if (e.isDataFlavorSupported(ElementDrag.DEFAULT_FLAVOR)) {
ok = true;
}
return ok;
}
/**
* Called by drop Checks the flavors and operations
*
* @param e
* the DropTargetDropEvent object
* @return the chosen DataFlavor or null if none match
*/
private DataFlavor chooseDropFlavor(DropTargetDropEvent e) {
if (e.isLocalTransfer() == true && e.isDataFlavorSupported(ElementDrag.DEFAULT_FLAVOR)) {
return ElementDrag.DEFAULT_FLAVOR;
}
return null;
}
/**
* Called by dragEnter and dragOver Checks the flavors and operations
*
* @param e
* the event object
* @return whether the flavor and operation is ok
*/
private boolean isDragOk(DropTargetDragEvent e) {
if (!isDragFlavorSupported(e)) {
return false;
}
int da = e.getDropAction();
// we're saying that these actions are necessary
if ((da & acceptableActions) == 0) {
return false;
}
try {
FIBDraggable element = (FIBDraggable) e.getTransferable().getTransferData(ElementDrag.DEFAULT_FLAVOR);
if (element == null) {
return false;
}
Object source = e.getSource();
if (source instanceof FIBDropTarget) {
return element.acceptDragging((FIBDropTarget) source);
}
return false;
} catch (UnsupportedFlavorException e1) {
logger.warning("Unexpected: " + e1);
e1.printStackTrace();
return false;
} catch (IOException e1) {
logger.warning("Unexpected: " + e1);
e1.printStackTrace();
return false;
} catch (Exception e1) {
logger.warning("Unexpected: " + e1);
e1.printStackTrace();
return false;
}
}
/**
* start "drag under" feedback on component invoke acceptDrag or rejectDrag based on isDragOk
*
* @param e
*/
@Override
public void dragEnter(DropTargetDragEvent e) {
if (isDragOk(e)) {
getContainerDelegate().addToPlaceHolderVisibleRequesters(getTargetComponent());
getTargetComponent().setFocused(true);
/* Some explanations required here
* What may happen is that making place holders visible will
* place current cursor location inside a newly displayed place
* holder, and cause a subsequent exitComponent() event to the
* current component, and then a big blinking. We test here that
* case and ignore following exitComponent()
* SGU/ I'm not sure this behaviour is platform independant
* please check...
*
*/
if (placeHolder == null && editableView.getPlaceHolders() != null) {
for (PlaceHolder ph2 : editableView.getPlaceHolders()) {
if (ph2.getBounds().contains(e.getLocation())) {
getContainerDelegate().addToPlaceHolderVisibleRequesters(ph2);
}
}
}
e.acceptDrag(e.getDropAction());
} else {
e.rejectDrag();
return;
}
}
/**
* continue "drag under" feedback on component invoke acceptDrag or rejectDrag based on isDragOk
*
* @param e
*/
@Override
public void dragOver(DropTargetDragEvent e) {
/*
* if (isDragFlavorSupported(e))
* getController().getDrawingView().paintDraggedNode
* (e,_controller.getDrawingView().getActivePalette().getPaletteView());
*/
if (isDragOk(e)) {
e.acceptDrag(e.getDropAction());
} else {
e.rejectDrag();
}
}
@Override
public void dropActionChanged(DropTargetDragEvent e) {
if (isDragOk(e)) {
e.acceptDrag(e.getDropAction());
} else {
e.rejectDrag();
}
}
@Override
public void dragExit(DropTargetEvent e) {
getContainerDelegate().removeFromPlaceHolderVisibleRequesters(getTargetComponent());
getTargetComponent().setFocused(false);
}
/**
* perform action from getSourceActions on the transferrable invoke acceptDrop or rejectDrop invoke dropComplete if its a local (same
* JVM) transfer, use StringTransferable.localStringFlavor find a match for the flavor check the operation get the transferable
* according to the chosen flavor do the transfer
*
* @param e
*/
@Override
public void drop(DropTargetDropEvent e) {
getContainerDelegate().removeFromPlaceHolderVisibleRequesters(getTargetComponent());
getTargetComponent().setFocused(false);
try {
DataFlavor chosen = chooseDropFlavor(e);
if (chosen == null) {
e.rejectDrop();
return;
}
// the actions that the source has specified with
// DragGestureRecognizer
int sa = e.getSourceActions();
if ((sa & acceptableActions) == 0) {
e.rejectDrop();
return;
}
Object data = null;
try {
/*
* the source listener receives this action in dragDropEnd. if
* the action is DnDConstants.ACTION_COPY_OR_MOVE then the
* source receives MOVE!
*/
data = e.getTransferable().getTransferData(chosen);
if (logger.isLoggable(Level.FINE)) {
logger.fine("data is a " + data.getClass().getName());
}
if (data == null) {
throw new NullPointerException();
}
} catch (Throwable t) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning("Couldn't get transfer data: " + t.getMessage());
}
t.printStackTrace();
e.dropComplete(false);
return;
}
if (data instanceof FIBDraggable) {
try {
FIBDraggable element = (FIBDraggable) data;
if (element == null) {
e.rejectDrop();
return;
}
Object source = e.getSource();
// OK, let's got for the drop
if (source instanceof FIBDropTarget && element.acceptDragging((FIBDropTarget) source)) {
Point pt = e.getLocation();
if (element.elementDragged((FIBDropTarget) source, pt)) {
e.acceptDrop(acceptableActions);
e.dropComplete(true);
logger.info("Drop succeeded");
return;
}
}
e.rejectDrop();
e.dropComplete(false);
return;
} catch (Exception e1) {
logger.warning("Unexpected: " + e1);
e1.printStackTrace();
e.rejectDrop();
e.dropComplete(false);
return;
}
}
e.rejectDrop();
e.dropComplete(false);
return;
}
finally {
// getController().getDrawingView().resetCapturedNode();
}
}
public FIBEditableViewDelegate<?, ?> getContainerDelegate() {
if (!(editableView.getComponent() instanceof FIBContainer)) {
if (editableView.getParentView() != null && editableView.getParentView() instanceof FIBEditableView) {
return ((FIBEditableView<?, ?>) editableView.getParentView()).getDelegate();
}
}
return editableView.getDelegate();
}
}