com.mxgraph.swing.handler.mxGraphHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jgraphx Show documentation
Show all versions of jgraphx Show documentation
JGraphX Swing Component - Java Graph Visualization Library
This is a binary & source redistribution of the original, unmodified JGraphX library originating from:
"https://github.com/jgraph/jgraphx/archive/v3.4.1.3.zip".
The purpose of this redistribution is to make the library available to other Maven projects.
/**
* $Id: mxGraphHandler.java,v 1.81 2012/07/30 11:22:37 gaudenz Exp $
* Copyright (c) 2008-2012, JGraph Ltd
*
* 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.model.mxIGraphModel;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.swing.util.mxGraphTransferable;
import com.mxgraph.swing.util.mxMouseAdapter;
import com.mxgraph.swing.util.mxSwingConstants;
import com.mxgraph.util.mxCellRenderer;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxEventSource.mxIEventListener;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.util.mxUtils;
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.
*/
protected 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, mxSwingConstants.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);
}
/**
*
*/
public mxMovePreview getMovePreview()
{
return movePreview;
}
/**
*
*/
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)
{
mxIGraphModel model = graphComponent.getGraph().getModel();
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);
// Checks if parent is dropped into child
Object parent = cell;
while (parent != null)
{
if (mxUtils.contains(cells, parent))
{
return null;
}
parent = model.getParent(parent);
}
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 mxCellMarker getMarker()
{
return marker;
}
/**
*
*/
public void setMarker(mxCellMarker value)
{
marker = 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
if (graphComponent.isAutoScroll())
{
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)
{
mxSwingConstants.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();
}
}