/**
* $Id: mxGraphHandler.java,v 1.72 2011-05-07 09:46:58 gaudenz Exp $
* Copyright (c) 2008, Gaudenz Alder
*
* Known issue: Drag image size depends on the initial position and may sometimes
* not align with the grid when dragging. This is because the rounding of the width
* and height at the initial position may be different than that at the current
* position as the left and bottom side of the shape must align to the grid lines.
*/
package com.mxgraph.swing.handler;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceAdapter;
import java.awt.dnd.DragSourceDropEvent;
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.event.InputEvent;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.TooManyListenersException;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import javax.swing.TransferHandler;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.swing.util.mxGraphTransferable;
import com.mxgraph.swing.util.mxMouseAdapter;
import com.mxgraph.util.mxCellRenderer;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.util.mxEventSource.mxIEventListener;
import com.mxgraph.view.mxCellState;
import com.mxgraph.view.mxGraph;
public class mxGraphHandler extends mxMouseAdapter implements
DropTargetListener
{
/**
*
*/
private static final long serialVersionUID = 3241109976696510225L;
/**
* Default is Cursor.DEFAULT_CURSOR.
*/
public static Cursor DEFAULT_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR);
/**
* Default is Cursor.MOVE_CURSOR.
*/
public static Cursor MOVE_CURSOR = new Cursor(Cursor.MOVE_CURSOR);
/**
* Default is Cursor.HAND_CURSOR.
*/
public static Cursor FOLD_CURSOR = new Cursor(Cursor.HAND_CURSOR);
/**
* Reference to the enclosing graph component.
*/
protected mxGraphComponent graphComponent;
/**
* Specifies if the handler is enabled. Default is true.
*/
protected boolean enabled = true;
/**
* Specifies if cloning by control-drag is enabled. Default is true.
*/
protected boolean cloneEnabled = true;
/**
* Specifies if moving is enabled. Default is true.
*/
protected boolean moveEnabled = true;
/**
* Specifies if moving is enabled. Default is true.
*/
protected boolean selectEnabled = true;
/**
* Specifies if the cell marker should be called (for splitting edges and
* dropping cells into groups). Default is true.
*/
protected boolean markerEnabled = true;
/**
* Specifies if cells may be moved out of their parents. Default is true.
*/
protected boolean removeCellsFromParent = true;
/**
*
*/
protected mxMovePreview movePreview;
/**
* Specifies if live preview should be used if possible. Default is false.
*/
protected boolean livePreview = false;
/**
* Specifies if an image should be used for preview. Default is true.
*/
protected boolean imagePreview = true;
/**
* Specifies if the preview should be centered around the mouse cursor if there
* was no mouse click to define the offset within the shape (eg. drag from
* external source). Default is true.
*/
protected boolean centerPreview = true;
/**
* Specifies if this handler should be painted on top of all other components.
* Default is true.
*/
protected boolean keepOnTop = true;
/**
* Holds the cells that are being moved by this handler.
*/
protected transient Object[] cells;
/**
* Holds the image that is being used for the preview.
*/
protected transient ImageIcon dragImage;
/**
* Holds the start location of the mouse gesture.
*/
protected transient Point first;
/**
*
*/
protected transient Object cell;
/**
*
*/
protected transient Object initialCell;
/**
*
*/
protected transient Object[] dragCells;
/**
*
*/
protected transient mxCellMarker marker;
/**
*
*/
protected transient boolean canImport;
/**
* Scaled, translated bounds of the selection cells.
*/
protected transient mxRectangle cellBounds;
/**
* Scaled, translated bounding box of the selection cells.
*/
protected transient mxRectangle bbox;
/**
* Unscaled, untranslated bounding box of the selection cells.
*/
protected transient mxRectangle transferBounds;
/**
*
*/
protected transient boolean visible = false;
/**
*
*/
protected transient Rectangle previewBounds = null;
/**
* Workaround for alt-key-state not correct in mouseReleased. Note: State
* of the alt-key is not available during drag-and-drop.
*/
private transient boolean gridEnabledEvent = false;
/**
* Workaround for shift-key-state not correct in mouseReleased.
*/
protected transient boolean constrainedEvent = false;
/**
* Reference to the current drop target.
*/
protected transient DropTarget currentDropTarget = null;
/**
*
* @param graphComponent
*/
public mxGraphHandler(final mxGraphComponent graphComponent)
{
this.graphComponent = graphComponent;
marker = createMarker();
movePreview = createMovePreview();
// Installs the paint handler
graphComponent.addListener(mxEvent.AFTER_PAINT, new mxIEventListener()
{
public void invoke(Object sender, mxEventObject evt)
{
Graphics g = (Graphics) evt.getProperty("g");
paint(g);
}
});
// Listens to all mouse events on the rendering control
graphComponent.getGraphControl().addMouseListener(this);
graphComponent.getGraphControl().addMouseMotionListener(this);
// Drag target creates preview image
installDragGestureHandler();
// Listens to dropped graph cells
installDropTargetHandler();
// Listens to changes of the transferhandler
graphComponent.addPropertyChangeListener(new PropertyChangeListener()
{
public void propertyChange(PropertyChangeEvent evt)
{
if (evt.getPropertyName().equals("transferHandler"))
{
if (currentDropTarget != null)
{
currentDropTarget.removeDropTargetListener(mxGraphHandler.this);
}
installDropTargetHandler();
}
}
});
setVisible(false);
}
/**
*
*/
protected void installDragGestureHandler()
{
DragGestureListener dragGestureListener = new DragGestureListener()
{
public void dragGestureRecognized(DragGestureEvent e)
{
if (graphComponent.isDragEnabled() && first != null)
{
final TransferHandler th = graphComponent
.getTransferHandler();
if (th instanceof mxGraphTransferHandler)
{
final mxGraphTransferable t = (mxGraphTransferable) ((mxGraphTransferHandler) th)
.createTransferable(graphComponent);
if (t != null)
{
e.startDrag(null, mxConstants.EMPTY_IMAGE,
new Point(), t, new DragSourceAdapter()
{
/**
*
*/
public void dragDropEnd(
DragSourceDropEvent dsde)
{
((mxGraphTransferHandler) th)
.exportDone(
graphComponent,
t,
TransferHandler.NONE);
first = null;
}
});
}
}
}
}
};
DragSource dragSource = new DragSource();
dragSource.createDefaultDragGestureRecognizer(
graphComponent.getGraphControl(),
DnDConstants.ACTION_COPY_OR_MOVE, dragGestureListener);
}
/**
*
*/
protected void installDropTargetHandler()
{
DropTarget dropTarget = graphComponent.getDropTarget();
try
{
if (dropTarget != null)
{
dropTarget.addDropTargetListener(this);
currentDropTarget = dropTarget;
}
}
catch (TooManyListenersException tmle)
{
// should not happen... swing drop target is multicast
}
}
/**
*
*/
public boolean isVisible()
{
return visible;
}
/**
*
*/
public void setVisible(boolean value)
{
if (visible != value)
{
visible = value;
if (previewBounds != null)
{
graphComponent.getGraphControl().repaint(previewBounds);
}
}
}
/**
*
*/
public void setPreviewBounds(Rectangle bounds)
{
if ((bounds == null && previewBounds != null)
|| (bounds != null && previewBounds == null)
|| (bounds != null && previewBounds != null && !bounds
.equals(previewBounds)))
{
Rectangle dirty = null;
if (isVisible())
{
dirty = previewBounds;
if (dirty != null)
{
dirty.add(bounds);
}
else
{
dirty = bounds;
}
}
previewBounds = bounds;
if (dirty != null)
{
graphComponent.getGraphControl().repaint(dirty.x - 1,
dirty.y - 1, dirty.width + 2, dirty.height + 2);
}
}
}
/**
*
*/
protected mxMovePreview createMovePreview()
{
return new mxMovePreview(graphComponent);
}
/**
*
*/
protected mxCellMarker createMarker()
{
mxCellMarker marker = new mxCellMarker(graphComponent, Color.BLUE)
{
/**
*
*/
private static final long serialVersionUID = -8451338653189373347L;
/**
*
*/
public boolean isEnabled()
{
return graphComponent.getGraph().isDropEnabled();
}
/**
*
*/
public Object getCell(MouseEvent e)
{
TransferHandler th = graphComponent.getTransferHandler();
boolean isLocal = th instanceof mxGraphTransferHandler
&& ((mxGraphTransferHandler) th).isLocalDrag();
mxGraph graph = graphComponent.getGraph();
Object cell = super.getCell(e);
Object[] cells = (isLocal) ? graph.getSelectionCells()
: dragCells;
cell = graph.getDropTarget(cells, e.getPoint(), cell);
boolean clone = graphComponent.isCloneEvent(e) && cloneEnabled;
if (isLocal && cell != null && cells.length > 0 && !clone
&& graph.getModel().getParent(cells[0]) == cell)
{
cell = null;
}
return cell;
}
};
// Swimlane content area will not be transparent drop targets
marker.setSwimlaneContentEnabled(true);
return marker;
}
/**
*
*/
public mxGraphComponent getGraphComponent()
{
return graphComponent;
}
/**
*
*/
public boolean isEnabled()
{
return enabled;
}
/**
*
*/
public void setEnabled(boolean value)
{
enabled = value;
}
/**
*
*/
public boolean isCloneEnabled()
{
return cloneEnabled;
}
/**
*
*/
public void setCloneEnabled(boolean value)
{
cloneEnabled = value;
}
/**
*
*/
public boolean isMoveEnabled()
{
return moveEnabled;
}
/**
*
*/
public void setMoveEnabled(boolean value)
{
moveEnabled = value;
}
/**
*
*/
public boolean isMarkerEnabled()
{
return markerEnabled;
}
/**
*
*/
public void setMarkerEnabled(boolean value)
{
markerEnabled = value;
}
/**
*
*/
public boolean isSelectEnabled()
{
return selectEnabled;
}
/**
*
*/
public void setSelectEnabled(boolean value)
{
selectEnabled = value;
}
/**
*
*/
public boolean isRemoveCellsFromParent()
{
return removeCellsFromParent;
}
/**
*
*/
public void setRemoveCellsFromParent(boolean value)
{
removeCellsFromParent = value;
}
/**
*
*/
public boolean isLivePreview()
{
return livePreview;
}
/**
*
*/
public void setLivePreview(boolean value)
{
livePreview = value;
}
/**
*
*/
public boolean isImagePreview()
{
return imagePreview;
}
/**
*
*/
public void setImagePreview(boolean value)
{
imagePreview = value;
}
/**
*
*/
public boolean isCenterPreview()
{
return centerPreview;
}
/**
*
*/
public void setCenterPreview(boolean value)
{
centerPreview = value;
}
/**
*
*/
public void updateDragImage(Object[] cells)
{
dragImage = null;
if (cells != null && cells.length > 0)
{
Image img = mxCellRenderer.createBufferedImage(
graphComponent.getGraph(), cells, graphComponent.getGraph()
.getView().getScale(), null,
graphComponent.isAntiAlias(), null,
graphComponent.getCanvas());
if (img != null)
{
dragImage = new ImageIcon(img);
previewBounds.setSize(dragImage.getIconWidth(),
dragImage.getIconHeight());
}
}
}
/**
*
*/
public void mouseMoved(MouseEvent e)
{
if (graphComponent.isEnabled() && isEnabled() && !e.isConsumed())
{
Cursor cursor = getCursor(e);
if (cursor != null)
{
graphComponent.getGraphControl().setCursor(cursor);
e.consume();
}
else
{
graphComponent.getGraphControl().setCursor(DEFAULT_CURSOR);
}
}
}
/**
*
*/
protected Cursor getCursor(MouseEvent e)
{
Cursor cursor = null;
if (isMoveEnabled())
{
Object cell = graphComponent.getCellAt(e.getX(), e.getY(), false);
if (cell != null)
{
if (graphComponent.isFoldingEnabled()
&& graphComponent.hitFoldingIcon(cell, e.getX(),
e.getY()))
{
cursor = FOLD_CURSOR;
}
else if (graphComponent.getGraph().isCellMovable(cell))
{
cursor = MOVE_CURSOR;
}
}
}
return cursor;
}
/**
*
*/
public void dragEnter(DropTargetDragEvent e)
{
JComponent component = getDropTarget(e);
TransferHandler th = component.getTransferHandler();
boolean isLocal = th instanceof mxGraphTransferHandler
&& ((mxGraphTransferHandler) th).isLocalDrag();
if (isLocal)
{
canImport = true;
}
else
{
canImport = graphComponent.isImportEnabled()
&& th.canImport(component, e.getCurrentDataFlavors());
}
if (canImport)
{
transferBounds = null;
setVisible(false);
try
{
Transferable t = e.getTransferable();
if (t.isDataFlavorSupported(mxGraphTransferable.dataFlavor))
{
mxGraphTransferable gt = (mxGraphTransferable) t
.getTransferData(mxGraphTransferable.dataFlavor);
dragCells = gt.getCells();
if (gt.getBounds() != null)
{
mxGraph graph = graphComponent.getGraph();
double scale = graph.getView().getScale();
transferBounds = gt.getBounds();
int w = (int) Math.ceil((transferBounds.getWidth() + 1)
* scale);
int h = (int) Math
.ceil((transferBounds.getHeight() + 1) * scale);
setPreviewBounds(new Rectangle(
(int) transferBounds.getX(),
(int) transferBounds.getY(), w, h));
if (imagePreview)
{
// Does not render fixed cells for local preview
// but ignores movable state for non-local previews
if (isLocal)
{
if (!isLivePreview())
{
updateDragImage(graph
.getMovableCells(dragCells));
}
}
else
{
Object[] tmp = graphComponent
.getImportableCells(dragCells);
updateDragImage(tmp);
// Shows no drag icon if import is allowed but none
// of the cells can be imported
if (tmp == null || tmp.length == 0)
{
canImport = false;
e.rejectDrag();
return;
}
}
}
setVisible(true);
}
}
e.acceptDrag(TransferHandler.COPY_OR_MOVE);
}
catch (Exception ex)
{
// do nothing
ex.printStackTrace();
}
}
else
{
e.rejectDrag();
}
}
/**
*
*/
public void mousePressed(MouseEvent e)
{
if (graphComponent.isEnabled() && isEnabled() && !e.isConsumed()
&& !graphComponent.isForceMarqueeEvent(e))
{
cell = graphComponent.getCellAt(e.getX(), e.getY(), false);
initialCell = cell;
if (cell != null)
{
if (isSelectEnabled()
&& !graphComponent.getGraph().isCellSelected(cell))
{
graphComponent.selectCellForEvent(cell, e);
cell = null;
}
// Starts move if the cell under the mouse is movable and/or any
// cells of the selection are movable
if (isMoveEnabled() && !e.isPopupTrigger())
{
start(e);
e.consume();
}
}
else if (e.isPopupTrigger())
{
graphComponent.getGraph().clearSelection();
}
}
}
/**
*
*/
public Object[] getCells(Object initialCell)
{
mxGraph graph = graphComponent.getGraph();
return graph.getMovableCells(graph.getSelectionCells());
}
/**
*
*/
public void start(MouseEvent e)
{
if (isLivePreview())
{
movePreview.start(e,
graphComponent.getGraph().getView().getState(initialCell));
}
else
{
mxGraph graph = graphComponent.getGraph();
// Constructs an array with cells that are indeed movable
cells = getCells(initialCell);
cellBounds = graph.getView().getBounds(cells);
if (cellBounds != null)
{
// Updates the size of the graph handler that is in
// charge of painting all other handlers
bbox = graph.getView().getBoundingBox(cells);
Rectangle bounds = cellBounds.getRectangle();
bounds.width += 1;
bounds.height += 1;
setPreviewBounds(bounds);
}
}
first = e.getPoint();
}
/**
*
*/
public void dropActionChanged(DropTargetDragEvent e)
{
// do nothing
}
/**
*
* @param e
*/
public void dragOver(DropTargetDragEvent e)
{
if (canImport)
{
mouseDragged(createEvent(e));
mxGraphTransferHandler handler = getGraphTransferHandler(e);
if (handler != null)
{
mxGraph graph = graphComponent.getGraph();
double scale = graph.getView().getScale();
Point pt = SwingUtilities.convertPoint(graphComponent,
e.getLocation(), graphComponent.getGraphControl());
pt = graphComponent.snapScaledPoint(new mxPoint(pt)).getPoint();
handler.setLocation(new Point(pt));
int dx = 0;
int dy = 0;
// Centers the preview image
if (centerPreview && transferBounds != null)
{
dx -= Math.round(transferBounds.getWidth() * scale / 2);
dy -= Math.round(transferBounds.getHeight() * scale / 2);
}
// Sets the drop offset so that the location in the transfer
// handler reflects the actual mouse position
handler.setOffset(new Point((int) graph.snap(dx / scale),
(int) graph.snap(dy / scale)));
pt.translate(dx, dy);
// Shifts the preview so that overlapping parts do not
// affect the centering
if (transferBounds != null && dragImage != null)
{
dx = (int) Math
.round((dragImage.getIconWidth() - 2 - transferBounds
.getWidth() * scale) / 2);
dy = (int) Math
.round((dragImage.getIconHeight() - 2 - transferBounds
.getHeight() * scale) / 2);
pt.translate(-dx, -dy);
}
if (!handler.isLocalDrag() && previewBounds != null)
{
setPreviewBounds(new Rectangle(pt, previewBounds.getSize()));
}
}
}
else
{
e.rejectDrag();
}
}
/**
*
*/
public Point convertPoint(Point pt)
{
pt = SwingUtilities.convertPoint(graphComponent, pt,
graphComponent.getGraphControl());
pt.x -= graphComponent.getHorizontalScrollBar().getValue();
pt.y -= graphComponent.getVerticalScrollBar().getValue();
return pt;
}
/**
*
*/
public void mouseDragged(MouseEvent e)
{
// LATER: Check scrollborder, use scroll-increments, do not
// scroll when over ruler dragging from library
graphComponent.getGraphControl().scrollRectToVisible(
new Rectangle(e.getPoint()));
if (!e.isConsumed())
{
gridEnabledEvent = graphComponent.isGridEnabledEvent(e);
constrainedEvent = graphComponent.isConstrainedEvent(e);
if (constrainedEvent && first != null)
{
int x = e.getX();
int y = e.getY();
if (Math.abs(e.getX() - first.x) > Math.abs(e.getY() - first.y))
{
y = first.y;
}
else
{
x = first.x;
}
e = new MouseEvent(e.getComponent(), e.getID(), e.getWhen(),
e.getModifiers(), x, y, e.getClickCount(),
e.isPopupTrigger(), e.getButton());
}
if (isVisible() && isMarkerEnabled())
{
marker.process(e);
}
if (first != null)
{
if (movePreview.isActive())
{
double dx = e.getX() - first.x;
double dy = e.getY() - first.y;
if (graphComponent.isGridEnabledEvent(e))
{
mxGraph graph = graphComponent.getGraph();
dx = graph.snap(dx);
dy = graph.snap(dy);
}
boolean clone = isCloneEnabled()
&& graphComponent.isCloneEvent(e);
movePreview.update(e, dx, dy, clone);
e.consume();
}
else if (cellBounds != null)
{
double dx = e.getX() - first.x;
double dy = e.getY() - first.y;
if (previewBounds != null)
{
setPreviewBounds(new Rectangle(getPreviewLocation(e,
gridEnabledEvent), previewBounds.getSize()));
}
if (!isVisible() && graphComponent.isSignificant(dx, dy))
{
if (imagePreview && dragImage == null
&& !graphComponent.isDragEnabled())
{
updateDragImage(cells);
}
setVisible(true);
}
e.consume();
}
}
}
}
/**
*
*/
protected Point getPreviewLocation(MouseEvent e, boolean gridEnabled)
{
int x = 0;
int y = 0;
if (first != null && cellBounds != null)
{
mxGraph graph = graphComponent.getGraph();
double scale = graph.getView().getScale();
mxPoint trans = graph.getView().getTranslate();
// LATER: Drag image _size_ depends on the initial position and may sometimes
// not align with the grid when dragging. This is because the rounding of the width
// and height at the initial position may be different than that at the current
// position as the left and bottom side of the shape must align to the grid lines.
// Only fix is a full repaint of the drag cells at each new mouse location.
double dx = e.getX() - first.x;
double dy = e.getY() - first.y;
double dxg = ((cellBounds.getX() + dx) / scale) - trans.getX();
double dyg = ((cellBounds.getY() + dy) / scale) - trans.getY();
if (gridEnabled)
{
dxg = graph.snap(dxg);
dyg = graph.snap(dyg);
}
x = (int) Math.round((dxg + trans.getX()) * scale)
+ (int) Math.round(bbox.getX())
- (int) Math.round(cellBounds.getX());
y = (int) Math.round((dyg + trans.getY()) * scale)
+ (int) Math.round(bbox.getY())
- (int) Math.round(cellBounds.getY());
}
return new Point(x, y);
}
/**
*
* @param e
*/
public void dragExit(DropTargetEvent e)
{
mxGraphTransferHandler handler = getGraphTransferHandler(e);
if (handler != null)
{
handler.setLocation(null);
}
dragCells = null;
setVisible(false);
marker.reset();
reset();
}
/**
*
* @param e
*/
public void drop(DropTargetDropEvent e)
{
if (canImport)
{
mxGraphTransferHandler handler = getGraphTransferHandler(e);
MouseEvent event = createEvent(e);
// Ignores the event in mouseReleased if it is
// handled by the transfer handler as a drop
if (handler != null && !handler.isLocalDrag())
{
event.consume();
}
mouseReleased(event);
}
}
/**
*
*/
public void mouseReleased(MouseEvent e)
{
if (graphComponent.isEnabled() && isEnabled() && !e.isConsumed())
{
mxGraph graph = graphComponent.getGraph();
double dx = 0;
double dy = 0;
if (first != null && (cellBounds != null || movePreview.isActive()))
{
double scale = graph.getView().getScale();
mxPoint trans = graph.getView().getTranslate();
// TODO: Simplify math below, this was copy pasted from
// getPreviewLocation with the rounding removed
dx = e.getX() - first.x;
dy = e.getY() - first.y;
if (cellBounds != null)
{
double dxg = ((cellBounds.getX() + dx) / scale)
- trans.getX();
double dyg = ((cellBounds.getY() + dy) / scale)
- trans.getY();
if (gridEnabledEvent)
{
dxg = graph.snap(dxg);
dyg = graph.snap(dyg);
}
double x = ((dxg + trans.getX()) * scale) + (bbox.getX())
- (cellBounds.getX());
double y = ((dyg + trans.getY()) * scale) + (bbox.getY())
- (cellBounds.getY());
dx = Math.round((x - bbox.getX()) / scale);
dy = Math.round((y - bbox.getY()) / scale);
}
}
if (first == null
|| !graphComponent.isSignificant(e.getX() - first.x,
e.getY() - first.y))
{
// Delayed handling of selection
if (cell != null && !e.isPopupTrigger() && isSelectEnabled()
&& (first != null || !isMoveEnabled()))
{
graphComponent.selectCellForEvent(cell, e);
}
// Delayed folding for cell that was initially under the mouse
if (graphComponent.isFoldingEnabled()
&& graphComponent.hitFoldingIcon(initialCell, e.getX(),
e.getY()))
{
fold(initialCell);
}
else
{
// Handles selection if no cell was initially under the mouse
Object tmp = graphComponent.getCellAt(e.getX(), e.getY(),
graphComponent.isSwimlaneSelectionEnabled());
if (cell == null && first == null)
{
if (tmp == null)
{
if (!graphComponent.isToggleEvent(e))
{
graph.clearSelection();
}
}
else if (graph.isSwimlane(tmp)
&& graphComponent.getCanvas()
.hitSwimlaneContent(graphComponent,
graph.getView().getState(tmp),
e.getX(), e.getY()))
{
graphComponent.selectCellForEvent(tmp, e);
}
}
if (graphComponent.isFoldingEnabled()
&& graphComponent.hitFoldingIcon(tmp, e.getX(),
e.getY()))
{
fold(tmp);
e.consume();
}
}
}
else if (movePreview.isActive())
{
if (graphComponent.isConstrainedEvent(e))
{
if (Math.abs(dx) > Math.abs(dy))
{
dy = 0;
}
else
{
dx = 0;
}
}
mxCellState markedState = marker.getMarkedState();
Object target = (markedState != null) ? markedState.getCell()
: null;
// FIXME: Cell is null if selection was carried out, need other variable
//trace("cell", cell);
if (target == null
&& isRemoveCellsFromParent()
&& shouldRemoveCellFromParent(graph.getModel()
.getParent(initialCell), cells, e))
{
target = graph.getDefaultParent();
}
boolean clone = isCloneEnabled()
&& graphComponent.isCloneEvent(e);
Object[] result = movePreview.stop(true, e, dx, dy, clone,
target);
if (cells != result)
{
graph.setSelectionCells(result);
}
e.consume();
}
else if (isVisible())
{
if (constrainedEvent)
{
if (Math.abs(dx) > Math.abs(dy))
{
dy = 0;
}
else
{
dx = 0;
}
}
mxCellState targetState = marker.getValidState();
Object target = (targetState != null) ? targetState.getCell()
: null;
if (graph.isSplitEnabled()
&& graph.isSplitTarget(target, cells))
{
graph.splitEdge(target, cells, dx, dy);
}
else
{
moveCells(cells, dx, dy, target, e);
}
e.consume();
}
}
reset();
}
/**
*
*/
protected void fold(Object cell)
{
boolean collapse = !graphComponent.getGraph().isCellCollapsed(cell);
graphComponent.getGraph().foldCells(collapse, false,
new Object[] { cell });
}
/**
*
*/
public void reset()
{
if (movePreview.isActive())
{
movePreview.stop(false, null, 0, 0, false, null);
}
setVisible(false);
marker.reset();
initialCell = null;
dragCells = null;
dragImage = null;
cells = null;
first = null;
cell = null;
}
/**
* Returns true if the given cells should be removed from the parent for the specified
* mousereleased event.
*/
protected boolean shouldRemoveCellFromParent(Object parent, Object[] cells,
MouseEvent e)
{
if (graphComponent.getGraph().getModel().isVertex(parent))
{
mxCellState pState = graphComponent.getGraph().getView()
.getState(parent);
return pState != null && !pState.contains(e.getX(), e.getY());
}
return false;
}
/**
*
* @param dx
* @param dy
* @param e
*/
protected void moveCells(Object[] cells, double dx, double dy,
Object target, MouseEvent e)
{
mxGraph graph = graphComponent.getGraph();
boolean clone = e.isControlDown() && isCloneEnabled();
if (clone)
{
cells = graph.getCloneableCells(cells);
}
// Removes cells from parent
if (target == null
&& isRemoveCellsFromParent()
&& shouldRemoveCellFromParent(
graph.getModel().getParent(initialCell), cells, e))
{
target = graph.getDefaultParent();
}
Object[] tmp = graph.moveCells(cells, dx, dy, clone, target,
e.getPoint());
if (isSelectEnabled() && clone && tmp != null
&& tmp.length == cells.length)
{
graph.setSelectionCells(tmp);
}
}
/**
*
*/
public void paint(Graphics g)
{
if (isVisible() && previewBounds != null)
{
if (dragImage != null)
{
// LATER: Clipping with mxUtils doesnt fix the problem
// of the drawImage being painted over the scrollbars
Graphics2D tmp = (Graphics2D) g.create();
if (graphComponent.getPreviewAlpha() < 1)
{
tmp.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER,
graphComponent.getPreviewAlpha()));
}
tmp.drawImage(dragImage.getImage(), previewBounds.x,
previewBounds.y, dragImage.getIconWidth(),
dragImage.getIconHeight(), null);
tmp.dispose();
}
else if (!imagePreview)
{
mxConstants.PREVIEW_BORDER.paintBorder(graphComponent, g,
previewBounds.x, previewBounds.y, previewBounds.width,
previewBounds.height);
}
}
}
/**
*
*/
protected MouseEvent createEvent(DropTargetEvent e)
{
JComponent component = getDropTarget(e);
Point location = null;
int action = 0;
if (e instanceof DropTargetDropEvent)
{
location = ((DropTargetDropEvent) e).getLocation();
action = ((DropTargetDropEvent) e).getDropAction();
}
else if (e instanceof DropTargetDragEvent)
{
location = ((DropTargetDragEvent) e).getLocation();
action = ((DropTargetDragEvent) e).getDropAction();
}
if (location != null)
{
location = convertPoint(location);
Rectangle r = graphComponent.getViewport().getViewRect();
location.translate(r.x, r.y);
}
// LATER: Fetch state of modifier keys from event or via global
// key listener using Toolkit.getDefaultToolkit().addAWTEventListener(
// new AWTEventListener() {...}, AWTEvent.KEY_EVENT_MASK). Problem
// is the event does not contain the modifier keys and the global
// handler is not called during drag and drop.
int mod = (action == TransferHandler.COPY) ? InputEvent.CTRL_MASK : 0;
return new MouseEvent(component, 0, System.currentTimeMillis(), mod,
location.x, location.y, 1, false, MouseEvent.BUTTON1);
}
/**
* Helper method to return the component for a drop target event.
*/
protected static final mxGraphTransferHandler getGraphTransferHandler(
DropTargetEvent e)
{
JComponent component = getDropTarget(e);
TransferHandler transferHandler = component.getTransferHandler();
if (transferHandler instanceof mxGraphTransferHandler)
{
return (mxGraphTransferHandler) transferHandler;
}
return null;
}
/**
* Helper method to return the component for a drop target event.
*/
protected static final JComponent getDropTarget(DropTargetEvent e)
{
return (JComponent) e.getDropTargetContext().getComponent();
}
}