package com.mxgraph.swing.handler;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.swing.util.mxMouseAdapter;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxEventSource.mxIEventListener;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.util.mxUtils;
import com.mxgraph.view.mxCellState;
/**
* Basic example of implementing a handler for rotation. This can be used as follows:
*
* new mxRotationHandler(graphComponent)
*
* Note that the Java core does actually not support rotation for the selection handles,
* perimeter points etc. Feel free to contribute a fix!
*/
public class mxRotationHandler extends mxMouseAdapter
{
/**
*
*/
public static ImageIcon ROTATE_ICON = null;
/**
* Loads the collapse and expand icons.
*/
static
{
ROTATE_ICON = new ImageIcon(
mxRotationHandler.class
.getResource("/com/mxgraph/swing/images/rotate.gif"));
}
/**
*
*/
private static double PI4 = Math.PI / 4;
/**
* Reference to the enclosing graph component.
*/
protected mxGraphComponent graphComponent;
/**
* Specifies if this handler is enabled. Default is true.
*/
protected boolean enabled = true;
/**
*
*/
protected JComponent handle;
/**
*
*/
protected mxCellState currentState;
/**
*
*/
protected double initialAngle;
/**
*
*/
protected double currentAngle;
/**
*
*/
protected Point first;
/**
* Constructs a new rotation handler.
*/
public mxRotationHandler(mxGraphComponent graphComponent)
{
this.graphComponent = graphComponent;
graphComponent.addMouseListener(this);
handle = createHandle();
// 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);
// Needs to catch events because these are consumed
handle.addMouseListener(this);
handle.addMouseMotionListener(this);
}
/**
*
*/
public mxGraphComponent getGraphComponent()
{
return graphComponent;
}
/**
*
*/
public boolean isEnabled()
{
return enabled;
}
/**
*
*/
public void setEnabled(boolean value)
{
enabled = value;
}
/**
*
*/
protected JComponent createHandle()
{
JLabel label = new JLabel(ROTATE_ICON);
label.setSize(ROTATE_ICON.getIconWidth(), ROTATE_ICON.getIconHeight());
label.setOpaque(false);
return label;
}
/**
*
*/
public boolean isStateHandled(mxCellState state)
{
return graphComponent.getGraph().getModel().isVertex(state.getCell());
}
/**
*
*/
public void mousePressed(MouseEvent e)
{
if (currentState != null && handle.getParent() != null
&& e.getSource() == handle /* mouse hits handle */)
{
start(e);
e.consume();
}
}
/**
*
*/
public void start(MouseEvent e)
{
initialAngle = mxUtils.getDouble(currentState.getStyle(),
mxConstants.STYLE_ROTATION) * mxConstants.RAD_PER_DEG;
currentAngle = initialAngle;
first = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(),
graphComponent.getGraphControl());
if (!graphComponent.getGraph().isCellSelected(currentState.getCell()))
{
graphComponent.selectCellForEvent(currentState.getCell(), e);
}
}
/**
*
*/
public void mouseMoved(MouseEvent e)
{
if (graphComponent.isEnabled() && isEnabled())
{
if (handle.getParent() != null && e.getSource() == handle /* mouse hits handle */)
{
graphComponent.getGraphControl().setCursor(
new Cursor(Cursor.HAND_CURSOR));
e.consume();
}
else if (currentState == null
|| !currentState.getRectangle().contains(e.getPoint()))
{
mxCellState eventState = graphComponent
.getGraph()
.getView()
.getState(
graphComponent.getCellAt(e.getX(), e.getY(),
false));
mxCellState state = null;
if (eventState != null && isStateHandled(eventState))
{
state = eventState;
}
if (currentState != state)
{
currentState = state;
if (currentState == null && handle.getParent() != null)
{
handle.setVisible(false);
handle.getParent().remove(handle);
}
else if (currentState != null)
{
if (handle.getParent() == null)
{
// Adds component for rendering the handles (preview is separate)
graphComponent.getGraphControl().add(handle, 0);
handle.setVisible(true);
}
handle.setLocation(
(int) (currentState.getX()
+ currentState.getWidth()
- handle.getWidth() - 4),
(int) (currentState.getY()
+ currentState.getHeight()
- handle.getWidth() - 4));
}
}
}
}
}
/**
*
*/
public void mouseDragged(MouseEvent e)
{
if (graphComponent.isEnabled() && isEnabled() && !e.isConsumed()
&& first != null)
{
mxRectangle dirty = mxUtils.getBoundingBox(currentState,
currentAngle * mxConstants.DEG_PER_RAD);
Point pt = SwingUtilities.convertPoint(e.getComponent(),
e.getPoint(), graphComponent.getGraphControl());
double cx = currentState.getCenterX();
double cy = currentState.getCenterY();
double dx = pt.getX() - cx;
double dy = pt.getY() - cy;
double c = Math.sqrt(dx * dx + dy * dy);
currentAngle = ((pt.getX() > cx) ? -1 : 1) * Math.acos(dy / c)
+ PI4 + initialAngle;
dirty.add(mxUtils.getBoundingBox(currentState, currentAngle
* mxConstants.DEG_PER_RAD));
dirty.grow(1);
// TODO: Compute dirty rectangle and repaint
graphComponent.getGraphControl().repaint(dirty.getRectangle());
e.consume();
}
else if (handle.getParent() != null)
{
handle.getParent().remove(handle);
}
}
/**
*
*/
public void mouseReleased(MouseEvent e)
{
if (graphComponent.isEnabled() && isEnabled() && !e.isConsumed()
&& first != null)
{
double deg = 0;
Object cell = null;
if (currentState != null)
{
cell = currentState.getCell();
/*deg = mxUtils.getDouble(currentState.getStyle(),
mxConstants.STYLE_ROTATION);*/
}
deg += currentAngle * mxConstants.DEG_PER_RAD;
boolean willExecute = cell != null && first != null;
// TODO: Call reset before execute in all handlers that
// offer an execute method
reset();
if (graphComponent.isEnabled() && isEnabled() && !e.isConsumed()
&& willExecute)
{
graphComponent.getGraph().setCellStyles(
mxConstants.STYLE_ROTATION, String.valueOf(deg),
new Object[] { cell });
graphComponent.getGraphControl().repaint();
e.consume();
}
}
currentState = null;
}
/**
*
*/
public void reset()
{
if (handle.getParent() != null)
{
handle.getParent().remove(handle);
}
mxRectangle dirty = null;
if (currentState != null && first != null)
{
dirty = mxUtils.getBoundingBox(currentState, currentAngle
* mxConstants.DEG_PER_RAD);
dirty.grow(1);
}
currentState = null;
currentAngle = 0;
first = null;
if (dirty != null)
{
graphComponent.getGraphControl().repaint(dirty.getRectangle());
}
}
/**
*
*/
public void paint(Graphics g)
{
if (currentState != null && first != null)
{
Rectangle rect = currentState.getRectangle();
double deg = currentAngle * mxConstants.DEG_PER_RAD;
if (deg != 0)
{
((Graphics2D) g).rotate(Math.toRadians(deg),
currentState.getCenterX(), currentState.getCenterY());
}
mxUtils.setAntiAlias((Graphics2D) g, true, false);
g.drawRect(rect.x, rect.y, rect.width, rect.height);
}
}
}