/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* ValueEditorResizeMouseListener.java
* Creation date: (03/07/01 9:57:00 AM)
* By: Michael Cheng
*/
package org.openquark.gems.client.valueentry;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import javax.swing.JComponent;
import javax.swing.event.MouseInputAdapter;
/**
* A mouse listener that enables resize capability for value editors. It allows resizing the editor from
* the left, right and bottom borders. This listener is automatically added by the ValueEditor base class.
* Note: If you use the listener, remember to add it as a Mouse and MouseMotion listener.
* Note: Since this class may change the JPanel's mouse cursor, it is very important that any JPanel that use this
* sets its components (Eg: ScrollPane, buttons, etc) to have the default cursor. This way, the default cursor will
* be shown on the JPanel's components, while the mouse cursor on the JPanel itself will be determined by this class.
* @author Michael Cheng
* @author Frank Worsley
*/
class ValueEditorResizeMouseListener extends MouseInputAdapter {
/**
* Unless a person is extremely dexterious, allow a 'fudge' factor
* that if the person moves the mouse pointer close enough to the
* border of the editor, then enable the resize.
*/
private static final int FUDGE_FACTOR = 8;
/** The default minimum size for an editor. */
private static final Dimension DEFAULT_MIN_SIZE = new Dimension(50, 50);
/** The default maximum size for an editor. */
private static final Dimension DEFAULT_MAX_SIZE = new Dimension(2048, 2048);
/** Whether the south side is being resized. */
private boolean southResize = false;
/** Whether the east side is being resized. */
private boolean eastResize = false;
/** Whether the west side is being resized. */
private boolean westResize = false;
/** Whether the north side is being resized. */
private boolean northResize = false;
/** Whether the location of the editor is being changed. */
private boolean locationChange = false;
/** The point relative to which the user is dragging the mouse. */
private Point dragPoint = null;
/** The panel this resize listener is for. */
private final ValueEditor editor;
/**
* Constructor for a new ValueEditorResizeMouseListener.
* @param editor the ValueEditor this listener is for
*/
public ValueEditorResizeMouseListener(ValueEditor editor) {
if (editor == null) {
throw new NullPointerException();
}
this.editor = editor;
}
/**
* @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
*/
@Override
public void mouseDragged(MouseEvent e) {
// Determine which resizes are valid, and resize accordingly.
// In case you were wondering, e's X and Y are relative to the editor's position.
if (locationChange && editor.isMoveable()) {
int newX = editor.getX() + e.getX() - dragPoint.x;
int newY = editor.getY() + e.getY() - dragPoint.y;
editor.setLocation(newX, newY);
editor.revalidate();
editor.repaint();
} else if (!editor.isResizable()) {
return;
} else if (westResize && southResize) {
int resizeWidth = editor.getWidth() + -e.getX();
int resizeHeight = e.getY();
reshape(resizeWidth, resizeHeight);
} else if (eastResize && southResize) {
int resizeWidth = e.getX();
int resizeHeight = e.getY();
reshape(resizeWidth, resizeHeight);
} else if (westResize && northResize) {
int resizeWidth = editor.getWidth() + -e.getX();
int resizeHeight = editor.getHeight() + -e.getY();
reshape(resizeWidth, resizeHeight);
} else if (eastResize && northResize) {
int resizeWidth = e.getX();
int resizeHeight = editor.getHeight() + -e.getY();
reshape(resizeWidth, resizeHeight);
} else if (eastResize) {
int resizeWidth = e.getX();
reshape(resizeWidth, editor.getHeight());
} else if (westResize) {
int resizeWidth = editor.getWidth() + -e.getX();
reshape(resizeWidth, editor.getHeight());
} else if (southResize) {
int resizeHeight = e.getY();
reshape(editor.getWidth(), resizeHeight);
} else if (northResize) {
int resizeHeight = editor.getHeight() + -e.getY();
reshape(editor.getWidth(), resizeHeight);
}
}
/**
* @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
*/
@Override
public void mouseMoved(MouseEvent e) {
southResize = e.getY() >= editor.getHeight() - FUDGE_FACTOR && e.getY() < editor.getHeight();
eastResize = e.getX() >= editor.getWidth() - FUDGE_FACTOR && e.getX() < editor.getWidth();
westResize = e.getX() <= FUDGE_FACTOR && e.getX() > 0;
northResize = e.getY() <= FUDGE_FACTOR && e.getY() > 0;
locationChange = e.getY() <= editor.getInsets().top && e.getY() > FUDGE_FACTOR;
if (!editor.isResizable()) {
return;
} else if (eastResize && southResize) {
editor.setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));
} else if (westResize && southResize) {
editor.setCursor(Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR));
} else if (eastResize && northResize) {
editor.setCursor(Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR));
} else if (westResize && northResize) {
editor.setCursor(Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR));
} else if (eastResize) {
editor.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
} else if (southResize) {
editor.setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
} else if (westResize) {
editor.setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR));
} else if (northResize) {
editor.setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
} else {
editor.setCursor(null);
}
}
/**
* Resizes the editor to the given width and height. If the given size is outside
* the allowed resize range, then it will be changed to fit the range.
* @param width the new width
* @param height the new height
*/
private void reshape(int width, int height) {
// Enfore the minimum/maximum size requirements.
Dimension minSize = editor.getMinResizeDimension();
Dimension maxSize = editor.getMaxResizeDimension();
if (minSize == null) {
minSize = DEFAULT_MIN_SIZE;
}
if (maxSize == null) {
maxSize = DEFAULT_MAX_SIZE;
}
width = ValueEditorManager.clamp(minSize.width, width, maxSize.width);
height = ValueEditorManager.clamp(minSize.height, height, maxSize.height);
// Editor position may change while this is running.
int x = editor.getX();
int y = editor.getY();
Component parent = editor.getParent();
Rectangle parentRect = new Rectangle(parent.getWidth(), parent.getHeight());
if (parent instanceof JComponent) {
parentRect = ((JComponent) parent).getVisibleRect();
}
if (westResize) {
// Components resize to the east, so if we want to give the user the
// appearance that he is resizing to the west, we need to move the
// editor to the left or right.
x = editor.getX() + editor.getWidth() - width;
if (x < parentRect.x) {
// If we have sized all the way to the left edge, then we
// don't want to resize any further.
x = parentRect.x;
width = editor.getX() + editor.getWidth() - parentRect.x;
}
}
if (northResize) {
// See above.
y = editor.getY() + editor.getHeight() - height;
if (y < parentRect.y) {
y = parentRect.y;
height = editor.getY() + editor.getHeight() - parentRect.y;
}
}
// We have to set size and location twice for this to work in all cases.
// This is because the value editors override these methods to enforce that
// they are always displayed fully within their parent's bounds. If the user
// resizes an editor very quickly and we try to change its location before
// changing its size, then the editor may be positioned outside the parent's
// bounds. The setLocation method will then modify the location to place the
// editor within the parent bounds. At this point the sizing will be screwed
// up. Sometimes we need to set location before setting size, at other times
// it's the reverse. Therefore we just do it twice to catch all cases.
if (x + width > parentRect.x + parentRect.width) {
width = parentRect.x + parentRect.width - x;
}
if (y + height > parentRect.y + parentRect.height) {
height = parentRect.y + parentRect.height - y;
}
editor.setLocation(x, y);
editor.setSize(width, height);
editor.setLocation(x, y);
editor.setSize(width, height);
editor.revalidate();
editor.repaint();
editor.userHasResized();
}
/**
* @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
*/
@Override
public void mousePressed(MouseEvent e) {
dragPoint = e.getPoint();
}
/**
* @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
*/
@Override
public void mouseReleased(MouseEvent e) {
dragPoint = null;
}
}