/**
*
*/
package org.korsakow.ide.ui.interfacebuilder;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Frame;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JComponent;
import javax.swing.event.MouseInputAdapter;
import org.korsakow.ide.resources.widget.WidgetComponent;
import org.korsakow.ide.ui.components.KTooltip;
import com.jroller.santosh.border.ResizableBorder;
public class WidgetResizer extends MouseInputAdapter
{
public static class Bounds
{
public int x1;
public int y1;
public int x2;
public int y2;
public Bounds() {
}
public Bounds(Bounds b) {
x1 = b.x1;
y1 = b.y1;
x2 = b.x2;
y2 = b.y2;
}
}
private static Point getMousePosition(MouseEvent mouseEvent)
{
Point point = mouseEvent.getPoint();
Component comp = mouseEvent.getComponent();
point.x += comp.getX();
point.y += comp.getY();
return point;
}
private final WidgetCanvas widgetCanvas;
private KTooltip resizeTip;
private final Set<WidgetComponent> activeComponents = new HashSet<WidgetComponent>();
private Rectangle startBounds = null;
private Point startPoint = null;
protected int cursor;
public WidgetResizer(WidgetCanvas widgetCanvas)
{
this.widgetCanvas = widgetCanvas;
}
@Override
public void mousePressed(MouseEvent mouseEvent) {
WidgetComponent widgetComp = (WidgetComponent)mouseEvent.getComponent();
if (widgetComp.getBorder() instanceof ResizableBorder == false)
return;
ResizableBorder border = (ResizableBorder)widgetComp.getBorder();
Collection<WidgetComponent> widgets = widgetCanvas.getSelectedWidgetComponents();
int newCursor = border.getResizeCursor(mouseEvent);
beginResizeOrMove(getMousePosition(mouseEvent), newCursor, widgets);
}
@Override
public void mouseReleased(MouseEvent mouseEvent) {
endResizeOrMove();
}
@Override
public void mouseMoved(MouseEvent mouseEvent) {
JComponent widgetComp = (JComponent)mouseEvent.getComponent();
if (widgetComp.getBorder() instanceof ResizableBorder) {
ResizableBorder border = (ResizableBorder)widgetComp.getBorder();
widgetComp.setCursor(Cursor.getPredefinedCursor(border.getResizeCursor(mouseEvent)));
}
}
@Override
public void mouseExited(MouseEvent mouseEvent) {
JComponent widgetComp = (JComponent)mouseEvent.getComponent();
widgetComp.setCursor(Cursor.getDefaultCursor());
}
private void beginResizeOrMove(Point mousePoint, int cursor,
Collection<WidgetComponent> widgetComps) {
this.cursor = cursor;
startBounds = new Rectangle(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
for (WidgetComponent widgetComp: widgetComps) {
widgetComp.getWidget().setPropertyUpdating("x", true);
widgetComp.getWidget().setPropertyUpdating("y", true);
widgetComp.getWidget().setPropertyUpdating("width", true);
widgetComp.getWidget().setPropertyUpdating("height", true);
// startPoints.put(widgetComp, mousePoint);
Rectangle bounds = widgetComp.getBounds();
startBounds.x = Math.min(startBounds.x, bounds.x);
startBounds.y = Math.min(startBounds.y, bounds.y);
startBounds.width = Math.max(startBounds.width, bounds.width);
startBounds.height = Math.max(startBounds.height, bounds.height);
activeComponents.add(widgetComp);
}
startPoint = mousePoint;
}
private void endResizeOrMove() {
for (JComponent comp : activeComponents) {
WidgetComponent widgetComp = (WidgetComponent)comp;
widgetComp.getWidget().setPropertyUpdating("x", false);
widgetComp.getWidget().setPropertyUpdating("y", false);
widgetComp.getWidget().setPropertyUpdating("width", false);
widgetComp.getWidget().setPropertyUpdating("height", false);
widgetComp.getWidget().fireBatchPropertyChange();
}
activeComponents.clear();
// startPoints.clear();
startPoint = null;
startBounds = null;
if (resizeTip != null)
resizeTip.setVisible(false);
}
@Override
public void mouseDragged(MouseEvent mouseEvent)
{
Point mousePoint = getMousePosition(mouseEvent);
final boolean isAltDown = isSnapToGrid(mouseEvent);
final boolean isShiftDown = mouseEvent.isShiftDown();
doDragged(activeComponents, mousePoint, isAltDown, isShiftDown);
}
private boolean isSnapToGrid(MouseEvent mouseEvent){
if(!(widgetCanvas.getModel().isSnapToGrid())){
return !mouseEvent.isAltDown();
}else{
return mouseEvent.isAltDown();
}
}
public void doDragged(final Collection<WidgetComponent> widgets, final Point mousePoint, final boolean isAltDown, final boolean isShiftDown)
{
if (widgets.isEmpty())
return;
if (mousePoint == null)
return;
if (startPoint == null)
return;
final boolean snapToGrid = !isAltDown;
final int gridWidth = widgetCanvas.getModel().getGridWidth();
final int gridHeight = widgetCanvas.getModel().getGridHeight();
Bounds tooltipBounds = null;
if (widgets.size() == 1) {
WidgetComponent widgetComp = widgets.iterator().next();
boolean maintainAspect = widgetComp.getMaintainsAspectByDefaultWhenResized();
if (isShiftDown)
maintainAspect = !maintainAspect;
final Bounds newBounds = doResizeOrMove(widgetCanvas.getModel(), cursor, startPoint, startBounds, mousePoint, gridWidth, gridHeight, snapToGrid, maintainAspect);
widgetComp.setBounds(newBounds.x1, newBounds.y1, newBounds.x2-newBounds.x1, newBounds.y2-newBounds.y1);
tooltipBounds = new Bounds(newBounds);
} else {
boolean maintainAspect = false;
for (WidgetComponent wc : widgets)
maintainAspect = maintainAspect || wc.getMaintainsAspectByDefaultWhenResized();
if (isShiftDown)
maintainAspect = !maintainAspect;
final Bounds newBounds = doResizeOrMove(widgetCanvas.getModel(), cursor, startPoint, startBounds, mousePoint, gridWidth, gridHeight, snapToGrid, maintainAspect);
int dx = newBounds.x1 - startBounds.x;
int dy = newBounds.y1 - startBounds.y;
startBounds.x = newBounds.x1;
startBounds.y = newBounds.y1;
startPoint.x += dx;
startPoint.y += dy;
tooltipBounds = new Bounds(newBounds);
for (WidgetComponent wc : widgets) {
wc.setLocation(wc.getX()+dx, wc.getY()+dy);
}
}
for (JComponent widgetComp : widgets)
{
if (widgetComp.getParent() != null) {
widgetComp.getParent().repaint();
((JComponent)widgetComp.getParent()).revalidate();
}
widgetComp.invalidate();
}
if (resizeTip == null) {
resizeTip = new KTooltip((Frame)widgetCanvas.getTopLevelAncestor());
}
// tooltip
Point pos = MouseInfo.getPointerInfo().getLocation();
if (pos != null) {
tooltipBounds.x1 -= widgetCanvas.getModel().getMovieOffsetX();
tooltipBounds.y1 -= widgetCanvas.getModel().getMovieOffsetY();
tooltipBounds.x2 -= widgetCanvas.getModel().getMovieOffsetX();
tooltipBounds.y2 -= widgetCanvas.getModel().getMovieOffsetY();
pos.y -= resizeTip.getHeight();
String tip = cursor == Cursor.MOVE_CURSOR?
String.format("%d,%d", tooltipBounds.x1, tooltipBounds.y1):
String.format("%d,%d", tooltipBounds.x2-tooltipBounds.x1, tooltipBounds.y2-tooltipBounds.y1);
resizeTip.show(pos, tip);
}
// cursor shouldn't change while dragging
widgetCanvas.setCursor(Cursor.getPredefinedCursor(cursor));
}
public static Bounds doResizeOrMove(
WidgetCanvasModel canvasModel,
final int cursor,
final Point startPoint,
final Rectangle startBounds,
final Point mousePoint,
final int gridWidth,
final int gridHeight,
final boolean snapToGrid, final boolean maintainAspect)
{
final double aspect = startBounds.width / (double)startBounds.height;
final Bounds newBounds = new Bounds();
newBounds.x1 = startBounds.x;
newBounds.y1 = startBounds.y;
newBounds.x2 = startBounds.x+startBounds.width;
newBounds.y2 = startBounds.y+startBounds.height;
final int mX = mousePoint.x;
final int mY = mousePoint.y;
final int movieOffsetX = canvasModel.getMovieOffsetX();
final int movieOffsetY = canvasModel.getMovieOffsetY();
switch(cursor)
{
case Cursor.N_RESIZE_CURSOR:
resizeN(movieOffsetX, movieOffsetY, startPoint, startBounds, gridWidth, gridHeight, maintainAspect, snapToGrid, aspect, mX, mY, newBounds);
break;
case Cursor.S_RESIZE_CURSOR:
resizeS(movieOffsetX, movieOffsetY, startPoint, startBounds, gridWidth, gridHeight, maintainAspect, snapToGrid, aspect, mX, mY, newBounds);
break;
case Cursor.W_RESIZE_CURSOR:
resizeW(movieOffsetX, movieOffsetY, startPoint, startBounds, gridWidth, gridHeight, maintainAspect, snapToGrid, aspect, mX, mY, newBounds);
break;
case Cursor.E_RESIZE_CURSOR:
resizeE(movieOffsetX, movieOffsetY, startPoint, startBounds, gridWidth, gridHeight, maintainAspect, snapToGrid, aspect, mX, mY, newBounds);
break;
case Cursor.NE_RESIZE_CURSOR:
resizeNE(movieOffsetX, movieOffsetY, startPoint, startBounds, gridWidth, gridHeight, maintainAspect, snapToGrid, aspect, mX, mY, newBounds);
break;
case Cursor.SE_RESIZE_CURSOR:
resizeSE(movieOffsetX, movieOffsetY, startPoint, startBounds, gridWidth, gridHeight, maintainAspect, snapToGrid, aspect, mX, mY, newBounds);
break;
case Cursor.SW_RESIZE_CURSOR:
resizeSW(movieOffsetX, movieOffsetY, startPoint, startBounds, gridWidth, gridHeight, maintainAspect, snapToGrid, aspect, mX, mY, newBounds);
break;
case Cursor.NW_RESIZE_CURSOR:
resizeNW(movieOffsetX, movieOffsetY, startPoint, startBounds, gridWidth, gridHeight, maintainAspect, snapToGrid, aspect, mX, mY, newBounds);
break;
case Cursor.MOVE_CURSOR:
move(movieOffsetX, movieOffsetY, startPoint, startBounds, gridWidth, gridHeight, maintainAspect, snapToGrid, aspect, mX, mY, newBounds);
break;
}
return newBounds;
}
private static void move(
int movieOffsetX, int movieOffsetY,
final Point startPoint, final Rectangle startBounds,
final int gridWidth, final int gridHeight,
final boolean maintainAspect, final boolean snapToGrid,
final double aspect, final int mX, final int mY,
final Bounds nb
) {
nb.x1 = mX + (startBounds.x - startPoint.x);
nb.y1 = mY + (startBounds.y - startPoint.y);
nb.x2 = nb.x1 + startBounds.width;
nb.y2 = nb.y1 + startBounds.height;
if (snapToGrid) {
if (gridWidth != 0) {
int dX = (nb.x1-movieOffsetX)%gridWidth;
nb.x1 -= dX;
nb.x2 -= dX;
}
if (gridHeight != 0) {
int dY = (nb.y1-movieOffsetY)%gridHeight;
nb.y1 -= dY;
nb.y2 -= dY;
}
}
}
private static void resizeNW(
int movieOffsetX, int movieOffsetY,
final Point startPoint, final Rectangle startBounds,
final int gridWidth, final int gridHeight,
final boolean maintainAspect, final boolean snapToGrid,
final double aspect, final int mX, final int mY,
final Bounds nb
) {
if (maintainAspect) {
double dX = (mX - (startBounds.x + startBounds.width));
double dY = (mY - (startBounds.y + startBounds.height));
if (snapToGrid) {
dX -= (dX-movieOffsetX)%gridWidth;
dY -= (dY-movieOffsetY)%gridHeight;
}
double distance = Math.sqrt(dX*dX + dY*dY);
if (startBounds.width > startBounds.height) {
nb.x1 = (int)(nb.x2 - distance);
if (snapToGrid)
nb.x1 -= (nb.x1-movieOffsetX)%gridWidth;
int height = (int)((nb.x2-nb.x1)/aspect);
nb.y1 = nb.y2 - height;
} else {
nb.y1 = (int)(nb.y2 - distance);
if (snapToGrid)
nb.y1 -= (nb.y1-movieOffsetY)%gridHeight;
int width = (int)((nb.y2-nb.y2)*aspect);
nb.x1 = nb.x2 - width;
}
} else {
nb.x1 = mX;
nb.y1 = mY;
if (snapToGrid) {
nb.x1 -= (nb.x1-movieOffsetX)%gridWidth;
}
if (snapToGrid)
nb.y1 -= (nb.y1-movieOffsetY)%gridHeight;
if (nb.x1 >= nb.x2)
nb.x1 = nb.x2 - gridWidth;
if (nb.y1 >= nb.y2)
nb.y1 = nb.y2 - gridHeight;
}
}
private static void resizeSW(
int movieOffsetX, int movieOffsetY,
final Point startPoint, final Rectangle startBounds,
final int gridWidth, final int gridHeight,
final boolean maintainAspect, final boolean snapToGrid,
final double aspect, final int mX, final int mY,
final Bounds nb
) {
if (maintainAspect) {
double dX = (mX - (startBounds.x + startBounds.width));
double dY = (mY - (startBounds.y));
if (snapToGrid) {
dX -= (dX-movieOffsetX)%gridWidth;
dY -= (dY-movieOffsetY)%gridHeight;
}
double distance = Math.sqrt(dX*dX + dY*dY);
if (startBounds.width > startBounds.height) {
nb.x1 = (int)(nb.x2 - distance);
if (snapToGrid)
nb.x1 -= (nb.x1-movieOffsetX)%gridWidth;
int height = (int)((nb.x2-nb.x1)/aspect);
nb.y2 = nb.y1 + height;
} else {
nb.y2 = (int)(nb.y1 + distance);
if (snapToGrid)
nb.y2 -= (nb.y2-movieOffsetY)%gridHeight;
int width = (int)((nb.y2-nb.y1)*aspect);
nb.x1 = nb.x2 - width;
}
} else {
nb.x1 = mX;
nb.y2 = mY;
if (snapToGrid) {
nb.x1 -= (nb.x1-movieOffsetX)%gridWidth;
}
if (snapToGrid)
nb.y2 -= (nb.y2-movieOffsetY)%gridHeight;
if (nb.x1 >= nb.x2)
nb.x1 = nb.x2 - gridWidth;
if (nb.y2 <= nb.y1)
nb.y2 = nb.y1 + gridHeight;
}
}
private static void resizeSE(
int movieOffsetX, int movieOffsetY,
final Point startPoint, final Rectangle startBounds,
final int gridWidth, final int gridHeight,
final boolean maintainAspect, final boolean snapToGrid,
final double aspect, final int mX, final int mY,
final Bounds nb
) {
if (maintainAspect) {
double dX = (mX - (startBounds.x));
double dY = (mY - (startBounds.y));
if (snapToGrid) {
dX -= (dX-movieOffsetX)%gridWidth;
dY -= (dY-movieOffsetY)%gridHeight;
}
double distance = Math.sqrt(dX*dX + dY*dY);
if (startBounds.width > startBounds.height) {
nb.x2 = (int)(nb.x1 + distance);
if (snapToGrid)
nb.x2 -= (nb.x2-movieOffsetX)%gridWidth;
int height = (int)((nb.x2-nb.x1)/aspect);
nb.y2 = nb.y1 + height;
} else {
nb.y2 = (int)(nb.y1 + distance);
if (snapToGrid)
nb.y2 -= (nb.y2-movieOffsetY)%gridHeight;
int width = (int)((nb.y2-nb.y1)*aspect);
nb.x2 = nb.x1 + width;
}
} else {
nb.x2 = mX;
nb.y2 = mY;
if (snapToGrid) {
nb.x2 -= (nb.x2-movieOffsetX)%gridWidth;
}
if (snapToGrid)
nb.y2 -= (nb.y2-movieOffsetY)%gridHeight;
if (nb.x2 <= nb.x1)
nb.x2 = nb.x1 + gridWidth;
if (nb.y2 <= nb.y1)
nb.y2 = nb.y1 + gridHeight;
}
}
private static void resizeNE(
int movieOffsetX, int movieOffsetY,
final Point startPoint, final Rectangle startBounds,
final int gridWidth, final int gridHeight,
final boolean maintainAspect, final boolean snapToGrid,
final double aspect, final int mX, final int mY,
final Bounds nb
) {
if (maintainAspect) {
double dX = (mX - (startBounds.x));
double dY = (mY - (startBounds.y + startBounds.height));
if (snapToGrid) {
dX -= (dX+movieOffsetX)%gridWidth;
dY -= (dY+movieOffsetY)%gridHeight;
}
double distance = Math.sqrt(dX*dX + dY*dY);
if (startBounds.width > startBounds.height) {
nb.x2 = (int)(nb.x1 + distance);
if (snapToGrid)
nb.x2 -= (nb.x2+movieOffsetX)%gridWidth;
int height = (int)((nb.x2-nb.x1)/aspect);
nb.y1 = nb.y2 - height;
} else {
nb.y1 = (int)(nb.y2 - distance);
if (snapToGrid)
nb.y1 -= (nb.y1+movieOffsetY)%gridHeight;
int width = (int)((nb.y2-nb.y1)*aspect);
nb.x2 = nb.x1 + width;
}
} else {
nb.x2 = mX;
nb.y1 = mY;
if (snapToGrid) {
nb.x2 -= (nb.x2-movieOffsetX)%gridWidth;
}
if (snapToGrid)
nb.y1 -= (nb.y1-movieOffsetY)%gridHeight;
if (nb.x2 <= nb.x1)
nb.x2 = nb.x1 + gridWidth;
if (nb.y1 >= nb.y2)
nb.y1 = nb.y2 - gridHeight;
}
}
private static void resizeE(
int movieOffsetX, int movieOffsetY,
final Point startPoint, final Rectangle startBounds,
final int gridWidth, final int gridHeight,
final boolean maintainAspect, final boolean snapToGrid,
final double aspect, final int mX, final int mY,
final Bounds nb
) {
if (mX <= nb.x1)
return;
nb.x2 = mX;
if (snapToGrid)
nb.x2 -= (nb.x2-movieOffsetX)%gridWidth;
if (maintainAspect) {
int height = (int)((nb.x2-nb.x1)/aspect);
nb.y1 = startBounds.y + (startBounds.height - height)/2;//(startBounds.height/2 - height/2) introduces rounding errors
nb.y2 = startBounds.y + (startBounds.height + height)/2;
}
}
private static void resizeW(
int movieOffsetX, int movieOffsetY,
final Point startPoint, final Rectangle startBounds,
final int gridWidth, final int gridHeight,
final boolean maintainAspect, final boolean snapToGrid,
final double aspect, final int mX, final int mY,
final Bounds nb
) {
if (mX >= nb.x2)
return;
nb.x1 = mX;
if (snapToGrid)
nb.x1 -= (nb.x1-movieOffsetX)%gridWidth;
if (maintainAspect) {
int height = (int)((nb.x2-nb.x1)/aspect);
nb.y1 = startBounds.y + (startBounds.height - height)/2;
nb.y2 = startBounds.y + (startBounds.height + height)/2;
}
}
private static void resizeS(
int movieOffsetX, int movieOffsetY,
final Point startPoint, final Rectangle startBounds,
final int gridWidth, final int gridHeight,
final boolean maintainAspect, final boolean snapToGrid,
final double aspect, final int mX, final int mY,
final Bounds nb
) {
if (mY <= nb.y1)
return;
nb.y2 = mY;
if (snapToGrid)
nb.y2 -= (nb.y2-movieOffsetY)%gridHeight;
if (maintainAspect) {
int width = (int)((nb.y2-nb.y1)*aspect);
nb.x1 = startBounds.x + (startBounds.width - width)/2;
nb.x2 = startBounds.x + (startBounds.width + width)/2;
}
}
private static void resizeN(
int movieOffsetX, int movieOffsetY,
final Point startPoint, final Rectangle startBounds,
final int gridWidth, final int gridHeight,
final boolean maintainAspect, final boolean snapToGrid,
final double aspect, final int mX, final int mY,
final Bounds nb
) {
if (mY >= nb.y2)
return;
nb.y1 = mY;
if (snapToGrid)
nb.y1 -= (nb.y1-movieOffsetY)%gridHeight;
if (maintainAspect) {
int width = (int)((nb.y2-nb.y1)*aspect);
nb.x1 = startBounds.x + (startBounds.width - width)/2;
nb.x2 = startBounds.x + (startBounds.width + width)/2;
}
}
}