Widening.

git-svn-id: https://samskivert.googlecode.com/svn/trunk@2507 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
samskivert
2008-12-18 01:29:32 +00:00
parent c1f89f9227
commit b7e85472f4
+109 -159
View File
@@ -70,6 +70,33 @@ import com.samskivert.util.SortableArrayList;
*/ */
public class SwingUtil public class SwingUtil
{ {
/**
* An operation that may be applied to a component.
*/
public static interface ComponentOp
{
/** Apply an operation to the given component. */
public void apply (Component comp);
}
/**
* An interface for validating the text contained within a document.
*/
public static interface DocumentValidator
{
/** Return false if the text is not valid for any reason. */
public boolean isValid (String text);
}
/**
* An interface for transforming the text contained within a document.
*/
public static interface DocumentTransformer
{
/** Transform the specified text in some way, or simply return the text untransformed. */
public String transform (String text);
}
/** /**
* Center the given window within the screen boundaries. * Center the given window within the screen boundaries.
* *
@@ -98,13 +125,12 @@ public class SwingUtil
public static void centerComponent (Component a, Component b) public static void centerComponent (Component a, Component b)
{ {
Dimension asize = a.getSize(), bsize = b.getSize(); Dimension asize = a.getSize(), bsize = b.getSize();
b.setLocation((asize.width - bsize.width) / 2, b.setLocation((asize.width - bsize.width) / 2, (asize.height - bsize.height) / 2);
(asize.height - bsize.height) / 2);
} }
/** /**
* Draw a string centered within a rectangle. The string is drawn * Draw a string centered within a rectangle. The string is drawn using the graphics context's
* using the graphics context's current font and color. * current font and color.
* *
* @param g the graphics context. * @param g the graphics context.
* @param str the string. * @param str the string.
@@ -114,7 +140,7 @@ public class SwingUtil
* @param height the bounding height. * @param height the bounding height.
*/ */
public static void drawStringCentered ( public static void drawStringCentered (
Graphics g, String str, int x, int y, int width, int height) Graphics g, String str, int x, int y, int width, int height)
{ {
FontMetrics fm = g.getFontMetrics(g.getFont()); FontMetrics fm = g.getFontMetrics(g.getFont());
int xpos = x + ((width - fm.stringWidth(str)) / 2); int xpos = x + ((width - fm.stringWidth(str)) / 2);
@@ -123,41 +149,35 @@ public class SwingUtil
} }
/** /**
* Returns the most reasonable position for the specified rectangle to * Returns the most reasonable position for the specified rectangle to be placed at so as to
* be placed at so as to maximize its containment by the specified * maximize its containment by the specified bounding rectangle while still placing it as near
* bounding rectangle while still placing it as near its original * its original coordinates as possible.
* coordinates as possible.
* *
* @param rect the rectangle to be positioned. * @param rect the rectangle to be positioned.
* @param bounds the containing rectangle. * @param bounds the containing rectangle.
*/ */
public static Point fitRectInRect ( public static Point fitRectInRect (Rectangle rect, Rectangle bounds)
Rectangle rect, Rectangle bounds)
{ {
// Guarantee that the right and bottom edges will be contained // Guarantee that the right and bottom edges will be contained and do our best for the top
// and do our best for the top and left edges. // and left edges.
return new Point( return new Point(Math.min(bounds.x + bounds.width - rect.width,
Math.min(bounds.x + bounds.width - rect.width, Math.max(rect.x, bounds.x)),
Math.max(rect.x, bounds.x)), Math.min(bounds.y + bounds.height - rect.height,
Math.min(bounds.y + bounds.height - rect.height, Math.max(rect.y, bounds.y)));
Math.max(rect.y, bounds.y)));
} }
/** /**
* Position the specified rectangle as closely as possible to * Position the specified rectangle as closely as possible to its current position, but make
* its current position, but make sure it is within the specified * sure it is within the specified bounds and that it does not overlap any of the Shapes
* bounds and that it does not overlap any of the Shapes contained * contained in the avoid list.
* in the avoid list.
* *
* @param r the rectangle to attempt to position. * @param r the rectangle to attempt to position.
* @param bounds the bounding box within which the rectangle must be * @param bounds the bounding box within which the rectangle must be positioned.
* positioned. * @param avoidShapes a collection of Shapes that must not be overlapped. The collection will
* @param avoidShapes a collection of Shapes that must not be overlapped. * be destructively modified.
* The collection will be destructively modified.
* *
* @return true if the rectangle was successfully placed, given the * @return true if the rectangle was successfully placed, given the constraints, or false if
* constraints, or false if the positioning failed (the rectangle will * the positioning failed (the rectangle will be left at it's original location.
* be left at it's original location.
*/ */
public static boolean positionRect ( public static boolean positionRect (
Rectangle r, Rectangle bounds, Collection<? extends Shape> avoidShapes) Rectangle r, Rectangle bounds, Collection<? extends Shape> avoidShapes)
@@ -165,8 +185,7 @@ public class SwingUtil
Point origPos = r.getLocation(); Point origPos = r.getLocation();
Comparator<Point> comp = createPointComparator(origPos); Comparator<Point> comp = createPointComparator(origPos);
SortableArrayList<Point> possibles = new SortableArrayList<Point>(); SortableArrayList<Point> possibles = new SortableArrayList<Point>();
// we start things off with the passed-in point (adjusted to // start things off with the passed-in point (adjusted to be inside the bounds, if needed)
// be inside the bounds, if needed)
possibles.add(fitRectInRect(r, bounds)); possibles.add(fitRectInRect(r, bounds));
// keep track of area that doesn't generate new possibles // keep track of area that doesn't generate new possibles
@@ -184,7 +203,6 @@ public class SwingUtil
// see if it hits any shapes we're trying to avoid // see if it hits any shapes we're trying to avoid
for (Iterator<? extends Shape> iter = avoidShapes.iterator(); iter.hasNext(); ) { for (Iterator<? extends Shape> iter = avoidShapes.iterator(); iter.hasNext(); ) {
Shape shape = iter.next(); Shape shape = iter.next();
if (shape.intersects(r)) { if (shape.intersects(r)) {
// remove that shape from our avoid list // remove that shape from our avoid list
iter.remove(); iter.remove();
@@ -193,7 +211,6 @@ public class SwingUtil
// add 4 new possible points, each pushed in one direction // add 4 new possible points, each pushed in one direction
Rectangle pusher = shape.getBounds(); Rectangle pusher = shape.getBounds();
possibles.add(new Point(pusher.x - r.width, r.y)); possibles.add(new Point(pusher.x - r.width, r.y));
possibles.add(new Point(r.x, pusher.y - r.height)); possibles.add(new Point(r.x, pusher.y - r.height));
possibles.add(new Point(pusher.x + pusher.width, r.y)); possibles.add(new Point(pusher.x + pusher.width, r.y));
@@ -215,17 +232,14 @@ public class SwingUtil
} }
/** /**
* Create a comparator that compares against the distance from * Create a comparator that compares against the distance from the specified point.
* the specified point.
* *
* Note: The comparator will continue to sort by distance from the origin * Note: The comparator will continue to sort by distance from the origin point, even if the
* point, even if the origin point's coordinates are modified after * origin point's coordinates are modified after the comparator is created.
* the comparator is created.
* *
* Used by positionRect(). * Used by positionRect().
*/ */
public static <P extends Point2D> Comparator<P> createPointComparator ( public static <P extends Point2D> Comparator<P> createPointComparator (final P origin)
final P origin)
{ {
return new Comparator<P>() { return new Comparator<P>() {
public int compare (P p1, P p2) public int compare (P p1, P p2)
@@ -238,11 +252,10 @@ public class SwingUtil
} }
/** /**
* Enables (or disables) the specified component, <em>and all of its * Enables (or disables) the specified component, <em>and all of its children.</em> A simple
* children.</cite> A simple call to {@link Container#setEnabled} * call to {@link Container#setEnabled} does not propagate the enabled state to the children of
* does not propagate the enabled state to the children of a * a component, which is senseless in our opinion, but was surely done for some arguably good
* component, which is senseless in our opinion, but was surely done * reason.
* for some arguably good reason.
*/ */
public static void setEnabled (Container comp, final boolean enabled) public static void setEnabled (Container comp, final boolean enabled)
{ {
@@ -254,8 +267,7 @@ public class SwingUtil
} }
/** /**
* Set the opacity on the specified component, <em>and all of its * Set the opacity on the specified component, <em>and all of its children.</em>
* children.</cite>
*/ */
public static void setOpaque (JComponent comp, final boolean opaque) public static void setOpaque (JComponent comp, final boolean opaque)
{ {
@@ -269,8 +281,7 @@ public class SwingUtil
} }
/** /**
* Apply the specified ComponentOp to the supplied component * Apply the specified ComponentOp to the supplied component and then all its descendants.
* and then all its descendants.
*/ */
public static void applyToHierarchy (Component comp, ComponentOp op) public static void applyToHierarchy (Component comp, ComponentOp op)
{ {
@@ -278,11 +289,10 @@ public class SwingUtil
} }
/** /**
* Apply the specified ComponentOp to the supplied component * Apply the specified ComponentOp to the supplied component and then all its descendants, up
* and then all its descendants, up to the specified maximum depth. * to the specified maximum depth.
*/ */
public static void applyToHierarchy ( public static void applyToHierarchy (Component comp, int depth, ComponentOp op)
Component comp, int depth, ComponentOp op)
{ {
if (comp == null) { if (comp == null) {
return; return;
@@ -299,78 +309,36 @@ public class SwingUtil
} }
/** /**
* An operation that may be applied to a component. * Set active Document helpers on the specified text component. Changes will not and cannot be
*/ * made (either via user inputs or direct method manipulation) unless the validator says that
public static interface ComponentOp * the changes are ok.
{
/**
* Apply an operation to the given component.
*/
public void apply (Component comp);
}
/**
* An interface for validating the text contained within a document.
*/
public static interface DocumentValidator
{
/**
* Should return false if the text is not valid for any reason.
*/
public boolean isValid (String text);
}
/**
* An interface for transforming the text contained within a document.
*/
public static interface DocumentTransformer
{
/**
* Should transform the specified text in some way, or simply
* return the text untransformed.
*/
public String transform (String text);
}
/**
* Set active Document helpers on the specified text component.
* Changes will not and cannot be made (either via user inputs or
* direct method manipulation) unless the validator says that the
* changes are ok.
* *
* @param validator if non-null, all changes are sent to this for approval. * @param validator if non-null, all changes are sent to this for approval.
* @param transformer if non-null, is queried to change the text * @param transformer if non-null, is queried to change the text after all changes are made.
* after all changes are made.
*/ */
public static void setDocumentHelpers ( public static void setDocumentHelpers (JTextComponent comp, DocumentValidator validator,
JTextComponent comp, DocumentValidator validator, DocumentTransformer transformer)
DocumentTransformer transformer)
{ {
setDocumentHelpers(comp.getDocument(), validator, transformer); setDocumentHelpers(comp.getDocument(), validator, transformer);
} }
/** /**
* Set active Document helpers on the specified Document. * Set active Document helpers on the specified Document. Changes will not and cannot be made
* Changes will not and cannot be made (either via user inputs or * (either via user inputs or direct method manipulation) unless the validator says that the
* direct method manipulation) unless the validator says that the
* changes are ok. * changes are ok.
* *
* @param validator if non-null, all changes are sent to this for approval. * @param validator if non-null, all changes are sent to this for approval.
* @param transformer if non-null, is queried to change the text * @param transformer if non-null, is queried to change the text after all changes are made.
* after all changes are made.
*/ */
public static void setDocumentHelpers ( public static void setDocumentHelpers (final Document doc, final DocumentValidator validator,
final Document doc, final DocumentValidator validator, final DocumentTransformer transformer)
final DocumentTransformer transformer)
{ {
if (!(doc instanceof AbstractDocument)) { if (!(doc instanceof AbstractDocument)) {
throw new IllegalArgumentException( throw new IllegalArgumentException("Specified document cannot be filtered!");
"Specified document cannot be filtered!");
} }
// set up the filter. // set up the filter.
((AbstractDocument) doc).setDocumentFilter(new DocumentFilter() ((AbstractDocument) doc).setDocumentFilter(new DocumentFilter() {
{
@Override public void remove (FilterBypass fb, int offset, int length) @Override public void remove (FilterBypass fb, int offset, int length)
throws BadLocationException throws BadLocationException
{ {
@@ -391,8 +359,7 @@ public class SwingUtil
} }
@Override public void replace ( @Override public void replace (
FilterBypass fb, int offset, int length, String text, FilterBypass fb, int offset, int length, String text, AttributeSet attrs)
AttributeSet attrs)
throws BadLocationException throws BadLocationException
{ {
if (replaceOk(offset, length, text)) { if (replaceOk(offset, length, text)) {
@@ -402,8 +369,7 @@ public class SwingUtil
} }
/** /**
* Convenience for remove/insert/replace to see if the * Convenience for remove/insert/replace to see if the proposed change is valid.
* proposed change is valid.
*/ */
protected boolean replaceOk (int offset, int length, String text) protected boolean replaceOk (int offset, int length, String text)
throws BadLocationException throws BadLocationException
@@ -414,20 +380,18 @@ public class SwingUtil
try { try {
String current = doc.getText(0, doc.getLength()); String current = doc.getText(0, doc.getLength());
String potential = current.substring(0, offset) + String potential = current.substring(0, offset) +
text + current.substring(offset + length); text + current.substring(offset + length);
// validate the potential text. // validate the potential text.
return validator.isValid(potential); return validator.isValid(potential);
} catch (IndexOutOfBoundsException ioobe) { } catch (IndexOutOfBoundsException ioobe) {
throw new BadLocationException( throw new BadLocationException("Bad Location", offset + length);
"Bad Location", offset + length);
} }
} }
/** /**
* After a remove/insert/replace has taken place, we may * After a remove/insert/replace has taken place, we may want to transform the text in
* want to transform the text in some way. * some way.
*/ */
protected void transform (FilterBypass fb) protected void transform (FilterBypass fb)
{ {
@@ -476,8 +440,7 @@ public class SwingUtil
} }
/** /**
* Adjusts the widths and heights of the cells of the supplied table * Adjusts the widths and heights of the cells of the supplied table to fit their contents.
* to fit their contents.
*/ */
public static void sizeToContents (JTable table) public static void sizeToContents (JTable table)
{ {
@@ -491,9 +454,8 @@ public class SwingUtil
int headerWidth = 0, cellWidth = 0; int headerWidth = 0, cellWidth = 0;
column = table.getColumnModel().getColumn(cc); column = table.getColumnModel().getColumn(cc);
try { try {
comp = column.getHeaderRenderer(). comp = column.getHeaderRenderer().getTableCellRendererComponent(
getTableCellRendererComponent(null, column.getHeaderValue(), null, column.getHeaderValue(), false, false, 0, 0);
false, false, 0, 0);
headerWidth = comp.getPreferredSize().width; headerWidth = comp.getPreferredSize().width;
} catch (NullPointerException e) { } catch (NullPointerException e) {
// getHeaderRenderer() this doesn't work in 1.3 // getHeaderRenderer() this doesn't work in 1.3
@@ -502,8 +464,7 @@ public class SwingUtil
for (int rr = 0; rr < rcount; rr++) { for (int rr = 0; rr < rcount; rr++) {
Object cellValue = model.getValueAt(rr, cc); Object cellValue = model.getValueAt(rr, cc);
comp = table.getDefaultRenderer(model.getColumnClass(cc)). comp = table.getDefaultRenderer(model.getColumnClass(cc)).
getTableCellRendererComponent(table, cellValue, getTableCellRendererComponent(table, cellValue, false, false, 0, cc);
false, false, 0, cc);
Dimension psize = comp.getPreferredSize(); Dimension psize = comp.getPreferredSize();
cellWidth = Math.max(psize.width, cellWidth); cellWidth = Math.max(psize.width, cellWidth);
cellHeight = Math.max(psize.height, cellHeight); cellHeight = Math.max(psize.height, cellHeight);
@@ -517,11 +478,10 @@ public class SwingUtil
} }
/** /**
* Refreshes the supplied {@link JComponent} to effect a call to * Refreshes the supplied {@link JComponent} to effect a call to {@link JComponent#revalidate}
* {@link JComponent#revalidate} and {@link JComponent#repaint}, which * and {@link JComponent#repaint}, which is frequently necessary in cases such as adding
* is frequently necessary in cases such as adding components to or * components to or removing components from a {@link JPanel} since Swing doesn't automatically
* removing components from a {@link JPanel} since Swing doesn't * invalidate things for proper re-rendering.
* automatically invalidate things for proper re-rendering.
*/ */
public static void refresh (JComponent c) public static void refresh (JComponent c)
{ {
@@ -530,8 +490,8 @@ public class SwingUtil
} }
/** /**
* Create a custom cursor out of the specified image, putting the hotspot * Create a custom cursor out of the specified image, putting the hotspot in the exact center
* in the exact center of the created cursor. * of the created cursor.
*/ */
public static Cursor createImageCursor (Image img) public static Cursor createImageCursor (Image img)
{ {
@@ -539,8 +499,7 @@ public class SwingUtil
} }
/** /**
* Create a custom cursor out of the specified image, with the specified * Create a custom cursor out of the specified image, with the specified hotspot.
* hotspot.
*/ */
public static Cursor createImageCursor (Image img, Point hotspot) public static Cursor createImageCursor (Image img, Point hotspot)
{ {
@@ -555,8 +514,7 @@ public class SwingUtil
// ", bestSize=" + d.width + "x" + d.height + // ", bestSize=" + d.width + "x" + d.height +
// ", maxcolors=" + colors + "]."); // ", maxcolors=" + colors + "].");
// if the passed-in image is smaller, pad it with transparent pixels // if the passed-in image is smaller, pad it with transparent pixels and use it anyway.
// and use it anyway.
if (((w < d.width) && (h <= d.height)) || if (((w < d.width) && (h <= d.height)) ||
((w <= d.width) && (h < d.height))) { ((w <= d.width) && (h < d.height))) {
Image padder = GraphicsEnvironment.getLocalGraphicsEnvironment(). Image padder = GraphicsEnvironment.getLocalGraphicsEnvironment().
@@ -587,8 +545,8 @@ public class SwingUtil
} }
/** /**
* Adds a one pixel border of random color to this and all panels * Adds a one pixel border of random color to this and all panels contained in this panel's
* contained in this panel's child hierarchy. * child hierarchy.
*/ */
public static void addDebugBorders (JPanel panel) public static void addDebugBorders (JPanel panel)
{ {
@@ -606,15 +564,14 @@ public class SwingUtil
} }
/** /**
* Aligns the first <code>rows</code> * <code>cols</code> components * Aligns the first <code>rows</code> * <code>cols</code> components of <code>parent</code> in
* of <code>parent</code> in a grid. Each component in a column is as * a grid. Each component in a column is as wide as the maximum preferred width of the
* wide as the maximum preferred width of the components in that * components in that column; height is similarly determined for each row. The parent is made
* column; height is similarly determined for each row. The parent is * just big enough to fit them all. The components should be already added to the parent in
* made just big enough to fit them all. The components should be * row-major order.
* already added to the parent in row-major order.
* *
* @param parent the container component; must be configured with a * @param parent the container component; must be configured with a {@link SpringLayout} prior
* {@link SpringLayout} prior to calling this method. * to calling this method.
* @param rows number of rows. * @param rows number of rows.
* @param cols number of columns. * @param cols number of columns.
* @param initialX x location at which to start the grid. * @param initialX x location at which to start the grid.
@@ -622,9 +579,8 @@ public class SwingUtil
* @param xPad x padding between cells. * @param xPad x padding between cells.
* @param yPad y padding between cells. * @param yPad y padding between cells.
*/ */
public static void makeCompactGrid ( public static void makeCompactGrid (Container parent, int rows, int cols,
Container parent, int rows, int cols, int initialX, int initialY, int xPad, int yPad)
int initialX, int initialY, int xPad, int yPad)
{ {
SpringLayout layout = (SpringLayout)parent.getLayout(); SpringLayout layout = (SpringLayout)parent.getLayout();
@@ -633,13 +589,10 @@ public class SwingUtil
for (int c = 0; c < cols; c++) { for (int c = 0; c < cols; c++) {
Spring width = Spring.constant(0); Spring width = Spring.constant(0);
for (int r = 0; r < rows; r++) { for (int r = 0; r < rows; r++) {
width = Spring.max( width = Spring.max(width, getConstraintsForCell(r, c, parent, cols).getWidth());
width, getConstraintsForCell(
r, c, parent, cols).getWidth());
} }
for (int r = 0; r < rows; r++) { for (int r = 0; r < rows; r++) {
SpringLayout.Constraints constraints = SpringLayout.Constraints constraints = getConstraintsForCell(r, c, parent, cols);
getConstraintsForCell(r, c, parent, cols);
constraints.setX(x); constraints.setX(x);
constraints.setWidth(width); constraints.setWidth(width);
} }
@@ -651,13 +604,10 @@ public class SwingUtil
for (int r = 0; r < rows; r++) { for (int r = 0; r < rows; r++) {
Spring height = Spring.constant(0); Spring height = Spring.constant(0);
for (int c = 0; c < cols; c++) { for (int c = 0; c < cols; c++) {
height = Spring.max( height = Spring.max(height, getConstraintsForCell(r, c, parent, cols).getHeight());
height, getConstraintsForCell(
r, c, parent, cols).getHeight());
} }
for (int c = 0; c < cols; c++) { for (int c = 0; c < cols; c++) {
SpringLayout.Constraints constraints = SpringLayout.Constraints constraints = getConstraintsForCell(r, c, parent, cols);
getConstraintsForCell(r, c, parent, cols);
constraints.setY(y); constraints.setY(y);
constraints.setHeight(height); constraints.setHeight(height);
} }