/*
* (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.swing;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.Window;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.SwingUtilities;
public class MouseResizer implements MouseListener, MouseMotionListener {
private static final int RESIZE_ZONE_WIDTH = 3;
private Component component;
private MouseResizerDelegate delegate;
private Cursor previousCursor;
private ResizeMode mode;
private Point pressedPoint;
private Point lastDraggedPoint;
private ResizeMode[] modesToUse;
public enum ResizeMode {
NORTH, SOUTH, WEST, EAST, NORTH_EAST, NORTH_WEST, SOUTH_WEST, SOUTH_EAST, NONE;
}
public interface MouseResizerDelegate {
/**
* This method is invoked as the mouse is being dragged.
*
* @param deltaX
* @param deltaY
*/
public void resizeDirectlyBy(int deltaX, int deltaY);
/**
* This method is invoked when the mouse is released.
*
* @param deltaX
* the total distance on the X-axis performed by the mouse
* @param deltaY
* the total distance on the Y-axis performed by the mouse
*/
public void resizeBy(int deltaX, int deltaY);
}
public MouseResizer(Component component, MouseResizerDelegate delegate) {
this(component, delegate, (ResizeMode[]) null);
}
public MouseResizer(Component component, MouseResizerDelegate delegate, ResizeMode... modesToUse) {
this.component = component;
this.delegate = delegate;
this.mode = ResizeMode.NONE;
this.modesToUse = modesToUse;
component.addMouseListener(this);
component.addMouseMotionListener(this);
}
@Override
public void mouseClicked(MouseEvent e) {
// Nothing to do
}
@Override
public void mouseEntered(MouseEvent e) {
if (mode == ResizeMode.NONE) {
updateCursor(e);
}
}
@Override
public void mouseExited(MouseEvent e) {
if (mode == ResizeMode.NONE) {
resetCursor();
}
}
@Override
public void mousePressed(MouseEvent e) {
if (isWithinResizeZone(e)) {
pressedPoint = e.getPoint();
if (isInNorthZone(e)) {
mode = ResizeMode.NORTH;
} else if (isInSouthZone(e)) {
mode = ResizeMode.SOUTH;
} else if (isInWestZone(e)) {
mode = ResizeMode.WEST;
} else if (isInEastZone(e)) {
mode = ResizeMode.EAST;
} else if (isInNorthWestZone(e)) {
mode = ResizeMode.NORTH_WEST;
} else if (isInNorthEastZone(e)) {
mode = ResizeMode.NORTH_EAST;
} else if (isInSouthWestZone(e)) {
mode = ResizeMode.SOUTH_WEST;
} else if (isInSouthEastZone(e)) {
mode = ResizeMode.SOUTH_EAST;
}
e.consume();
} else {
mode = ResizeMode.NONE;
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (mode == ResizeMode.NONE) {
return;
}
if (lastDraggedPoint == null) {
notifyResizeDirectlyBy(e.getX() - pressedPoint.x, e.getY() - pressedPoint.y);
} else {
notifyResizeDirectlyBy(e.getX() - lastDraggedPoint.x, e.getY() - lastDraggedPoint.y);
}
notifyResizeBy(e.getX() - pressedPoint.x, e.getY() - pressedPoint.y);
mode = ResizeMode.NONE;
if (e.getComponent() != component) {
resetCursor();
}
pressedPoint = null;
lastDraggedPoint = null;
e.consume();
}
@Override
public void mouseDragged(MouseEvent e) {
if (mode == ResizeMode.NONE) {
return;
}
if (lastDraggedPoint == null) {
notifyResizeDirectlyBy(e.getX() - pressedPoint.x, e.getY() - pressedPoint.y);
} else {
notifyResizeDirectlyBy(e.getX() - lastDraggedPoint.x, e.getY() - lastDraggedPoint.y);
}
lastDraggedPoint = e.getPoint();
e.consume();
}
@Override
public void mouseMoved(MouseEvent e) {
if (mode == ResizeMode.NONE) {
updateCursor(e);
}
}
private void notifyResizeDirectlyBy(int deltaX, int deltaY) {
switch (mode) {
case NORTH:
delegate.resizeDirectlyBy(0, -deltaY);
break;
case SOUTH:
delegate.resizeDirectlyBy(0, deltaY);
break;
case WEST:
delegate.resizeDirectlyBy(-deltaX, 0);
break;
case EAST:
delegate.resizeDirectlyBy(deltaX, 0);
break;
case NORTH_EAST:
delegate.resizeDirectlyBy(deltaX, -deltaY);
break;
case NORTH_WEST:
delegate.resizeDirectlyBy(-deltaX, -deltaY);
break;
case SOUTH_EAST:
delegate.resizeDirectlyBy(deltaX, deltaY);
break;
case SOUTH_WEST:
delegate.resizeDirectlyBy(-deltaX, deltaY);
break;
default:
break;
}
}
private void notifyResizeBy(int deltaX, int deltaY) {
switch (mode) {
case NORTH:
delegate.resizeBy(0, -deltaY);
break;
case SOUTH:
delegate.resizeBy(0, deltaY);
break;
case WEST:
delegate.resizeBy(-deltaX, 0);
break;
case EAST:
delegate.resizeBy(deltaX, 0);
break;
case NORTH_EAST:
delegate.resizeBy(deltaX, -deltaY);
break;
case NORTH_WEST:
delegate.resizeBy(-deltaX, -deltaY);
break;
case SOUTH_EAST:
delegate.resizeBy(deltaX, deltaY);
break;
case SOUTH_WEST:
delegate.resizeBy(-deltaX, deltaY);
break;
default:
break;
}
}
private void resetCursor() {
if (previousCursor != null && mode == ResizeMode.NONE) {
setCursorForComponentAndHierarchy(component, previousCursor);
previousCursor = null;
}
}
private void updateCursor(MouseEvent e) {
if (isWithinResizeZone(e)) {
previousCursor = Cursor.getDefaultCursor();
Cursor cursor = null;
if (isInNorthZone(e)) {
cursor = Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR);
} else if (isInSouthZone(e)) {
cursor = Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR);
} else if (isInWestZone(e)) {
cursor = Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR);
} else if (isInEastZone(e)) {
cursor = Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR);
} else if (isInNorthWestZone(e)) {
cursor = Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR);
} else if (isInNorthEastZone(e)) {
cursor = Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR);
} else if (isInSouthWestZone(e)) {
cursor = Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR);
} else if (isInSouthEastZone(e)) {
cursor = Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR);
}
if (cursor != null) {
setCursorForComponentAndHierarchy(component, cursor);
}
} else {
resetCursor();
}
}
private void setCursorForComponentAndHierarchy(Component component, Cursor cursor) {
component.setCursor(cursor);
SwingUtilities.getAncestorOfClass(Window.class, component).setCursor(cursor);
}
private boolean isWithinResizeZone(MouseEvent e) {
return e.getX() >= -RESIZE_ZONE_WIDTH && e.getX() <= RESIZE_ZONE_WIDTH || e.getY() >= -RESIZE_ZONE_WIDTH
&& e.getY() <= RESIZE_ZONE_WIDTH || e.getX() <= getComponentWidth() + RESIZE_ZONE_WIDTH
&& e.getX() >= getComponentWidth() - RESIZE_ZONE_WIDTH || e.getY() <= getComponentHeight() + RESIZE_ZONE_WIDTH
&& e.getY() >= getComponentHeight() - RESIZE_ZONE_WIDTH;
}
/**
* @return
*/
protected int getComponentHeight() {
return component.getHeight();
}
/**
* @return
*/
protected int getComponentWidth() {
return component.getWidth();
}
private boolean isInNorthZone(MouseEvent e) {
return e.getY() >= -RESIZE_ZONE_WIDTH && e.getY() <= RESIZE_ZONE_WIDTH && e.getX() > RESIZE_ZONE_WIDTH
&& e.getX() < getComponentWidth() - RESIZE_ZONE_WIDTH && isUsable(ResizeMode.NORTH);
}
private boolean isInSouthZone(MouseEvent e) {
return e.getY() >= getComponentHeight() - RESIZE_ZONE_WIDTH && e.getY() <= getComponentHeight() + RESIZE_ZONE_WIDTH
&& e.getX() > RESIZE_ZONE_WIDTH && e.getX() < getComponentWidth() - RESIZE_ZONE_WIDTH && isUsable(ResizeMode.SOUTH);
}
private boolean isInWestZone(MouseEvent e) {
return e.getX() >= -RESIZE_ZONE_WIDTH && e.getX() <= RESIZE_ZONE_WIDTH && e.getY() > RESIZE_ZONE_WIDTH
&& e.getY() < getComponentHeight() - RESIZE_ZONE_WIDTH && isUsable(ResizeMode.WEST);
}
private boolean isInEastZone(MouseEvent e) {
return e.getX() >= getComponentWidth() - RESIZE_ZONE_WIDTH && e.getX() <= getComponentWidth() + RESIZE_ZONE_WIDTH
&& e.getY() > RESIZE_ZONE_WIDTH && e.getY() < getComponentHeight() - RESIZE_ZONE_WIDTH && isUsable(ResizeMode.EAST);
}
private boolean isInNorthWestZone(MouseEvent e) {
return e.getX() >= -RESIZE_ZONE_WIDTH && e.getX() <= RESIZE_ZONE_WIDTH && e.getY() >= -RESIZE_ZONE_WIDTH
&& e.getY() <= RESIZE_ZONE_WIDTH && isUsable(ResizeMode.NORTH_WEST);
}
private boolean isInNorthEastZone(MouseEvent e) {
return e.getX() >= getComponentWidth() - RESIZE_ZONE_WIDTH && e.getX() <= getComponentWidth() + RESIZE_ZONE_WIDTH
&& e.getY() >= -RESIZE_ZONE_WIDTH && e.getY() <= RESIZE_ZONE_WIDTH && isUsable(ResizeMode.NORTH_EAST);
}
private boolean isInSouthWestZone(MouseEvent e) {
return e.getY() >= getComponentHeight() - RESIZE_ZONE_WIDTH && e.getY() <= getComponentHeight() + RESIZE_ZONE_WIDTH
&& e.getX() >= -RESIZE_ZONE_WIDTH && e.getX() <= RESIZE_ZONE_WIDTH && isUsable(ResizeMode.SOUTH_WEST);
}
private boolean isInSouthEastZone(MouseEvent e) {
return e.getY() >= getComponentHeight() - RESIZE_ZONE_WIDTH && e.getY() <= getComponentHeight() + RESIZE_ZONE_WIDTH
&& e.getX() >= getComponentWidth() - RESIZE_ZONE_WIDTH && e.getX() <= getComponentWidth() + RESIZE_ZONE_WIDTH
&& isUsable(ResizeMode.SOUTH_EAST);
}
private boolean isUsable(ResizeMode mode) {
if (modesToUse == null) {
return true;
}
for (int i = 0; i < modesToUse.length; i++) {
if (mode == modesToUse[i]) {
return true;
}
}
return false;
}
public ResizeMode getMode() {
return mode;
}
}