/* * (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.fge.controller; import java.awt.Component; import java.awt.Point; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.dnd.DnDConstants; import java.awt.dnd.DragGestureEvent; import java.awt.dnd.DragGestureListener; import java.awt.dnd.DragGestureRecognizer; import java.awt.dnd.DragSource; import java.awt.dnd.DragSourceContext; import java.awt.dnd.DragSourceDragEvent; import java.awt.dnd.DragSourceDropEvent; import java.awt.dnd.DragSourceEvent; import java.awt.dnd.DragSourceListener; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import java.awt.dnd.InvalidDnDOperationException; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.io.IOException; import java.util.Hashtable; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JComponent; import org.openflexo.fge.GraphicalRepresentation; import org.openflexo.fge.ShapeGraphicalRepresentation; import org.openflexo.fge.controller.MoveAction.ShapeGraphicalRepresentationTransferable; import org.openflexo.fge.controller.MoveAction.TransferedShapeGraphicalRepresentation; import org.openflexo.fge.geom.FGEPoint; import org.openflexo.fge.view.FGEView; import org.openflexo.fge.view.ShapeView; import org.openflexo.fge.view.listener.FocusRetriever; import sun.awt.dnd.SunDragSourceContextPeer; public class DNDInfo { private static final Logger logger = Logger.getLogger(DNDInfo.class.getPackage().getName()); /** * */ private final MoveAction _moveAction; private ShapeView<?> shapeView; private DragSource dragSource; private DragGestureListener dgListener; private DragSourceListener dsListener; private int dragAction = DnDConstants.ACTION_MOVE; private DragGestureRecognizer dgr; private DragSourceContext dragSourceContext; private DrawingController<?> _controller; private ShapeGraphicalRepresentation<?> draggedObject; private Hashtable<FGEView<?>, DropTarget> dropTargets; DNDInfo(MoveAction moveAction, ShapeGraphicalRepresentation<?> gr, DrawingController<?> controller, final MouseEvent initialEvent) { _moveAction = moveAction; _controller = controller; draggedObject = gr; logger.info("DnD gesture recognized, starting DnD"); this.dgListener = new DGListener(); this.dragSource = DragSource.getDefaultDragSource(); this.dsListener = new DSListener(); shapeView = controller.getDrawingView().shapeViewForObject(gr); // component, action, listener dgr = this.dragSource.createDefaultDragGestureRecognizer(shapeView, this.dragAction, this.dgListener); enableDragging(); Point initialPoint = _moveAction.initialClickOffset; Vector<InputEvent> list = new Vector<InputEvent>(); list.add(initialEvent); DragGestureEvent dge = new DragGestureEvent(dgr, dragAction, initialPoint, list) { @Override public InputEvent getTriggerEvent() { return initialEvent; } }; // Hack for bug 1006304 synchronized (SunDragSourceContextPeer.class) { try { SunDragSourceContextPeer.checkDragDropInProgress(); } catch (InvalidDnDOperationException e) { if (logger.isLoggable(Level.WARNING)) { logger.warning("For some reason there was still a Dnd in progress. Will set it back to false. God knows why this happens"); } if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Stacktrace for DnD still in progress", e); } SunDragSourceContextPeer.setDragDropInProgress(false); } } this.dragSource.startDrag(dge, MoveAction.dropKO, new MoveAction.ShapeGraphicalRepresentationTransferable(gr, initialPoint), dsListener); controller.getDrawingView().captureDraggedNode(shapeView, dge); // gr.setIsVisible(false); } protected void enableDragging() { dgr.setComponent(shapeView); logger.info("MoveAction, enableDragging for " + shapeView); if (dropTargets != null) { dropTargets.clear(); } dropTargets = new Hashtable<FGEView<?>, DropTarget>(); for (GraphicalRepresentation<?> gr : _controller.getDrawingView().getContents().keySet()) { FGEView<?> view = _controller.getDrawingView().getContents().get(gr); if (((Component) view).getDropTarget() != null) { dropTargets.put(view, ((Component) view).getDropTarget()); } ((Component) view).setDropTarget(new DropTarget(shapeView, DnDConstants.ACTION_MOVE, new MoveActionDropListener( (JComponent) view), true)); } } protected void disableDragging() { for (FGEView<?> v : dropTargets.keySet()) { ((Component) v).setDropTarget(dropTargets.get(v)); } dgr.setComponent(null); _moveAction.resetCurrentDND(); } /** * DGListener a listener that will start the drag. has access to top level's dsListener and dragSource * * @see java.awt.dnd.DragGestureListener * @see java.awt.dnd.DragSource * @see java.awt.datatransfer.StringSelection */ class DGListener implements DragGestureListener { /** * Start the drag if the operation is ok. uses java.awt.datatransfer.StringSelection to transfer the label's data * * @param e * the event object */ @Override public void dragGestureRecognized(DragGestureEvent e) { logger.info("dragGestureRecognized"); // if the action is ok we go ahead // otherwise we punt if ((e.getDragAction() & dragAction) == 0) { return; // get the label's text and put it inside a Transferable // Transferable transferable = new StringSelection( // DragLabel.this.getText() ); } ShapeGraphicalRepresentationTransferable transferable = new MoveAction.ShapeGraphicalRepresentationTransferable(null, e.getDragOrigin()); try { // initial cursor, transferrable, dsource listener e.startDrag(DragSource.DefaultCopyNoDrop, transferable, dsListener); _controller.getDrawingView().captureDraggedNode(shapeView, e); } catch (Exception idoe) { logger.warning("Unexpected exception " + idoe); } } } /** * DSListener a listener that will track the state of the DnD operation * * @see java.awt.dnd.DragSourceListener * @see java.awt.dnd.DragSource * @see java.awt.datatransfer.StringSelection */ public class DSListener implements DragSourceListener { /** * @param e * the event */ @Override public void dragDropEnd(DragSourceDropEvent e) { try { // logger.info("dragDropEnd() "+e); if (e.getDropSuccess() == false) { if (logger.isLoggable(Level.INFO)) { logger.info("Dropping was not successful"); } return; } /* * the dropAction should be what the drop target specified in * acceptDrop */ // this is the action selected by the drop target if (e.getDropAction() == DnDConstants.ACTION_MOVE) { ; // setName(""); } } finally { _controller.getDrawingView().resetCapturedNode(); disableDragging(); } } /** * @param e * the event */ @Override public void dragEnter(DragSourceDragEvent e) { // logger.info("************** dragEnter() "+e); DragSourceContext context = e.getDragSourceContext(); // intersection of the users selected action, and the source and // target actions int myaction = e.getDropAction(); if ((myaction & dragAction) != 0) { context.setCursor(DragSource.DefaultCopyDrop); } else { context.setCursor(DragSource.DefaultCopyNoDrop); } } /** * @param e * the event */ @Override public void dragOver(DragSourceDragEvent e) { // logger.info("dragOver() "+e); // interface dragSourceContext = e.getDragSourceContext(); } /** * @param e * the event */ @Override public void dragExit(DragSourceEvent e) { // logger.info("dragExit() "+e); // interface } /** * for example, press shift during drag to change to a link action * * @param e * the event */ @Override public void dropActionChanged(DragSourceDragEvent e) { DragSourceContext context = e.getDragSourceContext(); context.setCursor(DragSource.DefaultCopyNoDrop); } } /** * DTListener a listener that tracks the state of the operation * * @see java.awt.dnd.DropTargetListener * @see java.awt.dnd.DropTarget */ public class MoveActionDropListener implements DropTargetListener { private int acceptableActions = DnDConstants.ACTION_MOVE; private JComponent _dropContainer; // private static DefaultProcessBuilder _myPrivateBuilder; public MoveActionDropListener(JComponent dropContainer) { super(); _dropContainer = dropContainer; } /** * 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(ShapeGraphicalRepresentationTransferable.defaultFlavor())) { 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(ShapeGraphicalRepresentationTransferable.defaultFlavor())) { return ShapeGraphicalRepresentationTransferable.defaultFlavor(); } 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) == false) { return false; } int da = e.getDropAction(); // we're saying that these actions are necessary if ((da & acceptableActions) == 0) { return false; } try { ShapeGraphicalRepresentation<?> element = ((TransferedShapeGraphicalRepresentation) e.getTransferable().getTransferData( ShapeGraphicalRepresentationTransferable.defaultFlavor())).getTransferedElement(); if (element == null) { return false; } GraphicalRepresentation<?> focused = getFocusedObject(e); if (focused == null) { return false; } return focused instanceof ShapeGraphicalRepresentation && element.isAllowedToBeDraggedOutsideParentContainerInsideContainer(focused); } 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)) { // DropLabel.this.borderColor=Color.red; // showBorder(true); e.rejectDrag(); return; } e.acceptDrag(e.getDropAction()); } /** * continue "drag under" feedback on component invoke acceptDrag or rejectDrag based on isDragOk * * @param e */ @Override public void dragOver(DropTargetDragEvent e) { if (isDragFlavorSupported(e)) { _controller.getDrawingView().updateCapturedDraggedNodeImagePosition(e, _controller.getDrawingView()); } if (!isDragOk(e)) { if (dragSourceContext == null) { logger.warning("dragSourceContext should NOT be null"); } else { dragSourceContext.setCursor(MoveAction.dropKO); } e.rejectDrag(); return; } if (dragSourceContext == null) { logger.warning("dragSourceContext should NOT be null"); } else { dragSourceContext.setCursor(MoveAction.dropOK); /*try { ShapeGraphicalRepresentation element = ((TransferedShapeGraphicalRepresentation)e.getTransferable().getTransferData(ShapeGraphicalRepresentationTransferable.defaultFlavor())).getTransferedElement(); GraphicalRepresentation<?> focused = getFocusedObject(e); if (focused instanceof ShapeGraphicalRepresentation) { element.dragOutsideParentContainerInsideContainer((ShapeGraphicalRepresentation<?>)focused,new FGEPoint(0,0),true); } } catch (UnsupportedFlavorException e1) { logger.warning("Unexpected: "+e1); e1.printStackTrace(); } catch (IOException e1) { logger.warning("Unexpected: "+e1); e1.printStackTrace(); } catch (Exception e1) { logger.warning("Unexpected: "+e1); e1.printStackTrace(); }*/ } e.acceptDrag(e.getDropAction()); } @Override public void dropActionChanged(DropTargetDragEvent e) { if (!isDragOk(e)) { e.rejectDrag(); return; } e.acceptDrag(e.getDropAction()); } @Override public void dragExit(DropTargetEvent e) { // interface method // _controller.getDrawingView().resetCapturedNode(); } /** * 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) { 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 TransferedShapeGraphicalRepresentation) { try { ShapeGraphicalRepresentation<?> element = ((TransferedShapeGraphicalRepresentation) data).getTransferedElement(); if (element == null) { e.rejectDrop(); return; } GraphicalRepresentation<?> focused = getFocusedObject(e); if (!(focused instanceof ShapeGraphicalRepresentation)) { e.rejectDrop(); return; } // OK, let's got for the drop if (element.isAllowedToBeDraggedOutsideParentContainerInsideContainer(focused)) { Component targetComponent = e.getDropTargetContext().getComponent(); Point pt = e.getLocation(); FGEPoint modelLocation = new FGEPoint(); if (targetComponent instanceof FGEView) { pt = GraphicalRepresentation.convertPoint(((FGEView<?>) targetComponent).getGraphicalRepresentation(), pt, focused, ((FGEView<?>) targetComponent).getScale()); modelLocation.x = pt.x / ((FGEView<?>) targetComponent).getScale(); modelLocation.y = pt.y / ((FGEView<?>) targetComponent).getScale(); modelLocation.x -= ((TransferedShapeGraphicalRepresentation) data).getOffset().x; modelLocation.y -= ((TransferedShapeGraphicalRepresentation) data).getOffset().y; } else { modelLocation.x -= ((TransferedShapeGraphicalRepresentation) data).getOffset().x; modelLocation.y -= ((TransferedShapeGraphicalRepresentation) data).getOffset().y; } if (element.dragOutsideParentContainerInsideContainer(focused, modelLocation)) { e.acceptDrop(acceptableActions); e.dropComplete(true); return; } else { 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 { // disableDragging(); } } private FocusRetriever getFocusRetriever() { if (_dropContainer instanceof FGEView) { return ((FGEView<?>) _dropContainer).getDrawingView().getFocusRetriever(); } return null; } private FGEView<?> getFGEView() { if (_dropContainer instanceof FGEView) { return (FGEView<?>) _dropContainer; } return null; } public GraphicalRepresentation<?> getFocusedObject(DropTargetDragEvent event) { if (getFocusRetriever() != null) { GraphicalRepresentation<?> returned = getFocusRetriever().getFocusedObject(event); if (returned == null) { // Since we are in a FGEView, a null value indicates that we are on the Drawing view return getFGEView().getGraphicalRepresentation().getDrawingGraphicalRepresentation(); } return returned; } // No focus retriever: we are not in a FGEView.... return null; } public GraphicalRepresentation<?> getFocusedObject(DropTargetDropEvent event) { if (getFocusRetriever() != null) { GraphicalRepresentation<?> returned = getFocusRetriever().getFocusedObject(event); if (returned == null) { // Since we are in a FGEView, a null value indicates that we are on the Drawing view return getFGEView().getGraphicalRepresentation().getDrawingGraphicalRepresentation(); } return returned; } // No focus retriever: we are not in a FGEView.... return null; } } }