0001 /*
0002 * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025 package javax.swing;
0026
0027 import sun.swing.SwingUtilities2;
0028 import sun.swing.UIAction;
0029
0030 import java.applet.*;
0031
0032 import java.awt.*;
0033 import java.awt.event.*;
0034 import java.awt.dnd.DropTarget;
0035
0036 import java.util.Vector;
0037 import java.util.Hashtable;
0038
0039 import java.lang.reflect.*;
0040
0041 import javax.accessibility.*;
0042 import javax.swing.event.MenuDragMouseEvent;
0043 import javax.swing.plaf.UIResource;
0044 import javax.swing.text.View;
0045 import java.security.AccessController;
0046 import sun.security.action.GetPropertyAction;
0047
0048 import sun.awt.AppContext;
0049
0050 /**
0051 * A collection of utility methods for Swing.
0052 *
0053 * @version 1.154 05/05/07
0054 * @author unknown
0055 */
0056 public class SwingUtilities implements SwingConstants {
0057 // These states are system-wide, rather than AppContext wide.
0058 private static boolean canAccessEventQueue = false;
0059 private static boolean eventQueueTested = false;
0060
0061 /**
0062 * Indicates if we should change the drop target when a
0063 * {@code TransferHandler} is set.
0064 */
0065 private static boolean suppressDropSupport;
0066
0067 /**
0068 * Indiciates if we've checked the system property for suppressing
0069 * drop support.
0070 */
0071 private static boolean checkedSuppressDropSupport;
0072
0073 /**
0074 * Returns true if <code>setTransferHandler</code> should change the
0075 * <code>DropTarget</code>.
0076 */
0077 private static boolean getSuppressDropTarget() {
0078 if (!checkedSuppressDropSupport) {
0079 suppressDropSupport = Boolean.valueOf(AccessController
0080 .doPrivileged(new GetPropertyAction(
0081 "suppressSwingDropSupport")));
0082 checkedSuppressDropSupport = true;
0083 }
0084 return suppressDropSupport;
0085 }
0086
0087 /**
0088 * Installs a {@code DropTarget} on the component as necessary for a
0089 * {@code TransferHandler} change.
0090 */
0091 static void installSwingDropTargetAsNecessary(Component c,
0092 TransferHandler t) {
0093
0094 if (!getSuppressDropTarget()) {
0095 DropTarget dropHandler = c.getDropTarget();
0096 if ((dropHandler == null)
0097 || (dropHandler instanceof UIResource)) {
0098 if (t == null) {
0099 c.setDropTarget(null);
0100 } else if (!GraphicsEnvironment.isHeadless()) {
0101 c
0102 .setDropTarget(new TransferHandler.SwingDropTarget(
0103 c));
0104 }
0105 }
0106 }
0107 }
0108
0109 /**
0110 * Return true if <code>a</code> contains <code>b</code>
0111 */
0112 public static final boolean isRectangleContainingRectangle(
0113 Rectangle a, Rectangle b) {
0114 if (b.x >= a.x && (b.x + b.width) <= (a.x + a.width)
0115 && b.y >= a.y && (b.y + b.height) <= (a.y + a.height)) {
0116 return true;
0117 }
0118 return false;
0119 }
0120
0121 /**
0122 * Return the rectangle (0,0,bounds.width,bounds.height) for the component <code>aComponent</code>
0123 */
0124 public static Rectangle getLocalBounds(Component aComponent) {
0125 Rectangle b = new Rectangle(aComponent.getBounds());
0126 b.x = b.y = 0;
0127 return b;
0128 }
0129
0130 /**
0131 * Returns the first <code>Window </code> ancestor of <code>c</code>, or
0132 * {@code null} if <code>c</code> is not contained inside a <code>Window</code>.
0133 *
0134 * @param c <code>Component</code> to get <code>Window</code> ancestor
0135 * of.
0136 * @return the first <code>Window </code> ancestor of <code>c</code>, or
0137 * {@code null} if <code>c</code> is not contained inside a
0138 * <code>Window</code>.
0139 * @since 1.3
0140 */
0141 public static Window getWindowAncestor(Component c) {
0142 for (Container p = c.getParent(); p != null; p = p.getParent()) {
0143 if (p instanceof Window) {
0144 return (Window) p;
0145 }
0146 }
0147 return null;
0148 }
0149
0150 /**
0151 * Converts the location <code>x</code> <code>y</code> to the
0152 * parents coordinate system, returning the location.
0153 */
0154 static Point convertScreenLocationToParent(Container parent, int x,
0155 int y) {
0156 for (Container p = parent; p != null; p = p.getParent()) {
0157 if (p instanceof Window) {
0158 Point point = new Point(x, y);
0159
0160 SwingUtilities.convertPointFromScreen(point, parent);
0161 return point;
0162 }
0163 }
0164 throw new Error(
0165 "convertScreenLocationToParent: no window ancestor");
0166 }
0167
0168 /**
0169 * Convert a <code>aPoint</code> in <code>source</code> coordinate system to
0170 * <code>destination</code> coordinate system.
0171 * If <code>source</code> is {@code null}, <code>aPoint</code> is assumed to be in <code>destination</code>'s
0172 * root component coordinate system.
0173 * If <code>destination</code> is {@code null}, <code>aPoint</code> will be converted to <code>source</code>'s
0174 * root component coordinate system.
0175 * If both <code>source</code> and <code>destination</code> are {@code null}, return <code>aPoint</code>
0176 * without any conversion.
0177 */
0178 public static Point convertPoint(Component source, Point aPoint,
0179 Component destination) {
0180 Point p;
0181
0182 if (source == null && destination == null)
0183 return aPoint;
0184 if (source == null) {
0185 source = getWindowAncestor(destination);
0186 if (source == null)
0187 throw new Error(
0188 "Source component not connected to component tree hierarchy");
0189 }
0190 p = new Point(aPoint);
0191 convertPointToScreen(p, source);
0192 if (destination == null) {
0193 destination = getWindowAncestor(source);
0194 if (destination == null)
0195 throw new Error(
0196 "Destination component not connected to component tree hierarchy");
0197 }
0198 convertPointFromScreen(p, destination);
0199 return p;
0200 }
0201
0202 /**
0203 * Convert the point <code>(x,y)</code> in <code>source</code> coordinate system to
0204 * <code>destination</code> coordinate system.
0205 * If <code>source</code> is {@code null}, <code>(x,y)</code> is assumed to be in <code>destination</code>'s
0206 * root component coordinate system.
0207 * If <code>destination</code> is {@code null}, <code>(x,y)</code> will be converted to <code>source</code>'s
0208 * root component coordinate system.
0209 * If both <code>source</code> and <code>destination</code> are {@code null}, return <code>(x,y)</code>
0210 * without any conversion.
0211 */
0212 public static Point convertPoint(Component source, int x, int y,
0213 Component destination) {
0214 Point point = new Point(x, y);
0215 return convertPoint(source, point, destination);
0216 }
0217
0218 /**
0219 * Convert the rectangle <code>aRectangle</code> in <code>source</code> coordinate system to
0220 * <code>destination</code> coordinate system.
0221 * If <code>source</code> is {@code null}, <code>aRectangle</code> is assumed to be in <code>destination</code>'s
0222 * root component coordinate system.
0223 * If <code>destination</code> is {@code null}, <code>aRectangle</code> will be converted to <code>source</code>'s
0224 * root component coordinate system.
0225 * If both <code>source</code> and <code>destination</code> are {@code null}, return <code>aRectangle</code>
0226 * without any conversion.
0227 */
0228 public static Rectangle convertRectangle(Component source,
0229 Rectangle aRectangle, Component destination) {
0230 Point point = new Point(aRectangle.x, aRectangle.y);
0231 point = convertPoint(source, point, destination);
0232 return new Rectangle(point.x, point.y, aRectangle.width,
0233 aRectangle.height);
0234 }
0235
0236 /**
0237 * Convenience method for searching above <code>comp</code> in the
0238 * component hierarchy and returns the first object of class <code>c</code> it
0239 * finds. Can return {@code null}, if a class <code>c</code> cannot be found.
0240 */
0241 public static Container getAncestorOfClass(Class<?> c,
0242 Component comp) {
0243 if (comp == null || c == null)
0244 return null;
0245
0246 Container parent = comp.getParent();
0247 while (parent != null && !(c.isInstance(parent)))
0248 parent = parent.getParent();
0249 return parent;
0250 }
0251
0252 /**
0253 * Convenience method for searching above <code>comp</code> in the
0254 * component hierarchy and returns the first object of <code>name</code> it
0255 * finds. Can return {@code null}, if <code>name</code> cannot be found.
0256 */
0257 public static Container getAncestorNamed(String name, Component comp) {
0258 if (comp == null || name == null)
0259 return null;
0260
0261 Container parent = comp.getParent();
0262 while (parent != null && !(name.equals(parent.getName())))
0263 parent = parent.getParent();
0264 return parent;
0265 }
0266
0267 /**
0268 * Returns the deepest visible descendent Component of <code>parent</code>
0269 * that contains the location <code>x</code>, <code>y</code>.
0270 * If <code>parent</code> does not contain the specified location,
0271 * then <code>null</code> is returned. If <code>parent</code> is not a
0272 * container, or none of <code>parent</code>'s visible descendents
0273 * contain the specified location, <code>parent</code> is returned.
0274 *
0275 * @param parent the root component to begin the search
0276 * @param x the x target location
0277 * @param y the y target location
0278 */
0279 public static Component getDeepestComponentAt(Component parent,
0280 int x, int y) {
0281 if (!parent.contains(x, y)) {
0282 return null;
0283 }
0284 if (parent instanceof Container) {
0285 Component components[] = ((Container) parent)
0286 .getComponents();
0287 for (int i = 0; i < components.length; i++) {
0288 Component comp = components[i];
0289 if (comp != null && comp.isVisible()) {
0290 Point loc = comp.getLocation();
0291 if (comp instanceof Container) {
0292 comp = getDeepestComponentAt(comp, x - loc.x, y
0293 - loc.y);
0294 } else {
0295 comp = comp
0296 .getComponentAt(x - loc.x, y - loc.y);
0297 }
0298 if (comp != null && comp.isVisible()) {
0299 return comp;
0300 }
0301 }
0302 }
0303 }
0304 return parent;
0305 }
0306
0307 /**
0308 * Returns a MouseEvent similar to <code>sourceEvent</code> except that its x
0309 * and y members have been converted to <code>destination</code>'s coordinate
0310 * system. If <code>source</code> is {@code null}, <code>sourceEvent</code> x and y members
0311 * are assumed to be into <code>destination</code>'s root component coordinate system.
0312 * If <code>destination</code> is <code>null</code>, the
0313 * returned MouseEvent will be in <code>source</code>'s coordinate system.
0314 * <code>sourceEvent</code> will not be changed. A new event is returned.
0315 * the <code>source</code> field of the returned event will be set
0316 * to <code>destination</code> if destination is non-{@code null}
0317 * use the translateMouseEvent() method to translate a mouse event from
0318 * one component to another without changing the source.
0319 */
0320 public static MouseEvent convertMouseEvent(Component source,
0321 MouseEvent sourceEvent, Component destination) {
0322 Point p = convertPoint(source, new Point(sourceEvent.getX(),
0323 sourceEvent.getY()), destination);
0324 Component newSource;
0325
0326 if (destination != null)
0327 newSource = destination;
0328 else
0329 newSource = source;
0330
0331 MouseEvent newEvent;
0332 if (sourceEvent instanceof MouseWheelEvent) {
0333 MouseWheelEvent sourceWheelEvent = (MouseWheelEvent) sourceEvent;
0334 newEvent = new MouseWheelEvent(newSource, sourceWheelEvent
0335 .getID(), sourceWheelEvent.getWhen(),
0336 sourceWheelEvent.getModifiers(), p.x, p.y,
0337 sourceWheelEvent.getXOnScreen(), sourceWheelEvent
0338 .getYOnScreen(), sourceWheelEvent
0339 .getClickCount(), sourceWheelEvent
0340 .isPopupTrigger(), sourceWheelEvent
0341 .getScrollType(), sourceWheelEvent
0342 .getScrollAmount(), sourceWheelEvent
0343 .getWheelRotation());
0344 } else if (sourceEvent instanceof MenuDragMouseEvent) {
0345 MenuDragMouseEvent sourceMenuDragEvent = (MenuDragMouseEvent) sourceEvent;
0346 newEvent = new MenuDragMouseEvent(newSource,
0347 sourceMenuDragEvent.getID(), sourceMenuDragEvent
0348 .getWhen(), sourceMenuDragEvent
0349 .getModifiers(), p.x, p.y,
0350 sourceMenuDragEvent.getXOnScreen(),
0351 sourceMenuDragEvent.getYOnScreen(),
0352 sourceMenuDragEvent.getClickCount(),
0353 sourceMenuDragEvent.isPopupTrigger(),
0354 sourceMenuDragEvent.getPath(), sourceMenuDragEvent
0355 .getMenuSelectionManager());
0356 } else {
0357 newEvent = new MouseEvent(newSource, sourceEvent.getID(),
0358 sourceEvent.getWhen(), sourceEvent.getModifiers(),
0359 p.x, p.y, sourceEvent.getXOnScreen(), sourceEvent
0360 .getYOnScreen(), sourceEvent
0361 .getClickCount(), sourceEvent
0362 .isPopupTrigger(), MouseEvent.NOBUTTON);
0363 }
0364 return newEvent;
0365 }
0366
0367 /**
0368 * Convert a point from a component's coordinate system to
0369 * screen coordinates.
0370 *
0371 * @param p a Point object (converted to the new coordinate system)
0372 * @param c a Component object
0373 */
0374 public static void convertPointToScreen(Point p, Component c) {
0375 Rectangle b;
0376 int x, y;
0377
0378 do {
0379 if (c instanceof JComponent) {
0380 x = ((JComponent) c).getX();
0381 y = ((JComponent) c).getY();
0382 } else if (c instanceof java.applet.Applet
0383 || c instanceof java.awt.Window) {
0384 try {
0385 Point pp = c.getLocationOnScreen();
0386 x = pp.x;
0387 y = pp.y;
0388 } catch (IllegalComponentStateException icse) {
0389 x = c.getX();
0390 y = c.getY();
0391 }
0392 } else {
0393 x = c.getX();
0394 y = c.getY();
0395 }
0396
0397 p.x += x;
0398 p.y += y;
0399
0400 if (c instanceof java.awt.Window
0401 || c instanceof java.applet.Applet)
0402 break;
0403 c = c.getParent();
0404 } while (c != null);
0405 }
0406
0407 /**
0408 * Convert a point from a screen coordinates to a component's
0409 * coordinate system
0410 *
0411 * @param p a Point object (converted to the new coordinate system)
0412 * @param c a Component object
0413 */
0414 public static void convertPointFromScreen(Point p, Component c) {
0415 Rectangle b;
0416 int x, y;
0417
0418 do {
0419 if (c instanceof JComponent) {
0420 x = ((JComponent) c).getX();
0421 y = ((JComponent) c).getY();
0422 } else if (c instanceof java.applet.Applet
0423 || c instanceof java.awt.Window) {
0424 try {
0425 Point pp = c.getLocationOnScreen();
0426 x = pp.x;
0427 y = pp.y;
0428 } catch (IllegalComponentStateException icse) {
0429 x = c.getX();
0430 y = c.getY();
0431 }
0432 } else {
0433 x = c.getX();
0434 y = c.getY();
0435 }
0436
0437 p.x -= x;
0438 p.y -= y;
0439
0440 if (c instanceof java.awt.Window
0441 || c instanceof java.applet.Applet)
0442 break;
0443 c = c.getParent();
0444 } while (c != null);
0445 }
0446
0447 /**
0448 * Returns the first <code>Window </code> ancestor of <code>c</code>, or
0449 * {@code null} if <code>c</code> is not contained inside a <code>Window</code>.
0450 * <p>
0451 * Note: This method provides the same functionality as
0452 * <code>getWindowAncestor</code>.
0453 *
0454 * @param c <code>Component</code> to get <code>Window</code> ancestor
0455 * of.
0456 * @return the first <code>Window </code> ancestor of <code>c</code>, or
0457 * {@code null} if <code>c</code> is not contained inside a
0458 * <code>Window</code>.
0459 */
0460 public static Window windowForComponent(Component c) {
0461 return getWindowAncestor(c);
0462 }
0463
0464 /**
0465 * Return <code>true</code> if a component <code>a</code> descends from a component <code>b</code>
0466 */
0467 public static boolean isDescendingFrom(Component a, Component b) {
0468 if (a == b)
0469 return true;
0470 for (Container p = a.getParent(); p != null; p = p.getParent())
0471 if (p == b)
0472 return true;
0473 return false;
0474 }
0475
0476 /**
0477 * Convenience to calculate the intersection of two rectangles
0478 * without allocating a new rectangle.
0479 * If the two rectangles don't intersect,
0480 * then the returned rectangle begins at (0,0)
0481 * and has zero width and height.
0482 *
0483 * @param x the X coordinate of the first rectangle's top-left point
0484 * @param y the Y coordinate of the first rectangle's top-left point
0485 * @param width the width of the first rectangle
0486 * @param height the height of the first rectangle
0487 * @param dest the second rectangle
0488 *
0489 * @return <code>dest</code>, modified to specify the intersection
0490 */
0491 public static Rectangle computeIntersection(int x, int y,
0492 int width, int height, Rectangle dest) {
0493 int x1 = (x > dest.x) ? x : dest.x;
0494 int x2 = ((x + width) < (dest.x + dest.width)) ? (x + width)
0495 : (dest.x + dest.width);
0496 int y1 = (y > dest.y) ? y : dest.y;
0497 int y2 = ((y + height) < (dest.y + dest.height) ? (y + height)
0498 : (dest.y + dest.height));
0499
0500 dest.x = x1;
0501 dest.y = y1;
0502 dest.width = x2 - x1;
0503 dest.height = y2 - y1;
0504
0505 // If rectangles don't intersect, return zero'd intersection.
0506 if (dest.width < 0 || dest.height < 0) {
0507 dest.x = dest.y = dest.width = dest.height = 0;
0508 }
0509
0510 return dest;
0511 }
0512
0513 /**
0514 * Convenience method that calculates the union of two rectangles
0515 * without allocating a new rectangle.
0516 *
0517 * @param x the x-coordinate of the first rectangle
0518 * @param y the y-coordinate of the first rectangle
0519 * @param width the width of the first rectangle
0520 * @param height the height of the first rectangle
0521 * @param dest the coordinates of the second rectangle; the union
0522 * of the two rectangles is returned in this rectangle
0523 * @return the <code>dest</code> <code>Rectangle</code>
0524 */
0525 public static Rectangle computeUnion(int x, int y, int width,
0526 int height, Rectangle dest) {
0527 int x1 = (x < dest.x) ? x : dest.x;
0528 int x2 = ((x + width) > (dest.x + dest.width)) ? (x + width)
0529 : (dest.x + dest.width);
0530 int y1 = (y < dest.y) ? y : dest.y;
0531 int y2 = ((y + height) > (dest.y + dest.height)) ? (y + height)
0532 : (dest.y + dest.height);
0533
0534 dest.x = x1;
0535 dest.y = y1;
0536 dest.width = (x2 - x1);
0537 dest.height = (y2 - y1);
0538 return dest;
0539 }
0540
0541 /**
0542 * Convenience returning an array of rect representing the regions within
0543 * <code>rectA</code> that do not overlap with <code>rectB</code>. If the
0544 * two Rects do not overlap, returns an empty array
0545 */
0546 public static Rectangle[] computeDifference(Rectangle rectA,
0547 Rectangle rectB) {
0548 if (rectB == null || !rectA.intersects(rectB)
0549 || isRectangleContainingRectangle(rectB, rectA)) {
0550 return new Rectangle[0];
0551 }
0552
0553 Rectangle t = new Rectangle();
0554 Rectangle a = null, b = null, c = null, d = null;
0555 Rectangle result[];
0556 int rectCount = 0;
0557
0558 /* rectA contains rectB */
0559 if (isRectangleContainingRectangle(rectA, rectB)) {
0560 t.x = rectA.x;
0561 t.y = rectA.y;
0562 t.width = rectB.x - rectA.x;
0563 t.height = rectA.height;
0564 if (t.width > 0 && t.height > 0) {
0565 a = new Rectangle(t);
0566 rectCount++;
0567 }
0568
0569 t.x = rectB.x;
0570 t.y = rectA.y;
0571 t.width = rectB.width;
0572 t.height = rectB.y - rectA.y;
0573 if (t.width > 0 && t.height > 0) {
0574 b = new Rectangle(t);
0575 rectCount++;
0576 }
0577
0578 t.x = rectB.x;
0579 t.y = rectB.y + rectB.height;
0580 t.width = rectB.width;
0581 t.height = rectA.y + rectA.height
0582 - (rectB.y + rectB.height);
0583 if (t.width > 0 && t.height > 0) {
0584 c = new Rectangle(t);
0585 rectCount++;
0586 }
0587
0588 t.x = rectB.x + rectB.width;
0589 t.y = rectA.y;
0590 t.width = rectA.x + rectA.width - (rectB.x + rectB.width);
0591 t.height = rectA.height;
0592 if (t.width > 0 && t.height > 0) {
0593 d = new Rectangle(t);
0594 rectCount++;
0595 }
0596 } else {
0597 /* 1 */
0598 if (rectB.x <= rectA.x && rectB.y <= rectA.y) {
0599 if ((rectB.x + rectB.width) > (rectA.x + rectA.width)) {
0600
0601 t.x = rectA.x;
0602 t.y = rectB.y + rectB.height;
0603 t.width = rectA.width;
0604 t.height = rectA.y + rectA.height
0605 - (rectB.y + rectB.height);
0606 if (t.width > 0 && t.height > 0) {
0607 a = t;
0608 rectCount++;
0609 }
0610 } else if ((rectB.y + rectB.height) > (rectA.y + rectA.height)) {
0611 t.setBounds((rectB.x + rectB.width), rectA.y,
0612 (rectA.x + rectA.width)
0613 - (rectB.x + rectB.width),
0614 rectA.height);
0615 if (t.width > 0 && t.height > 0) {
0616 a = t;
0617 rectCount++;
0618 }
0619 } else {
0620 t.setBounds((rectB.x + rectB.width), rectA.y,
0621 (rectA.x + rectA.width)
0622 - (rectB.x + rectB.width),
0623 (rectB.y + rectB.height) - rectA.y);
0624 if (t.width > 0 && t.height > 0) {
0625 a = new Rectangle(t);
0626 rectCount++;
0627 }
0628
0629 t.setBounds(rectA.x, (rectB.y + rectB.height),
0630 rectA.width, (rectA.y + rectA.height)
0631 - (rectB.y + rectB.height));
0632 if (t.width > 0 && t.height > 0) {
0633 b = new Rectangle(t);
0634 rectCount++;
0635 }
0636 }
0637 } else if (rectB.x <= rectA.x
0638 && (rectB.y + rectB.height) >= (rectA.y + rectA.height)) {
0639 if ((rectB.x + rectB.width) > (rectA.x + rectA.width)) {
0640 t.setBounds(rectA.x, rectA.y, rectA.width, rectB.y
0641 - rectA.y);
0642 if (t.width > 0 && t.height > 0) {
0643 a = t;
0644 rectCount++;
0645 }
0646 } else {
0647 t.setBounds(rectA.x, rectA.y, rectA.width, rectB.y
0648 - rectA.y);
0649 if (t.width > 0 && t.height > 0) {
0650 a = new Rectangle(t);
0651 rectCount++;
0652 }
0653 t.setBounds((rectB.x + rectB.width), rectB.y,
0654 (rectA.x + rectA.width)
0655 - (rectB.x + rectB.width),
0656 (rectA.y + rectA.height) - rectB.y);
0657 if (t.width > 0 && t.height > 0) {
0658 b = new Rectangle(t);
0659 rectCount++;
0660 }
0661 }
0662 } else if (rectB.x <= rectA.x) {
0663 if ((rectB.x + rectB.width) >= (rectA.x + rectA.width)) {
0664 t.setBounds(rectA.x, rectA.y, rectA.width, rectB.y
0665 - rectA.y);
0666 if (t.width > 0 && t.height > 0) {
0667 a = new Rectangle(t);
0668 rectCount++;
0669 }
0670
0671 t.setBounds(rectA.x, (rectB.y + rectB.height),
0672 rectA.width, (rectA.y + rectA.height)
0673 - (rectB.y + rectB.height));
0674 if (t.width > 0 && t.height > 0) {
0675 b = new Rectangle(t);
0676 rectCount++;
0677 }
0678 } else {
0679 t.setBounds(rectA.x, rectA.y, rectA.width, rectB.y
0680 - rectA.y);
0681 if (t.width > 0 && t.height > 0) {
0682 a = new Rectangle(t);
0683 rectCount++;
0684 }
0685
0686 t.setBounds((rectB.x + rectB.width), rectB.y,
0687 (rectA.x + rectA.width)
0688 - (rectB.x + rectB.width),
0689 rectB.height);
0690 if (t.width > 0 && t.height > 0) {
0691 b = new Rectangle(t);
0692 rectCount++;
0693 }
0694
0695 t.setBounds(rectA.x, (rectB.y + rectB.height),
0696 rectA.width, (rectA.y + rectA.height)
0697 - (rectB.y + rectB.height));
0698 if (t.width > 0 && t.height > 0) {
0699 c = new Rectangle(t);
0700 rectCount++;
0701 }
0702 }
0703 } else if (rectB.x <= (rectA.x + rectA.width)
0704 && (rectB.x + rectB.width) > (rectA.x + rectA.width)) {
0705 if (rectB.y <= rectA.y
0706 && (rectB.y + rectB.height) > (rectA.y + rectA.height)) {
0707 t.setBounds(rectA.x, rectA.y, rectB.x - rectA.x,
0708 rectA.height);
0709 if (t.width > 0 && t.height > 0) {
0710 a = t;
0711 rectCount++;
0712 }
0713 } else if (rectB.y <= rectA.y) {
0714 t.setBounds(rectA.x, rectA.y, rectB.x - rectA.x,
0715 (rectB.y + rectB.height) - rectA.y);
0716 if (t.width > 0 && t.height > 0) {
0717 a = new Rectangle(t);
0718 rectCount++;
0719 }
0720
0721 t.setBounds(rectA.x, (rectB.y + rectB.height),
0722 rectA.width, (rectA.y + rectA.height)
0723 - (rectB.y + rectB.height));
0724 if (t.width > 0 && t.height > 0) {
0725 b = new Rectangle(t);
0726 rectCount++;
0727 }
0728 } else if ((rectB.y + rectB.height) > (rectA.y + rectA.height)) {
0729 t.setBounds(rectA.x, rectA.y, rectA.width, rectB.y
0730 - rectA.y);
0731 if (t.width > 0 && t.height > 0) {
0732 a = new Rectangle(t);
0733 rectCount++;
0734 }
0735
0736 t.setBounds(rectA.x, rectB.y, rectB.x - rectA.x,
0737 (rectA.y + rectA.height) - rectB.y);
0738 if (t.width > 0 && t.height > 0) {
0739 b = new Rectangle(t);
0740 rectCount++;
0741 }
0742 } else {
0743 t.setBounds(rectA.x, rectA.y, rectA.width, rectB.y
0744 - rectA.y);
0745 if (t.width > 0 && t.height > 0) {
0746 a = new Rectangle(t);
0747 rectCount++;
0748 }
0749
0750 t.setBounds(rectA.x, rectB.y, rectB.x - rectA.x,
0751 rectB.height);
0752 if (t.width > 0 && t.height > 0) {
0753 b = new Rectangle(t);
0754 rectCount++;
0755 }
0756
0757 t.setBounds(rectA.x, (rectB.y + rectB.height),
0758 rectA.width, (rectA.y + rectA.height)
0759 - (rectB.y + rectB.height));
0760 if (t.width > 0 && t.height > 0) {
0761 c = new Rectangle(t);
0762 rectCount++;
0763 }
0764 }
0765 } else if (rectB.x >= rectA.x
0766 && (rectB.x + rectB.width) <= (rectA.x + rectA.width)) {
0767 if (rectB.y <= rectA.y
0768 && (rectB.y + rectB.height) > (rectA.y + rectA.height)) {
0769 t.setBounds(rectA.x, rectA.y, rectB.x - rectA.x,
0770 rectA.height);
0771 if (t.width > 0 && t.height > 0) {
0772 a = new Rectangle(t);
0773 rectCount++;
0774 }
0775 t.setBounds((rectB.x + rectB.width), rectA.y,
0776 (rectA.x + rectA.width)
0777 - (rectB.x + rectB.width),
0778 rectA.height);
0779 if (t.width > 0 && t.height > 0) {
0780 b = new Rectangle(t);
0781 rectCount++;
0782 }
0783 } else if (rectB.y <= rectA.y) {
0784 t.setBounds(rectA.x, rectA.y, rectB.x - rectA.x,
0785 rectA.height);
0786 if (t.width > 0 && t.height > 0) {
0787 a = new Rectangle(t);
0788 rectCount++;
0789 }
0790
0791 t.setBounds(rectB.x, (rectB.y + rectB.height),
0792 rectB.width, (rectA.y + rectA.height)
0793 - (rectB.y + rectB.height));
0794 if (t.width > 0 && t.height > 0) {
0795 b = new Rectangle(t);
0796 rectCount++;
0797 }
0798
0799 t.setBounds((rectB.x + rectB.width), rectA.y,
0800 (rectA.x + rectA.width)
0801 - (rectB.x + rectB.width),
0802 rectA.height);
0803 if (t.width > 0 && t.height > 0) {
0804 c = new Rectangle(t);
0805 rectCount++;
0806 }
0807 } else {
0808 t.setBounds(rectA.x, rectA.y, rectB.x - rectA.x,
0809 rectA.height);
0810 if (t.width > 0 && t.height > 0) {
0811 a = new Rectangle(t);
0812 rectCount++;
0813 }
0814
0815 t.setBounds(rectB.x, rectA.y, rectB.width, rectB.y
0816 - rectA.y);
0817 if (t.width > 0 && t.height > 0) {
0818 b = new Rectangle(t);
0819 rectCount++;
0820 }
0821
0822 t.setBounds((rectB.x + rectB.width), rectA.y,
0823 (rectA.x + rectA.width)
0824 - (rectB.x + rectB.width),
0825 rectA.height);
0826 if (t.width > 0 && t.height > 0) {
0827 c = new Rectangle(t);
0828 rectCount++;
0829 }
0830 }
0831 }
0832 }
0833
0834 result = new Rectangle[rectCount];
0835 rectCount = 0;
0836 if (a != null)
0837 result[rectCount++] = a;
0838 if (b != null)
0839 result[rectCount++] = b;
0840 if (c != null)
0841 result[rectCount++] = c;
0842 if (d != null)
0843 result[rectCount++] = d;
0844 return result;
0845 }
0846
0847 /**
0848 * Returns true if the mouse event specifies the left mouse button.
0849 *
0850 * @param anEvent a MouseEvent object
0851 * @return true if the left mouse button was active
0852 */
0853 public static boolean isLeftMouseButton(MouseEvent anEvent) {
0854 return ((anEvent.getModifiers() & InputEvent.BUTTON1_MASK) != 0);
0855 }
0856
0857 /**
0858 * Returns true if the mouse event specifies the middle mouse button.
0859 *
0860 * @param anEvent a MouseEvent object
0861 * @return true if the middle mouse button was active
0862 */
0863 public static boolean isMiddleMouseButton(MouseEvent anEvent) {
0864 return ((anEvent.getModifiers() & InputEvent.BUTTON2_MASK) == InputEvent.BUTTON2_MASK);
0865 }
0866
0867 /**
0868 * Returns true if the mouse event specifies the right mouse button.
0869 *
0870 * @param anEvent a MouseEvent object
0871 * @return true if the right mouse button was active
0872 */
0873 public static boolean isRightMouseButton(MouseEvent anEvent) {
0874 return ((anEvent.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK);
0875 }
0876
0877 /**
0878 * Compute the width of the string using a font with the specified
0879 * "metrics" (sizes).
0880 *
0881 * @param fm a FontMetrics object to compute with
0882 * @param str the String to compute
0883 * @return an int containing the string width
0884 */
0885 public static int computeStringWidth(FontMetrics fm, String str) {
0886 // You can't assume that a string's width is the sum of its
0887 // characters' widths in Java2D -- it may be smaller due to
0888 // kerning, etc.
0889 return SwingUtilities2.stringWidth(null, fm, str);
0890 }
0891
0892 /**
0893 * Compute and return the location of the icons origin, the
0894 * location of origin of the text baseline, and a possibly clipped
0895 * version of the compound labels string. Locations are computed
0896 * relative to the viewR rectangle.
0897 * The JComponents orientation (LEADING/TRAILING) will also be taken
0898 * into account and translated into LEFT/RIGHT values accordingly.
0899 */
0900 public static String layoutCompoundLabel(JComponent c,
0901 FontMetrics fm, String text, Icon icon,
0902 int verticalAlignment, int horizontalAlignment,
0903 int verticalTextPosition, int horizontalTextPosition,
0904 Rectangle viewR, Rectangle iconR, Rectangle textR,
0905 int textIconGap) {
0906 boolean orientationIsLeftToRight = true;
0907 int hAlign = horizontalAlignment;
0908 int hTextPos = horizontalTextPosition;
0909
0910 if (c != null) {
0911 if (!(c.getComponentOrientation().isLeftToRight())) {
0912 orientationIsLeftToRight = false;
0913 }
0914 }
0915
0916 // Translate LEADING/TRAILING values in horizontalAlignment
0917 // to LEFT/RIGHT values depending on the components orientation
0918 switch (horizontalAlignment) {
0919 case LEADING:
0920 hAlign = (orientationIsLeftToRight) ? LEFT : RIGHT;
0921 break;
0922 case TRAILING:
0923 hAlign = (orientationIsLeftToRight) ? RIGHT : LEFT;
0924 break;
0925 }
0926
0927 // Translate LEADING/TRAILING values in horizontalTextPosition
0928 // to LEFT/RIGHT values depending on the components orientation
0929 switch (horizontalTextPosition) {
0930 case LEADING:
0931 hTextPos = (orientationIsLeftToRight) ? LEFT : RIGHT;
0932 break;
0933 case TRAILING:
0934 hTextPos = (orientationIsLeftToRight) ? RIGHT : LEFT;
0935 break;
0936 }
0937
0938 return layoutCompoundLabelImpl(c, fm, text, icon,
0939 verticalAlignment, hAlign, verticalTextPosition,
0940 hTextPos, viewR, iconR, textR, textIconGap);
0941 }
0942
0943 /**
0944 * Compute and return the location of the icons origin, the
0945 * location of origin of the text baseline, and a possibly clipped
0946 * version of the compound labels string. Locations are computed
0947 * relative to the viewR rectangle.
0948 * This layoutCompoundLabel() does not know how to handle LEADING/TRAILING
0949 * values in horizontalTextPosition (they will default to RIGHT) and in
0950 * horizontalAlignment (they will default to CENTER).
0951 * Use the other version of layoutCompoundLabel() instead.
0952 */
0953 public static String layoutCompoundLabel(FontMetrics fm,
0954 String text, Icon icon, int verticalAlignment,
0955 int horizontalAlignment, int verticalTextPosition,
0956 int horizontalTextPosition, Rectangle viewR,
0957 Rectangle iconR, Rectangle textR, int textIconGap) {
0958 return layoutCompoundLabelImpl(null, fm, text, icon,
0959 verticalAlignment, horizontalAlignment,
0960 verticalTextPosition, horizontalTextPosition, viewR,
0961 iconR, textR, textIconGap);
0962 }
0963
0964 /**
0965 * Compute and return the location of the icons origin, the
0966 * location of origin of the text baseline, and a possibly clipped
0967 * version of the compound labels string. Locations are computed
0968 * relative to the viewR rectangle.
0969 * This layoutCompoundLabel() does not know how to handle LEADING/TRAILING
0970 * values in horizontalTextPosition (they will default to RIGHT) and in
0971 * horizontalAlignment (they will default to CENTER).
0972 * Use the other version of layoutCompoundLabel() instead.
0973 */
0974 private static String layoutCompoundLabelImpl(JComponent c,
0975 FontMetrics fm, String text, Icon icon,
0976 int verticalAlignment, int horizontalAlignment,
0977 int verticalTextPosition, int horizontalTextPosition,
0978 Rectangle viewR, Rectangle iconR, Rectangle textR,
0979 int textIconGap) {
0980 /* Initialize the icon bounds rectangle iconR.
0981 */
0982
0983 if (icon != null) {
0984 iconR.width = icon.getIconWidth();
0985 iconR.height = icon.getIconHeight();
0986 } else {
0987 iconR.width = iconR.height = 0;
0988 }
0989
0990 /* Initialize the text bounds rectangle textR. If a null
0991 * or and empty String was specified we substitute "" here
0992 * and use 0,0,0,0 for textR.
0993 */
0994
0995 boolean textIsEmpty = (text == null) || text.equals("");
0996 int lsb = 0;
0997 /* Unless both text and icon are non-null, we effectively ignore
0998 * the value of textIconGap.
0999 */
1000 int gap;
1001
1002 View v = null;
1003 if (textIsEmpty) {
1004 textR.width = textR.height = 0;
1005 text = "";
1006 gap = 0;
1007 } else {
1008 int availTextWidth;
1009 gap = (icon == null) ? 0 : textIconGap;
1010
1011 if (horizontalTextPosition == CENTER) {
1012 availTextWidth = viewR.width;
1013 } else {
1014 availTextWidth = viewR.width - (iconR.width + gap);
1015 }
1016 v = (c != null) ? (View) c.getClientProperty("html") : null;
1017 if (v != null) {
1018 textR.width = Math.min(availTextWidth, (int) v
1019 .getPreferredSpan(View.X_AXIS));
1020 textR.height = (int) v.getPreferredSpan(View.Y_AXIS);
1021 } else {
1022 textR.width = SwingUtilities2.stringWidth(c, fm, text);
1023
1024 // Take into account the left and right side bearings.
1025 // This gives more space than it is actually needed,
1026 // but there are two reasons:
1027 // 1. If we set the width to the actual bounds,
1028 // all callers would have to account for the bearings
1029 // themselves. NOTE: all pref size calculations don't do it.
1030 // 2. You can do a drawString at the returned location
1031 // and the text won't be clipped.
1032 lsb = SwingUtilities2.getLeftSideBearing(c, fm, text);
1033 if (lsb < 0) {
1034 textR.width -= lsb;
1035 }
1036 int rsb = SwingUtilities2.getRightSideBearing(c, fm,
1037 text);
1038 if (rsb > 0) {
1039 textR.width += rsb;
1040 }
1041
1042 if (textR.width > availTextWidth) {
1043 text = SwingUtilities2.clipString(c, fm, text,
1044 availTextWidth);
1045 textR.width = SwingUtilities2.stringWidth(c, fm,
1046 text);
1047 }
1048 textR.height = fm.getHeight();
1049 }
1050 }
1051
1052 /* Compute textR.x,y given the verticalTextPosition and
1053 * horizontalTextPosition properties
1054 */
1055
1056 if (verticalTextPosition == TOP) {
1057 if (horizontalTextPosition != CENTER) {
1058 textR.y = 0;
1059 } else {
1060 textR.y = -(textR.height + gap);
1061 }
1062 } else if (verticalTextPosition == CENTER) {
1063 textR.y = (iconR.height / 2) - (textR.height / 2);
1064 } else { // (verticalTextPosition == BOTTOM)
1065 if (horizontalTextPosition != CENTER) {
1066 textR.y = iconR.height - textR.height;
1067 } else {
1068 textR.y = (iconR.height + gap);
1069 }
1070 }
1071
1072 if (horizontalTextPosition == LEFT) {
1073 textR.x = -(textR.width + gap);
1074 } else if (horizontalTextPosition == CENTER) {
1075 textR.x = (iconR.width / 2) - (textR.width / 2);
1076 } else { // (horizontalTextPosition == RIGHT)
1077 textR.x = (iconR.width + gap);
1078 }
1079
1080 // WARNING: DefaultTreeCellEditor uses a shortened version of
1081 // this algorithm to position it's Icon. If you change how this
1082 // is calculated, be sure and update DefaultTreeCellEditor too.
1083
1084 /* labelR is the rectangle that contains iconR and textR.
1085 * Move it to its proper position given the labelAlignment
1086 * properties.
1087 *
1088 * To avoid actually allocating a Rectangle, Rectangle.union
1089 * has been inlined below.
1090 */
1091 int labelR_x = Math.min(iconR.x, textR.x);
1092 int labelR_width = Math.max(iconR.x + iconR.width, textR.x
1093 + textR.width)
1094 - labelR_x;
1095 int labelR_y = Math.min(iconR.y, textR.y);
1096 int labelR_height = Math.max(iconR.y + iconR.height, textR.y
1097 + textR.height)
1098 - labelR_y;
1099
1100 int dx, dy;
1101
1102 if (verticalAlignment == TOP) {
1103 dy = viewR.y - labelR_y;
1104 } else if (verticalAlignment == CENTER) {
1105 dy = (viewR.y + (viewR.height / 2))
1106 - (labelR_y + (labelR_height / 2));
1107 } else { // (verticalAlignment == BOTTOM)
1108 dy = (viewR.y + viewR.height) - (labelR_y + labelR_height);
1109 }
1110
1111 if (horizontalAlignment == LEFT) {
1112 dx = viewR.x - labelR_x;
1113 } else if (horizontalAlignment == RIGHT) {
1114 dx = (viewR.x + viewR.width) - (labelR_x + labelR_width);
1115 } else { // (horizontalAlignment == CENTER)
1116 dx = (viewR.x + (viewR.width / 2))
1117 - (labelR_x + (labelR_width / 2));
1118 }
1119
1120 /* Translate textR and glypyR by dx,dy.
1121 */
1122
1123 textR.x += dx;
1124 textR.y += dy;
1125
1126 iconR.x += dx;
1127 iconR.y += dy;
1128
1129 if (lsb < 0) {
1130 // lsb is negative. Shift the x location so that the text is
1131 // visually drawn at the right location.
1132 textR.x -= lsb;
1133 }
1134
1135 return text;
1136 }
1137
1138 /**
1139 * Paints a component to the specified <code>Graphics</code>.
1140 * This method is primarily useful to render
1141 * <code>Component</code>s that don't exist as part of the visible
1142 * containment hierarchy, but are used for rendering. For
1143 * example, if you are doing your own rendering and want to render
1144 * some text (or even HTML), you could make use of
1145 * <code>JLabel</code>'s text rendering support and have it paint
1146 * directly by way of this method, without adding the label to the
1147 * visible containment hierarchy.
1148 * <p>
1149 * This method makes use of <code>CellRendererPane</code> to handle
1150 * the actual painting, and is only recommended if you use one
1151 * component for rendering. If you make use of multiple components
1152 * to handle the rendering, as <code>JTable</code> does, use
1153 * <code>CellRendererPane</code> directly. Otherwise, as described
1154 * below, you could end up with a <code>CellRendererPane</code>
1155 * per <code>Component</code>.
1156 * <p>
1157 * If <code>c</code>'s parent is not a <code>CellRendererPane</code>,
1158 * a new <code>CellRendererPane</code> is created, <code>c</code> is
1159 * added to it, and the <code>CellRendererPane</code> is added to
1160 * <code>p</code>. If <code>c</code>'s parent is a
1161 * <code>CellRendererPane</code> and the <code>CellRendererPane</code>s
1162 * parent is not <code>p</code>, it is added to <code>p</code>.
1163 * <p>
1164 * The component should either descend from <code>JComponent</code>
1165 * or be another kind of lightweight component.
1166 * A lightweight component is one whose "lightweight" property
1167 * (returned by the <code>Component</code>
1168 * <code>isLightweight</code> method)
1169 * is true. If the Component is not lightweight, bad things map happen:
1170 * crashes, exceptions, painting problems...
1171 *
1172 * @param g the <code>Graphics</code> object to draw on
1173 * @param c the <code>Component</code> to draw
1174 * @param p the intermediate <code>Container</code>
1175 * @param x an int specifying the left side of the area draw in, in pixels,
1176 * measured from the left edge of the graphics context
1177 * @param y an int specifying the top of the area to draw in, in pixels
1178 * measured down from the top edge of the graphics context
1179 * @param w an int specifying the width of the area draw in, in pixels
1180 * @param h an int specifying the height of the area draw in, in pixels
1181 *
1182 * @see CellRendererPane
1183 * @see java.awt.Component#isLightweight
1184 */
1185 public static void paintComponent(Graphics g, Component c,
1186 Container p, int x, int y, int w, int h) {
1187 getCellRendererPane(c, p).paintComponent(g, c, p, x, y, w, h,
1188 false);
1189 }
1190
1191 /**
1192 * Paints a component to the specified <code>Graphics</code>. This
1193 * is a cover method for
1194 * {@link #paintComponent(Graphics,Component,Container,int,int,int,int)}.
1195 * Refer to it for more information.
1196 *
1197 * @param g the <code>Graphics</code> object to draw on
1198 * @param c the <code>Component</code> to draw
1199 * @param p the intermediate <code>Container</code>
1200 * @param r the <code>Rectangle</code> to draw in
1201 *
1202 * @see #paintComponent(Graphics,Component,Container,int,int,int,int)
1203 * @see CellRendererPane
1204 */
1205 public static void paintComponent(Graphics g, Component c,
1206 Container p, Rectangle r) {
1207 paintComponent(g, c, p, r.x, r.y, r.width, r.height);
1208 }
1209
1210 /*
1211 * Ensures that cell renderer <code>c</code> has a
1212 * <code>ComponentShell</code> parent and that
1213 * the shell's parent is p.
1214 */
1215 private static CellRendererPane getCellRendererPane(Component c,
1216 Container p) {
1217 Container shell = c.getParent();
1218 if (shell instanceof CellRendererPane) {
1219 if (shell.getParent() != p) {
1220 p.add(shell);
1221 }
1222 } else {
1223 shell = new CellRendererPane();
1224 shell.add(c);
1225 p.add(shell);
1226 }
1227 return (CellRendererPane) shell;
1228 }
1229
1230 /**
1231 * A simple minded look and feel change: ask each node in the tree
1232 * to <code>updateUI()</code> -- that is, to initialize its UI property
1233 * with the current look and feel.
1234 */
1235 public static void updateComponentTreeUI(Component c) {
1236 updateComponentTreeUI0(c);
1237 c.invalidate();
1238 c.validate();
1239 c.repaint();
1240 }
1241
1242 private static void updateComponentTreeUI0(Component c) {
1243 if (c instanceof JComponent) {
1244 JComponent jc = (JComponent) c;
1245 jc.updateUI();
1246 JPopupMenu jpm = jc.getComponentPopupMenu();
1247 if (jpm != null) {
1248 updateComponentTreeUI(jpm);
1249 }
1250 }
1251 Component[] children = null;
1252 if (c instanceof JMenu) {
1253 children = ((JMenu) c).getMenuComponents();
1254 } else if (c instanceof Container) {
1255 children = ((Container) c).getComponents();
1256 }
1257 if (children != null) {
1258 for (int i = 0; i < children.length; i++) {
1259 updateComponentTreeUI0(children[i]);
1260 }
1261 }
1262 }
1263
1264 /**
1265 * Causes <i>doRun.run()</i> to be executed asynchronously on the
1266 * AWT event dispatching thread. This will happen after all
1267 * pending AWT events have been processed. This method should
1268 * be used when an application thread needs to update the GUI.
1269 * In the following example the <code>invokeLater</code> call queues
1270 * the <code>Runnable</code> object <code>doHelloWorld</code>
1271 * on the event dispatching thread and
1272 * then prints a message.
1273 * <pre>
1274 * Runnable doHelloWorld = new Runnable() {
1275 * public void run() {
1276 * System.out.println("Hello World on " + Thread.currentThread());
1277 * }
1278 * };
1279 *
1280 * SwingUtilities.invokeLater(doHelloWorld);
1281 * System.out.println("This might well be displayed before the other message.");
1282 * </pre>
1283 * If invokeLater is called from the event dispatching thread --
1284 * for example, from a JButton's ActionListener -- the <i>doRun.run()</i> will
1285 * still be deferred until all pending events have been processed.
1286 * Note that if the <i>doRun.run()</i> throws an uncaught exception
1287 * the event dispatching thread will unwind (not the current thread).
1288 * <p>
1289 * Additional documentation and examples for this method can be
1290 * found in
1291 * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How to Use Threads</a>,
1292 * in <em>The Java Tutorial</em>.
1293 * <p>
1294 * As of 1.3 this method is just a cover for <code>java.awt.EventQueue.invokeLater()</code>.
1295 * <p>
1296 * Unlike the rest of Swing, this method can be invoked from any thread.
1297 *
1298 * @see #invokeAndWait
1299 */
1300 public static void invokeLater(Runnable doRun) {
1301 EventQueue.invokeLater(doRun);
1302 }
1303
1304 /**
1305 * Causes <code>doRun.run()</code> to be executed synchronously on the
1306 * AWT event dispatching thread. This call blocks until
1307 * all pending AWT events have been processed and (then)
1308 * <code>doRun.run()</code> returns. This method should
1309 * be used when an application thread needs to update the GUI.
1310 * It shouldn't be called from the event dispatching thread.
1311 * Here's an example that creates a new application thread
1312 * that uses <code>invokeAndWait</code> to print a string from the event
1313 * dispatching thread and then, when that's finished, print
1314 * a string from the application thread.
1315 * <pre>
1316 * final Runnable doHelloWorld = new Runnable() {
1317 * public void run() {
1318 * System.out.println("Hello World on " + Thread.currentThread());
1319 * }
1320 * };
1321 *
1322 * Thread appThread = new Thread() {
1323 * public void run() {
1324 * try {
1325 * SwingUtilities.invokeAndWait(doHelloWorld);
1326 * }
1327 * catch (Exception e) {
1328 * e.printStackTrace();
1329 * }
1330 * System.out.println("Finished on " + Thread.currentThread());
1331 * }
1332 * };
1333 * appThread.start();
1334 * </pre>
1335 * Note that if the <code>Runnable.run</code> method throws an
1336 * uncaught exception
1337 * (on the event dispatching thread) it's caught and rethrown, as
1338 * an <code>InvocationTargetException</code>, on the caller's thread.
1339 * <p>
1340 * Additional documentation and examples for this method can be
1341 * found in
1342 * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How to Use Threads</a>,
1343 * in <em>The Java Tutorial</em>.
1344 * <p>
1345 * As of 1.3 this method is just a cover for
1346 * <code>java.awt.EventQueue.invokeAndWait()</code>.
1347 *
1348 * @exception InterruptedException if we're interrupted while waiting for
1349 * the event dispatching thread to finish excecuting
1350 * <code>doRun.run()</code>
1351 * @exception InvocationTargetException if an exception is thrown
1352 * while running <code>doRun</code>
1353 *
1354 * @see #invokeLater
1355 */
1356 public static void invokeAndWait(final Runnable doRun)
1357 throws InterruptedException, InvocationTargetException {
1358 EventQueue.invokeAndWait(doRun);
1359 }
1360
1361 /**
1362 * Returns true if the current thread is an AWT event dispatching thread.
1363 * <p>
1364 * As of 1.3 this method is just a cover for
1365 * <code>java.awt.EventQueue.isDispatchThread()</code>.
1366 *
1367 * @return true if the current thread is an AWT event dispatching thread
1368 */
1369 public static boolean isEventDispatchThread() {
1370 return EventQueue.isDispatchThread();
1371 }
1372
1373 /*
1374 * --- Accessibility Support ---
1375 *
1376 */
1377
1378 /**
1379 * Get the index of this object in its accessible parent.<p>
1380 *
1381 * Note: as of the Java 2 platform v1.3, it is recommended that developers call
1382 * Component.AccessibleAWTComponent.getAccessibleIndexInParent() instead
1383 * of using this method.
1384 *
1385 * @return -1 of this object does not have an accessible parent.
1386 * Otherwise, the index of the child in its accessible parent.
1387 */
1388 public static int getAccessibleIndexInParent(Component c) {
1389 return c.getAccessibleContext().getAccessibleIndexInParent();
1390 }
1391
1392 /**
1393 * Returns the <code>Accessible</code> child contained at the
1394 * local coordinate <code>Point</code>, if one exists.
1395 * Otherwise returns <code>null</code>.
1396 *
1397 * @return the <code>Accessible</code> at the specified location,
1398 * if it exists; otherwise <code>null</code>
1399 */
1400 public static Accessible getAccessibleAt(Component c, Point p) {
1401 if (c instanceof Container) {
1402 return c.getAccessibleContext().getAccessibleComponent()
1403 .getAccessibleAt(p);
1404 } else if (c instanceof Accessible) {
1405 Accessible a = (Accessible) c;
1406 if (a != null) {
1407 AccessibleContext ac = a.getAccessibleContext();
1408 if (ac != null) {
1409 AccessibleComponent acmp;
1410 Point location;
1411 int nchildren = ac.getAccessibleChildrenCount();
1412 for (int i = 0; i < nchildren; i++) {
1413 a = ac.getAccessibleChild(i);
1414 if ((a != null)) {
1415 ac = a.getAccessibleContext();
1416 if (ac != null) {
1417 acmp = ac.getAccessibleComponent();
1418 if ((acmp != null)
1419 && (acmp.isShowing())) {
1420 location = acmp.getLocation();
1421 Point np = new Point(p.x
1422 - location.x, p.y
1423 - location.y);
1424 if (acmp.contains(np)) {
1425 return a;
1426 }
1427 }
1428 }
1429 }
1430 }
1431 }
1432 }
1433 return (Accessible) c;
1434 }
1435 return null;
1436 }
1437
1438 /**
1439 * Get the state of this object. <p>
1440 *
1441 * Note: as of the Java 2 platform v1.3, it is recommended that developers call
1442 * Component.AccessibleAWTComponent.getAccessibleIndexInParent() instead
1443 * of using this method.
1444 *
1445 * @return an instance of AccessibleStateSet containing the current state
1446 * set of the object
1447 * @see AccessibleState
1448 */
1449 public static AccessibleStateSet getAccessibleStateSet(Component c) {
1450 return c.getAccessibleContext().getAccessibleStateSet();
1451 }
1452
1453 /**
1454 * Returns the number of accessible children in the object. If all
1455 * of the children of this object implement Accessible, than this
1456 * method should return the number of children of this object. <p>
1457 *
1458 * Note: as of the Java 2 platform v1.3, it is recommended that developers call
1459 * Component.AccessibleAWTComponent.getAccessibleIndexInParent() instead
1460 * of using this method.
1461 *
1462 * @return the number of accessible children in the object.
1463 */
1464 public static int getAccessibleChildrenCount(Component c) {
1465 return c.getAccessibleContext().getAccessibleChildrenCount();
1466 }
1467
1468 /**
1469 * Return the nth Accessible child of the object. <p>
1470 *
1471 * Note: as of the Java 2 platform v1.3, it is recommended that developers call
1472 * Component.AccessibleAWTComponent.getAccessibleIndexInParent() instead
1473 * of using this method.
1474 *
1475 * @param i zero-based index of child
1476 * @return the nth Accessible child of the object
1477 */
1478 public static Accessible getAccessibleChild(Component c, int i) {
1479 return c.getAccessibleContext().getAccessibleChild(i);
1480 }
1481
1482 /**
1483 * Return the child <code>Component</code> of the specified
1484 * <code>Component</code> that is the focus owner, if any.
1485 *
1486 * @param c the root of the <code>Component</code> hierarchy to
1487 * search for the focus owner
1488 * @return the focus owner, or <code>null</code> if there is no focus
1489 * owner, or if the focus owner is not <code>comp</code>, or a
1490 * descendant of <code>comp</code>
1491 *
1492 * @see java.awt.KeyboardFocusManager#getFocusOwner
1493 * @deprecated As of 1.4, replaced by
1494 * <code>KeyboardFocusManager.getFocusOwner()</code>.
1495 */
1496 @Deprecated
1497 public static Component findFocusOwner(Component c) {
1498 Component focusOwner = KeyboardFocusManager
1499 .getCurrentKeyboardFocusManager().getFocusOwner();
1500
1501 // verify focusOwner is a descendant of c
1502 for (Component temp = focusOwner; temp != null; temp = (temp instanceof Window) ? null
1503 : temp.getParent()) {
1504 if (temp == c) {
1505 return focusOwner;
1506 }
1507 }
1508
1509 return null;
1510 }
1511
1512 /**
1513 * If c is a JRootPane descendant return its JRootPane ancestor.
1514 * If c is a RootPaneContainer then return its JRootPane.
1515 * @return the JRootPane for Component c or {@code null}.
1516 */
1517 public static JRootPane getRootPane(Component c) {
1518 if (c instanceof RootPaneContainer) {
1519 return ((RootPaneContainer) c).getRootPane();
1520 }
1521 for (; c != null; c = c.getParent()) {
1522 if (c instanceof JRootPane) {
1523 return (JRootPane) c;
1524 }
1525 }
1526 return null;
1527 }
1528
1529 /**
1530 * Returns the root component for the current component tree.
1531 * @return the first ancestor of c that's a Window or the last Applet ancestor
1532 */
1533 public static Component getRoot(Component c) {
1534 Component applet = null;
1535 for (Component p = c; p != null; p = p.getParent()) {
1536 if (p instanceof Window) {
1537 return p;
1538 }
1539 if (p instanceof Applet) {
1540 applet = p;
1541 }
1542 }
1543 return applet;
1544 }
1545
1546 /**
1547 * Process the key bindings for the <code>Component</code> associated with
1548 * <code>event</code>. This method is only useful if
1549 * <code>event.getComponent()</code> does not descend from
1550 * <code>JComponent</code>, or your are not invoking
1551 * <code>super.processKeyEvent</code> from within your
1552 * <code>JComponent</code> subclass. <code>JComponent</code>
1553 * automatically processes bindings from within its
1554 * <code>processKeyEvent</code> method, hence you rarely need
1555 * to directly invoke this method.
1556 *
1557 * @param event KeyEvent used to identify which bindings to process, as
1558 * well as which Component has focus.
1559 * @return true if a binding has found and processed
1560 * @since 1.4
1561 */
1562 public static boolean processKeyBindings(KeyEvent event) {
1563 if (event != null) {
1564 if (event.isConsumed()) {
1565 return false;
1566 }
1567
1568 Component component = event.getComponent();
1569 boolean pressed = (event.getID() == KeyEvent.KEY_PRESSED);
1570
1571 if (!isValidKeyEventForKeyBindings(event)) {
1572 return false;
1573 }
1574 // Find the first JComponent in the ancestor hierarchy, and
1575 // invoke processKeyBindings on it
1576 while (component != null) {
1577 if (component instanceof JComponent) {
1578 return ((JComponent) component).processKeyBindings(
1579 event, pressed);
1580 }
1581 if ((component instanceof Applet)
1582 || (component instanceof Window)) {
1583 // No JComponents, if Window or Applet parent, process
1584 // WHEN_IN_FOCUSED_WINDOW bindings.
1585 return JComponent
1586 .processKeyBindingsForAllComponents(event,
1587 (Container) component, pressed);
1588 }
1589 component = component.getParent();
1590 }
1591 }
1592 return false;
1593 }
1594
1595 /**
1596 * Returns true if the <code>e</code> is a valid KeyEvent to use in
1597 * processing the key bindings associated with JComponents.
1598 */
1599 static boolean isValidKeyEventForKeyBindings(KeyEvent e) {
1600 if (e.getID() == KeyEvent.KEY_TYPED) {
1601 int mod = e.getModifiers();
1602 if (((mod & ActionEvent.ALT_MASK) != 0)
1603 && ((mod & ActionEvent.CTRL_MASK) == 0)) {
1604 // filter out typed "alt-?" keys, but not those created
1605 // with AltGr, and not control characters
1606 return false;
1607 }
1608 }
1609 return true;
1610 }
1611
1612 /**
1613 * Invokes <code>actionPerformed</code> on <code>action</code> if
1614 * <code>action</code> is enabled (and non-{@code null}). The command for the
1615 * ActionEvent is determined by:
1616 * <ol>
1617 * <li>If the action was registered via
1618 * <code>registerKeyboardAction</code>, then the command string
1619 * passed in ({@code null} will be used if {@code null} was passed in).
1620 * <li>Action value with name Action.ACTION_COMMAND_KEY, unless {@code null}.
1621 * <li>String value of the KeyEvent, unless <code>getKeyChar</code>
1622 * returns KeyEvent.CHAR_UNDEFINED..
1623 * </ol>
1624 * This will return true if <code>action</code> is non-{@code null} and
1625 * actionPerformed is invoked on it.
1626 *
1627 * @since 1.3
1628 */
1629 public static boolean notifyAction(Action action, KeyStroke ks,
1630 KeyEvent event, Object sender, int modifiers) {
1631 if (action == null) {
1632 return false;
1633 }
1634 if (action instanceof UIAction) {
1635 if (!((UIAction) action).isEnabled(sender)) {
1636 return false;
1637 }
1638 } else if (!action.isEnabled()) {
1639 return false;
1640 }
1641 Object commandO;
1642 boolean stayNull;
1643
1644 // Get the command object.
1645 commandO = action.getValue(Action.ACTION_COMMAND_KEY);
1646 if (commandO == null
1647 && (action instanceof JComponent.ActionStandin)) {
1648 // ActionStandin is used for historical reasons to support
1649 // registerKeyboardAction with a null value.
1650 stayNull = true;
1651 } else {
1652 stayNull = false;
1653 }
1654
1655 // Convert it to a string.
1656 String command;
1657
1658 if (commandO != null) {
1659 command = commandO.toString();
1660 } else if (!stayNull
1661 && event.getKeyChar() != KeyEvent.CHAR_UNDEFINED) {
1662 command = String.valueOf(event.getKeyChar());
1663 } else {
1664 // Do null for undefined chars, or if registerKeyboardAction
1665 // was called with a null.
1666 command = null;
1667 }
1668 action.actionPerformed(new ActionEvent(sender,
1669 ActionEvent.ACTION_PERFORMED, command, event.getWhen(),
1670 modifiers));
1671 return true;
1672 }
1673
1674 /**
1675 * Convenience method to change the UI InputMap for <code>component</code>
1676 * to <code>uiInputMap</code>. If <code>uiInputMap</code> is {@code null},
1677 * this removes any previously installed UI InputMap.
1678 *
1679 * @since 1.3
1680 */
1681 public static void replaceUIInputMap(JComponent component,
1682 int type, InputMap uiInputMap) {
1683 InputMap map = component
1684 .getInputMap(type, (uiInputMap != null));
1685
1686 while (map != null) {
1687 InputMap parent = map.getParent();
1688 if (parent == null || (parent instanceof UIResource)) {
1689 map.setParent(uiInputMap);
1690 return;
1691 }
1692 map = parent;
1693 }
1694 }
1695
1696 /**
1697 * Convenience method to change the UI ActionMap for <code>component</code>
1698 * to <code>uiActionMap</code>. If <code>uiActionMap</code> is {@code null},
1699 * this removes any previously installed UI ActionMap.
1700 *
1701 * @since 1.3
1702 */
1703 public static void replaceUIActionMap(JComponent component,
1704 ActionMap uiActionMap) {
1705 ActionMap map = component.getActionMap((uiActionMap != null));
1706 ;
1707
1708 while (map != null) {
1709 ActionMap parent = map.getParent();
1710 if (parent == null || (parent instanceof UIResource)) {
1711 map.setParent(uiActionMap);
1712 return;
1713 }
1714 map = parent;
1715 }
1716 }
1717
1718 /**
1719 * Returns the InputMap provided by the UI for condition
1720 * <code>condition</code> in component <code>component</code>.
1721 * <p>This will return {@code null} if the UI has not installed a InputMap
1722 * of the specified type.
1723 *
1724 * @since 1.3
1725 */
1726 public static InputMap getUIInputMap(JComponent component,
1727 int condition) {
1728 InputMap map = component.getInputMap(condition, false);
1729 while (map != null) {
1730 InputMap parent = map.getParent();
1731 if (parent instanceof UIResource) {
1732 return parent;
1733 }
1734 map = parent;
1735 }
1736 return null;
1737 }
1738
1739 /**
1740 * Returns the ActionMap provided by the UI
1741 * in component <code>component</code>.
1742 * <p>This will return {@code null} if the UI has not installed an ActionMap.
1743 *
1744 * @since 1.3
1745 */
1746 public static ActionMap getUIActionMap(JComponent component) {
1747 ActionMap map = component.getActionMap(false);
1748 while (map != null) {
1749 ActionMap parent = map.getParent();
1750 if (parent instanceof UIResource) {
1751 return parent;
1752 }
1753 map = parent;
1754 }
1755 return null;
1756 }
1757
1758 // Don't use String, as it's not guaranteed to be unique in a Hashtable.
1759 private static final Object sharedOwnerFrameKey = new StringBuffer(
1760 "SwingUtilities.sharedOwnerFrame");
1761
1762 static class SharedOwnerFrame extends Frame implements
1763 WindowListener {
1764 public void addNotify() {
1765 super .addNotify();
1766 installListeners();
1767 }
1768
1769 /**
1770 * Install window listeners on owned windows to watch for displayability changes
1771 */
1772 void installListeners() {
1773 Window[] windows = getOwnedWindows();
1774 for (int ind = 0; ind < windows.length; ind++) {
1775 Window window = windows[ind];
1776 if (window != null) {
1777 window.removeWindowListener(this );
1778 window.addWindowListener(this );
1779 }
1780 }
1781 }
1782
1783 /**
1784 * Watches for displayability changes and disposes shared instance if there are no
1785 * displayable children left.
1786 */
1787 public void windowClosed(WindowEvent e) {
1788 synchronized (getTreeLock()) {
1789 Window[] windows = getOwnedWindows();
1790 for (int ind = 0; ind < windows.length; ind++) {
1791 Window window = windows[ind];
1792 if (window != null) {
1793 if (window.isDisplayable()) {
1794 return;
1795 }
1796 window.removeWindowListener(this );
1797 }
1798 }
1799 dispose();
1800 }
1801 }
1802
1803 public void windowOpened(WindowEvent e) {
1804 }
1805
1806 public void windowClosing(WindowEvent e) {
1807 }
1808
1809 public void windowIconified(WindowEvent e) {
1810 }
1811
1812 public void windowDeiconified(WindowEvent e) {
1813 }
1814
1815 public void windowActivated(WindowEvent e) {
1816 }
1817
1818 public void windowDeactivated(WindowEvent e) {
1819 }
1820
1821 public void show() {
1822 // This frame can never be shown
1823 }
1824
1825 public void dispose() {
1826 try {
1827 getToolkit().getSystemEventQueue();
1828 super .dispose();
1829 } catch (Exception e) {
1830 // untrusted code not allowed to dispose
1831 }
1832 }
1833 }
1834
1835 /**
1836 * Returns a toolkit-private, shared, invisible Frame
1837 * to be the owner for JDialogs and JWindows created with
1838 * {@code null} owners.
1839 * @exception HeadlessException if GraphicsEnvironment.isHeadless()
1840 * returns true.
1841 * @see java.awt.GraphicsEnvironment#isHeadless
1842 */
1843 static Frame getSharedOwnerFrame() throws HeadlessException {
1844 Frame sharedOwnerFrame = (Frame) SwingUtilities
1845 .appContextGet(sharedOwnerFrameKey);
1846 if (sharedOwnerFrame == null) {
1847 sharedOwnerFrame = new SharedOwnerFrame();
1848 SwingUtilities.appContextPut(sharedOwnerFrameKey,
1849 sharedOwnerFrame);
1850 }
1851 return sharedOwnerFrame;
1852 }
1853
1854 /**
1855 * Returns a SharedOwnerFrame's shutdown listener to dispose the SharedOwnerFrame
1856 * if it has no more displayable children.
1857 * @exception HeadlessException if GraphicsEnvironment.isHeadless()
1858 * returns true.
1859 * @see java.awt.GraphicsEnvironment#isHeadless
1860 */
1861 static WindowListener getSharedOwnerFrameShutdownListener()
1862 throws HeadlessException {
1863 Frame sharedOwnerFrame = getSharedOwnerFrame();
1864 return (WindowListener) sharedOwnerFrame;
1865 }
1866
1867 /* Don't make these AppContext accessors public or protected --
1868 * since AppContext is in sun.awt in 1.2, we shouldn't expose it
1869 * even indirectly with a public API.
1870 */
1871 // REMIND(aim): phase out use of 4 methods below since they
1872 // are just private covers for AWT methods (?)
1873 static Object appContextGet(Object key) {
1874 return AppContext.getAppContext().get(key);
1875 }
1876
1877 static void appContextPut(Object key, Object value) {
1878 AppContext.getAppContext().put(key, value);
1879 }
1880
1881 static void appContextRemove(Object key) {
1882 AppContext.getAppContext().remove(key);
1883 }
1884
1885 static Class loadSystemClass(String className)
1886 throws ClassNotFoundException {
1887 return Class.forName(className, true, Thread.currentThread()
1888 .getContextClassLoader());
1889 }
1890
1891 /*
1892 * Convenience function for determining ComponentOrientation. Helps us
1893 * avoid having Munge directives throughout the code.
1894 */
1895 static boolean isLeftToRight(Component c) {
1896 return c.getComponentOrientation().isLeftToRight();
1897 }
1898
1899 private SwingUtilities() {
1900 throw new Error(
1901 "SwingUtilities is just a container for static methods");
1902 }
1903
1904 /**
1905 * Returns true if the Icon <code>icon</code> is an instance of
1906 * ImageIcon, and the image it contains is the same as <code>image</code>.
1907 */
1908 static boolean doesIconReferenceImage(Icon icon, Image image) {
1909 Image iconImage = (icon != null && (icon instanceof ImageIcon)) ? ((ImageIcon) icon)
1910 .getImage()
1911 : null;
1912 return (iconImage == image);
1913 }
1914
1915 /**
1916 * Returns index of the first occurrence of <code>mnemonic</code>
1917 * within string <code>text</code>. Matching algorithm is not
1918 * case-sensitive.
1919 *
1920 * @param text The text to search through, may be {@code null}
1921 * @param mnemonic The mnemonic to find the character for.
1922 * @return index into the string if exists, otherwise -1
1923 */
1924 static int findDisplayedMnemonicIndex(String text, int mnemonic) {
1925 if (text == null || mnemonic == '\0') {
1926 return -1;
1927 }
1928
1929 char uc = Character.toUpperCase((char) mnemonic);
1930 char lc = Character.toLowerCase((char) mnemonic);
1931
1932 int uci = text.indexOf(uc);
1933 int lci = text.indexOf(lc);
1934
1935 if (uci == -1) {
1936 return lci;
1937 } else if (lci == -1) {
1938 return uci;
1939 } else {
1940 return (lci < uci) ? lci : uci;
1941 }
1942 }
1943
1944 /**
1945 * Stores the position and size of
1946 * the inner painting area of the specified component
1947 * in <code>r</code> and returns <code>r</code>.
1948 * The position and size specify the bounds of the component,
1949 * adjusted so as not to include the border area (the insets).
1950 * This method is useful for classes
1951 * that implement painting code.
1952 *
1953 * @param c the JComponent in question; if {@code null}, this method returns {@code null}
1954 * @param r the Rectangle instance to be modified;
1955 * may be {@code null}
1956 * @return {@code null} if the Component is {@code null};
1957 * otherwise, returns the passed-in rectangle (if non-{@code null})
1958 * or a new rectangle specifying position and size information
1959 *
1960 * @since 1.4
1961 */
1962 public static Rectangle calculateInnerArea(JComponent c, Rectangle r) {
1963 if (c == null) {
1964 return null;
1965 }
1966 Rectangle rect = r;
1967 Insets insets = c.getInsets();
1968
1969 if (rect == null) {
1970 rect = new Rectangle();
1971 }
1972
1973 rect.x = insets.left;
1974 rect.y = insets.top;
1975 rect.width = c.getWidth() - insets.left - insets.right;
1976 rect.height = c.getHeight() - insets.top - insets.bottom;
1977
1978 return rect;
1979 }
1980
1981 static void updateRendererOrEditorUI(Object rendererOrEditor) {
1982 if (rendererOrEditor == null) {
1983 return;
1984 }
1985
1986 Component component = null;
1987
1988 if (rendererOrEditor instanceof Component) {
1989 component = (Component) rendererOrEditor;
1990 }
1991 if (rendererOrEditor instanceof DefaultCellEditor) {
1992 component = ((DefaultCellEditor) rendererOrEditor)
1993 .getComponent();
1994 }
1995
1996 if (component != null) {
1997 SwingUtilities.updateComponentTreeUI(component);
1998 }
1999 }
2000 }
|