blob: d8f29e5288ca9d4c9c56e0b4817f412048b44863 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25package javax.swing;
26
27import sun.swing.SwingUtilities2;
28import sun.swing.UIAction;
29
30import java.applet.*;
31
32import java.awt.*;
33import java.awt.event.*;
34import java.awt.dnd.DropTarget;
35
36import java.util.Vector;
37import java.util.Hashtable;
38
39import java.lang.reflect.*;
40
41import javax.accessibility.*;
42import javax.swing.event.MenuDragMouseEvent;
43import javax.swing.plaf.UIResource;
44import javax.swing.text.View;
45import java.security.AccessController;
46import sun.security.action.GetPropertyAction;
47
48import sun.awt.AppContext;
49
50/**
51 * A collection of utility methods for Swing.
52 *
53 * @author unknown
54 */
55public class SwingUtilities implements SwingConstants
56{
57 // These states are system-wide, rather than AppContext wide.
58 private static boolean canAccessEventQueue = false;
59 private static boolean eventQueueTested = false;
60
61 /**
62 * Indicates if we should change the drop target when a
63 * {@code TransferHandler} is set.
64 */
65 private static boolean suppressDropSupport;
66
67 /**
68 * Indiciates if we've checked the system property for suppressing
69 * drop support.
70 */
71 private static boolean checkedSuppressDropSupport;
72
73
74 /**
75 * Returns true if <code>setTransferHandler</code> should change the
76 * <code>DropTarget</code>.
77 */
78 private static boolean getSuppressDropTarget() {
79 if (!checkedSuppressDropSupport) {
80 suppressDropSupport = Boolean.valueOf(
81 AccessController.doPrivileged(
82 new GetPropertyAction("suppressSwingDropSupport")));
83 checkedSuppressDropSupport = true;
84 }
85 return suppressDropSupport;
86 }
87
88 /**
89 * Installs a {@code DropTarget} on the component as necessary for a
90 * {@code TransferHandler} change.
91 */
92 static void installSwingDropTargetAsNecessary(Component c,
93 TransferHandler t) {
94
95 if (!getSuppressDropTarget()) {
96 DropTarget dropHandler = c.getDropTarget();
97 if ((dropHandler == null) || (dropHandler instanceof UIResource)) {
98 if (t == null) {
99 c.setDropTarget(null);
100 } else if (!GraphicsEnvironment.isHeadless()) {
101 c.setDropTarget(new TransferHandler.SwingDropTarget(c));
102 }
103 }
104 }
105 }
106
107 /**
108 * Return true if <code>a</code> contains <code>b</code>
109 */
110 public static final boolean isRectangleContainingRectangle(Rectangle a,Rectangle b) {
111 if (b.x >= a.x && (b.x + b.width) <= (a.x + a.width) &&
112 b.y >= a.y && (b.y + b.height) <= (a.y + a.height)) {
113 return true;
114 }
115 return false;
116 }
117
118 /**
119 * Return the rectangle (0,0,bounds.width,bounds.height) for the component <code>aComponent</code>
120 */
121 public static Rectangle getLocalBounds(Component aComponent) {
122 Rectangle b = new Rectangle(aComponent.getBounds());
123 b.x = b.y = 0;
124 return b;
125 }
126
127
128 /**
129 * Returns the first <code>Window </code> ancestor of <code>c</code>, or
130 * {@code null} if <code>c</code> is not contained inside a <code>Window</code>.
131 *
132 * @param c <code>Component</code> to get <code>Window</code> ancestor
133 * of.
134 * @return the first <code>Window </code> ancestor of <code>c</code>, or
135 * {@code null} if <code>c</code> is not contained inside a
136 * <code>Window</code>.
137 * @since 1.3
138 */
139 public static Window getWindowAncestor(Component c) {
140 for(Container p = c.getParent(); p != null; p = p.getParent()) {
141 if (p instanceof Window) {
142 return (Window)p;
143 }
144 }
145 return null;
146 }
147
148 /**
149 * Converts the location <code>x</code> <code>y</code> to the
150 * parents coordinate system, returning the location.
151 */
152 static Point convertScreenLocationToParent(Container parent,int x, int y) {
153 for (Container p = parent; p != null; p = p.getParent()) {
154 if (p instanceof Window) {
155 Point point = new Point(x, y);
156
157 SwingUtilities.convertPointFromScreen(point, parent);
158 return point;
159 }
160 }
161 throw new Error("convertScreenLocationToParent: no window ancestor");
162 }
163
164 /**
165 * Convert a <code>aPoint</code> in <code>source</code> coordinate system to
166 * <code>destination</code> coordinate system.
167 * If <code>source</code> is {@code null}, <code>aPoint</code> is assumed to be in <code>destination</code>'s
168 * root component coordinate system.
169 * If <code>destination</code> is {@code null}, <code>aPoint</code> will be converted to <code>source</code>'s
170 * root component coordinate system.
171 * If both <code>source</code> and <code>destination</code> are {@code null}, return <code>aPoint</code>
172 * without any conversion.
173 */
174 public static Point convertPoint(Component source,Point aPoint,Component destination) {
175 Point p;
176
177 if(source == null && destination == null)
178 return aPoint;
179 if(source == null) {
180 source = getWindowAncestor(destination);
181 if(source == null)
182 throw new Error("Source component not connected to component tree hierarchy");
183 }
184 p = new Point(aPoint);
185 convertPointToScreen(p,source);
186 if(destination == null) {
187 destination = getWindowAncestor(source);
188 if(destination == null)
189 throw new Error("Destination component not connected to component tree hierarchy");
190 }
191 convertPointFromScreen(p,destination);
192 return p;
193 }
194
195 /**
196 * Convert the point <code>(x,y)</code> in <code>source</code> coordinate system to
197 * <code>destination</code> coordinate system.
198 * If <code>source</code> is {@code null}, <code>(x,y)</code> is assumed to be in <code>destination</code>'s
199 * root component coordinate system.
200 * If <code>destination</code> is {@code null}, <code>(x,y)</code> will be converted to <code>source</code>'s
201 * root component coordinate system.
202 * If both <code>source</code> and <code>destination</code> are {@code null}, return <code>(x,y)</code>
203 * without any conversion.
204 */
205 public static Point convertPoint(Component source,int x, int y,Component destination) {
206 Point point = new Point(x,y);
207 return convertPoint(source,point,destination);
208 }
209
210 /**
211 * Convert the rectangle <code>aRectangle</code> in <code>source</code> coordinate system to
212 * <code>destination</code> coordinate system.
213 * If <code>source</code> is {@code null}, <code>aRectangle</code> is assumed to be in <code>destination</code>'s
214 * root component coordinate system.
215 * If <code>destination</code> is {@code null}, <code>aRectangle</code> will be converted to <code>source</code>'s
216 * root component coordinate system.
217 * If both <code>source</code> and <code>destination</code> are {@code null}, return <code>aRectangle</code>
218 * without any conversion.
219 */
220 public static Rectangle convertRectangle(Component source,Rectangle aRectangle,Component destination) {
221 Point point = new Point(aRectangle.x,aRectangle.y);
222 point = convertPoint(source,point,destination);
223 return new Rectangle(point.x,point.y,aRectangle.width,aRectangle.height);
224 }
225
226 /**
227 * Convenience method for searching above <code>comp</code> in the
228 * component hierarchy and returns the first object of class <code>c</code> it
229 * finds. Can return {@code null}, if a class <code>c</code> cannot be found.
230 */
231 public static Container getAncestorOfClass(Class<?> c, Component comp)
232 {
233 if(comp == null || c == null)
234 return null;
235
236 Container parent = comp.getParent();
237 while(parent != null && !(c.isInstance(parent)))
238 parent = parent.getParent();
239 return parent;
240 }
241
242 /**
243 * Convenience method for searching above <code>comp</code> in the
244 * component hierarchy and returns the first object of <code>name</code> it
245 * finds. Can return {@code null}, if <code>name</code> cannot be found.
246 */
247 public static Container getAncestorNamed(String name, Component comp) {
248 if(comp == null || name == null)
249 return null;
250
251 Container parent = comp.getParent();
252 while(parent != null && !(name.equals(parent.getName())))
253 parent = parent.getParent();
254 return parent;
255 }
256
257 /**
258 * Returns the deepest visible descendent Component of <code>parent</code>
259 * that contains the location <code>x</code>, <code>y</code>.
260 * If <code>parent</code> does not contain the specified location,
261 * then <code>null</code> is returned. If <code>parent</code> is not a
262 * container, or none of <code>parent</code>'s visible descendents
263 * contain the specified location, <code>parent</code> is returned.
264 *
265 * @param parent the root component to begin the search
266 * @param x the x target location
267 * @param y the y target location
268 */
269 public static Component getDeepestComponentAt(Component parent, int x, int y) {
270 if (!parent.contains(x, y)) {
271 return null;
272 }
273 if (parent instanceof Container) {
274 Component components[] = ((Container)parent).getComponents();
275 for (int i = 0 ; i < components.length ; i++) {
276 Component comp = components[i];
277 if (comp != null && comp.isVisible()) {
278 Point loc = comp.getLocation();
279 if (comp instanceof Container) {
280 comp = getDeepestComponentAt(comp, x - loc.x, y - loc.y);
281 } else {
282 comp = comp.getComponentAt(x - loc.x, y - loc.y);
283 }
284 if (comp != null && comp.isVisible()) {
285 return comp;
286 }
287 }
288 }
289 }
290 return parent;
291 }
292
293
294 /**
295 * Returns a MouseEvent similar to <code>sourceEvent</code> except that its x
296 * and y members have been converted to <code>destination</code>'s coordinate
297 * system. If <code>source</code> is {@code null}, <code>sourceEvent</code> x and y members
298 * are assumed to be into <code>destination</code>'s root component coordinate system.
299 * If <code>destination</code> is <code>null</code>, the
300 * returned MouseEvent will be in <code>source</code>'s coordinate system.
301 * <code>sourceEvent</code> will not be changed. A new event is returned.
302 * the <code>source</code> field of the returned event will be set
303 * to <code>destination</code> if destination is non-{@code null}
304 * use the translateMouseEvent() method to translate a mouse event from
305 * one component to another without changing the source.
306 */
307 public static MouseEvent convertMouseEvent(Component source,
308 MouseEvent sourceEvent,
309 Component destination) {
310 Point p = convertPoint(source,new Point(sourceEvent.getX(),
311 sourceEvent.getY()),
312 destination);
313 Component newSource;
314
315 if(destination != null)
316 newSource = destination;
317 else
318 newSource = source;
319
320 MouseEvent newEvent;
321 if (sourceEvent instanceof MouseWheelEvent) {
322 MouseWheelEvent sourceWheelEvent = (MouseWheelEvent)sourceEvent;
323 newEvent = new MouseWheelEvent(newSource,
324 sourceWheelEvent.getID(),
325 sourceWheelEvent.getWhen(),
326 sourceWheelEvent.getModifiers(),
327 p.x,p.y,
328 sourceWheelEvent.getXOnScreen(),
329 sourceWheelEvent.getYOnScreen(),
330 sourceWheelEvent.getClickCount(),
331 sourceWheelEvent.isPopupTrigger(),
332 sourceWheelEvent.getScrollType(),
333 sourceWheelEvent.getScrollAmount(),
334 sourceWheelEvent.getWheelRotation());
335 }
336 else if (sourceEvent instanceof MenuDragMouseEvent) {
337 MenuDragMouseEvent sourceMenuDragEvent = (MenuDragMouseEvent)sourceEvent;
338 newEvent = new MenuDragMouseEvent(newSource,
339 sourceMenuDragEvent.getID(),
340 sourceMenuDragEvent.getWhen(),
341 sourceMenuDragEvent.getModifiers(),
342 p.x,p.y,
343 sourceMenuDragEvent.getXOnScreen(),
344 sourceMenuDragEvent.getYOnScreen(),
345 sourceMenuDragEvent.getClickCount(),
346 sourceMenuDragEvent.isPopupTrigger(),
347 sourceMenuDragEvent.getPath(),
348 sourceMenuDragEvent.getMenuSelectionManager());
349 }
350 else {
351 newEvent = new MouseEvent(newSource,
352 sourceEvent.getID(),
353 sourceEvent.getWhen(),
354 sourceEvent.getModifiers(),
355 p.x,p.y,
356 sourceEvent.getXOnScreen(),
357 sourceEvent.getYOnScreen(),
358 sourceEvent.getClickCount(),
359 sourceEvent.isPopupTrigger(),
360 MouseEvent.NOBUTTON );
361 }
362 return newEvent;
363 }
364
365
366 /**
367 * Convert a point from a component's coordinate system to
368 * screen coordinates.
369 *
370 * @param p a Point object (converted to the new coordinate system)
371 * @param c a Component object
372 */
373 public static void convertPointToScreen(Point p,Component c) {
374 Rectangle b;
375 int x,y;
376
377 do {
378 if(c instanceof JComponent) {
379 x = ((JComponent)c).getX();
380 y = ((JComponent)c).getY();
381 } else if(c instanceof java.applet.Applet ||
382 c instanceof java.awt.Window) {
383 try {
384 Point pp = c.getLocationOnScreen();
385 x = pp.x;
386 y = pp.y;
387 } catch (IllegalComponentStateException icse) {
388 x = c.getX();
389 y = c.getY();
390 }
391 } else {
392 x = c.getX();
393 y = c.getY();
394 }
395
396 p.x += x;
397 p.y += y;
398
399 if(c instanceof java.awt.Window || c instanceof java.applet.Applet)
400 break;
401 c = c.getParent();
402 } while(c != null);
403 }
404
405 /**
406 * Convert a point from a screen coordinates to a component's
407 * coordinate system
408 *
409 * @param p a Point object (converted to the new coordinate system)
410 * @param c a Component object
411 */
412 public static void convertPointFromScreen(Point p,Component c) {
413 Rectangle b;
414 int x,y;
415
416 do {
417 if(c instanceof JComponent) {
418 x = ((JComponent)c).getX();
419 y = ((JComponent)c).getY();
420 } else if(c instanceof java.applet.Applet ||
421 c instanceof java.awt.Window) {
422 try {
423 Point pp = c.getLocationOnScreen();
424 x = pp.x;
425 y = pp.y;
426 } catch (IllegalComponentStateException icse) {
427 x = c.getX();
428 y = c.getY();
429 }
430 } else {
431 x = c.getX();
432 y = c.getY();
433 }
434
435 p.x -= x;
436 p.y -= y;
437
438 if(c instanceof java.awt.Window || c instanceof java.applet.Applet)
439 break;
440 c = c.getParent();
441 } while(c != null);
442 }
443
444 /**
445 * Returns the first <code>Window </code> ancestor of <code>c</code>, or
446 * {@code null} if <code>c</code> is not contained inside a <code>Window</code>.
447 * <p>
448 * Note: This method provides the same functionality as
449 * <code>getWindowAncestor</code>.
450 *
451 * @param c <code>Component</code> to get <code>Window</code> ancestor
452 * of.
453 * @return the first <code>Window </code> ancestor of <code>c</code>, or
454 * {@code null} if <code>c</code> is not contained inside a
455 * <code>Window</code>.
456 */
457 public static Window windowForComponent(Component c) {
458 return getWindowAncestor(c);
459 }
460
461 /**
462 * Return <code>true</code> if a component <code>a</code> descends from a component <code>b</code>
463 */
464 public static boolean isDescendingFrom(Component a,Component b) {
465 if(a == b)
466 return true;
467 for(Container p = a.getParent();p!=null;p=p.getParent())
468 if(p == b)
469 return true;
470 return false;
471 }
472
473
474 /**
475 * Convenience to calculate the intersection of two rectangles
476 * without allocating a new rectangle.
477 * If the two rectangles don't intersect,
478 * then the returned rectangle begins at (0,0)
479 * and has zero width and height.
480 *
481 * @param x the X coordinate of the first rectangle's top-left point
482 * @param y the Y coordinate of the first rectangle's top-left point
483 * @param width the width of the first rectangle
484 * @param height the height of the first rectangle
485 * @param dest the second rectangle
486 *
487 * @return <code>dest</code>, modified to specify the intersection
488 */
489 public static Rectangle computeIntersection(int x,int y,int width,int height,Rectangle dest) {
490 int x1 = (x > dest.x) ? x : dest.x;
491 int x2 = ((x+width) < (dest.x + dest.width)) ? (x+width) : (dest.x + dest.width);
492 int y1 = (y > dest.y) ? y : dest.y;
493 int y2 = ((y + height) < (dest.y + dest.height) ? (y+height) : (dest.y + dest.height));
494
495 dest.x = x1;
496 dest.y = y1;
497 dest.width = x2 - x1;
498 dest.height = y2 - y1;
499
500 // If rectangles don't intersect, return zero'd intersection.
501 if (dest.width < 0 || dest.height < 0) {
502 dest.x = dest.y = dest.width = dest.height = 0;
503 }
504
505 return dest;
506 }
507
508 /**
509 * Convenience method that calculates the union of two rectangles
510 * without allocating a new rectangle.
511 *
512 * @param x the x-coordinate of the first rectangle
513 * @param y the y-coordinate of the first rectangle
514 * @param width the width of the first rectangle
515 * @param height the height of the first rectangle
516 * @param dest the coordinates of the second rectangle; the union
517 * of the two rectangles is returned in this rectangle
518 * @return the <code>dest</code> <code>Rectangle</code>
519 */
520 public static Rectangle computeUnion(int x,int y,int width,int height,Rectangle dest) {
521 int x1 = (x < dest.x) ? x : dest.x;
522 int x2 = ((x+width) > (dest.x + dest.width)) ? (x+width) : (dest.x + dest.width);
523 int y1 = (y < dest.y) ? y : dest.y;
524 int y2 = ((y+height) > (dest.y + dest.height)) ? (y+height) : (dest.y + dest.height);
525
526 dest.x = x1;
527 dest.y = y1;
528 dest.width = (x2 - x1);
529 dest.height= (y2 - y1);
530 return dest;
531 }
532
533 /**
534 * Convenience returning an array of rect representing the regions within
535 * <code>rectA</code> that do not overlap with <code>rectB</code>. If the
536 * two Rects do not overlap, returns an empty array
537 */
538 public static Rectangle[] computeDifference(Rectangle rectA,Rectangle rectB) {
539 if (rectB == null || !rectA.intersects(rectB) || isRectangleContainingRectangle(rectB,rectA)) {
540 return new Rectangle[0];
541 }
542
543 Rectangle t = new Rectangle();
544 Rectangle a=null,b=null,c=null,d=null;
545 Rectangle result[];
546 int rectCount = 0;
547
548 /* rectA contains rectB */
549 if (isRectangleContainingRectangle(rectA,rectB)) {
550 t.x = rectA.x; t.y = rectA.y; t.width = rectB.x - rectA.x; t.height = rectA.height;
551 if(t.width > 0 && t.height > 0) {
552 a = new Rectangle(t);
553 rectCount++;
554 }
555
556 t.x = rectB.x; t.y = rectA.y; t.width = rectB.width; t.height = rectB.y - rectA.y;
557 if(t.width > 0 && t.height > 0) {
558 b = new Rectangle(t);
559 rectCount++;
560 }
561
562 t.x = rectB.x; t.y = rectB.y + rectB.height; t.width = rectB.width;
563 t.height = rectA.y + rectA.height - (rectB.y + rectB.height);
564 if(t.width > 0 && t.height > 0) {
565 c = new Rectangle(t);
566 rectCount++;
567 }
568
569 t.x = rectB.x + rectB.width; t.y = rectA.y; t.width = rectA.x + rectA.width - (rectB.x + rectB.width);
570 t.height = rectA.height;
571 if(t.width > 0 && t.height > 0) {
572 d = new Rectangle(t);
573 rectCount++;
574 }
575 } else {
576 /* 1 */
577 if (rectB.x <= rectA.x && rectB.y <= rectA.y) {
578 if ((rectB.x + rectB.width) > (rectA.x + rectA.width)) {
579
580 t.x = rectA.x; t.y = rectB.y + rectB.height;
581 t.width = rectA.width; t.height = rectA.y + rectA.height - (rectB.y + rectB.height);
582 if(t.width > 0 && t.height > 0) {
583 a = t;
584 rectCount++;
585 }
586 } else if ((rectB.y + rectB.height) > (rectA.y + rectA.height)) {
587 t.setBounds((rectB.x + rectB.width), rectA.y,
588 (rectA.x + rectA.width) - (rectB.x + rectB.width), rectA.height);
589 if(t.width > 0 && t.height > 0) {
590 a = t;
591 rectCount++;
592 }
593 } else {
594 t.setBounds((rectB.x + rectB.width), rectA.y,
595 (rectA.x + rectA.width) - (rectB.x + rectB.width),
596 (rectB.y + rectB.height) - rectA.y);
597 if(t.width > 0 && t.height > 0) {
598 a = new Rectangle(t);
599 rectCount++;
600 }
601
602 t.setBounds(rectA.x, (rectB.y + rectB.height), rectA.width,
603 (rectA.y + rectA.height) - (rectB.y + rectB.height));
604 if(t.width > 0 && t.height > 0) {
605 b = new Rectangle(t);
606 rectCount++;
607 }
608 }
609 } else if (rectB.x <= rectA.x && (rectB.y + rectB.height) >= (rectA.y + rectA.height)) {
610 if ((rectB.x + rectB.width) > (rectA.x + rectA.width)) {
611 t.setBounds(rectA.x, rectA.y, rectA.width, rectB.y - rectA.y);
612 if(t.width > 0 && t.height > 0) {
613 a = t;
614 rectCount++;
615 }
616 } else {
617 t.setBounds(rectA.x, rectA.y, rectA.width, rectB.y - rectA.y);
618 if(t.width > 0 && t.height > 0) {
619 a = new Rectangle(t);
620 rectCount++;
621 }
622 t.setBounds((rectB.x + rectB.width), rectB.y,
623 (rectA.x + rectA.width) - (rectB.x + rectB.width),
624 (rectA.y + rectA.height) - rectB.y);
625 if(t.width > 0 && t.height > 0) {
626 b = new Rectangle(t);
627 rectCount++;
628 }
629 }
630 } else if (rectB.x <= rectA.x) {
631 if ((rectB.x + rectB.width) >= (rectA.x + rectA.width)) {
632 t.setBounds(rectA.x, rectA.y, rectA.width, rectB.y - rectA.y);
633 if(t.width>0 && t.height > 0) {
634 a = new Rectangle(t);
635 rectCount++;
636 }
637
638 t.setBounds(rectA.x, (rectB.y + rectB.height), rectA.width,
639 (rectA.y + rectA.height) - (rectB.y + rectB.height));
640 if(t.width > 0 && t.height > 0) {
641 b = new Rectangle(t);
642 rectCount++;
643 }
644 } else {
645 t.setBounds(rectA.x, rectA.y, rectA.width, rectB.y - rectA.y);
646 if(t.width > 0 && t.height > 0) {
647 a = new Rectangle(t);
648 rectCount++;
649 }
650
651 t.setBounds((rectB.x + rectB.width), rectB.y,
652 (rectA.x + rectA.width) - (rectB.x + rectB.width),
653 rectB.height);
654 if(t.width > 0 && t.height > 0) {
655 b = new Rectangle(t);
656 rectCount++;
657 }
658
659 t.setBounds(rectA.x, (rectB.y + rectB.height), rectA.width,
660 (rectA.y + rectA.height) - (rectB.y + rectB.height));
661 if(t.width > 0 && t.height > 0) {
662 c = new Rectangle(t);
663 rectCount++;
664 }
665 }
666 } else if (rectB.x <= (rectA.x + rectA.width) && (rectB.x + rectB.width) > (rectA.x + rectA.width)) {
667 if (rectB.y <= rectA.y && (rectB.y + rectB.height) > (rectA.y + rectA.height)) {
668 t.setBounds(rectA.x, rectA.y, rectB.x - rectA.x, rectA.height);
669 if(t.width > 0 && t.height > 0) {
670 a = t;
671 rectCount++;
672 }
673 } else if (rectB.y <= rectA.y) {
674 t.setBounds(rectA.x, rectA.y, rectB.x - rectA.x,
675 (rectB.y + rectB.height) - rectA.y);
676 if(t.width > 0 && t.height > 0) {
677 a = new Rectangle(t);
678 rectCount++;
679 }
680
681 t.setBounds(rectA.x, (rectB.y + rectB.height), rectA.width,
682 (rectA.y + rectA.height) - (rectB.y + rectB.height));
683 if(t.width > 0 && t.height > 0) {
684 b = new Rectangle(t);
685 rectCount++;
686 }
687 } else if ((rectB.y + rectB.height) > (rectA.y + rectA.height)) {
688 t.setBounds(rectA.x, rectA.y, rectA.width, rectB.y - rectA.y);
689 if(t.width > 0 && t.height > 0) {
690 a = new Rectangle(t);
691 rectCount++;
692 }
693
694 t.setBounds(rectA.x, rectB.y, rectB.x - rectA.x,
695 (rectA.y + rectA.height) - rectB.y);
696 if(t.width > 0 && t.height > 0) {
697 b = new Rectangle(t);
698 rectCount++;
699 }
700 } else {
701 t.setBounds(rectA.x, rectA.y, rectA.width, rectB.y - rectA.y);
702 if(t.width > 0 && t.height > 0) {
703 a = new Rectangle(t);
704 rectCount++;
705 }
706
707 t.setBounds(rectA.x, rectB.y, rectB.x - rectA.x,
708 rectB.height);
709 if(t.width > 0 && t.height > 0) {
710 b = new Rectangle(t);
711 rectCount++;
712 }
713
714 t.setBounds(rectA.x, (rectB.y + rectB.height), rectA.width,
715 (rectA.y + rectA.height) - (rectB.y + rectB.height));
716 if(t.width > 0 && t.height > 0) {
717 c = new Rectangle(t);
718 rectCount++;
719 }
720 }
721 } else if (rectB.x >= rectA.x && (rectB.x + rectB.width) <= (rectA.x + rectA.width)) {
722 if (rectB.y <= rectA.y && (rectB.y + rectB.height) > (rectA.y + rectA.height)) {
723 t.setBounds(rectA.x, rectA.y, rectB.x - rectA.x, rectA.height);
724 if(t.width > 0 && t.height > 0) {
725 a = new Rectangle(t);
726 rectCount++;
727 }
728 t.setBounds((rectB.x + rectB.width), rectA.y,
729 (rectA.x + rectA.width) - (rectB.x + rectB.width), rectA.height);
730 if(t.width > 0 && t.height > 0) {
731 b = new Rectangle(t);
732 rectCount++;
733 }
734 } else if (rectB.y <= rectA.y) {
735 t.setBounds(rectA.x, rectA.y, rectB.x - rectA.x, rectA.height);
736 if(t.width > 0 && t.height > 0) {
737 a = new Rectangle(t);
738 rectCount++;
739 }
740
741 t.setBounds(rectB.x, (rectB.y + rectB.height),
742 rectB.width,
743 (rectA.y + rectA.height) - (rectB.y + rectB.height));
744 if(t.width > 0 && t.height > 0) {
745 b = new Rectangle(t);
746 rectCount++;
747 }
748
749 t.setBounds((rectB.x + rectB.width), rectA.y,
750 (rectA.x + rectA.width) - (rectB.x + rectB.width), rectA.height);
751 if(t.width > 0 && t.height > 0) {
752 c = new Rectangle(t);
753 rectCount++;
754 }
755 } else {
756 t.setBounds(rectA.x, rectA.y, rectB.x - rectA.x, rectA.height);
757 if(t.width > 0 && t.height > 0) {
758 a = new Rectangle(t);
759 rectCount++;
760 }
761
762 t.setBounds(rectB.x, rectA.y, rectB.width,
763 rectB.y - rectA.y);
764 if(t.width > 0 && t.height > 0) {
765 b = new Rectangle(t);
766 rectCount++;
767 }
768
769 t.setBounds((rectB.x + rectB.width), rectA.y,
770 (rectA.x + rectA.width) - (rectB.x + rectB.width), rectA.height);
771 if(t.width > 0 && t.height > 0) {
772 c = new Rectangle(t);
773 rectCount++;
774 }
775 }
776 }
777 }
778
779 result = new Rectangle[rectCount];
780 rectCount = 0;
781 if(a != null)
782 result[rectCount++] = a;
783 if(b != null)
784 result[rectCount++] = b;
785 if(c != null)
786 result[rectCount++] = c;
787 if(d != null)
788 result[rectCount++] = d;
789 return result;
790 }
791
792 /**
793 * Returns true if the mouse event specifies the left mouse button.
794 *
795 * @param anEvent a MouseEvent object
796 * @return true if the left mouse button was active
797 */
798 public static boolean isLeftMouseButton(MouseEvent anEvent) {
799 return ((anEvent.getModifiers() & InputEvent.BUTTON1_MASK) != 0);
800 }
801
802 /**
803 * Returns true if the mouse event specifies the middle mouse button.
804 *
805 * @param anEvent a MouseEvent object
806 * @return true if the middle mouse button was active
807 */
808 public static boolean isMiddleMouseButton(MouseEvent anEvent) {
809 return ((anEvent.getModifiers() & InputEvent.BUTTON2_MASK) == InputEvent.BUTTON2_MASK);
810 }
811
812 /**
813 * Returns true if the mouse event specifies the right mouse button.
814 *
815 * @param anEvent a MouseEvent object
816 * @return true if the right mouse button was active
817 */
818 public static boolean isRightMouseButton(MouseEvent anEvent) {
819 return ((anEvent.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK);
820 }
821
822 /**
823 * Compute the width of the string using a font with the specified
824 * "metrics" (sizes).
825 *
826 * @param fm a FontMetrics object to compute with
827 * @param str the String to compute
828 * @return an int containing the string width
829 */
830 public static int computeStringWidth(FontMetrics fm,String str) {
831 // You can't assume that a string's width is the sum of its
832 // characters' widths in Java2D -- it may be smaller due to
833 // kerning, etc.
834 return SwingUtilities2.stringWidth(null, fm, str);
835 }
836
837 /**
838 * Compute and return the location of the icons origin, the
839 * location of origin of the text baseline, and a possibly clipped
840 * version of the compound labels string. Locations are computed
841 * relative to the viewR rectangle.
842 * The JComponents orientation (LEADING/TRAILING) will also be taken
843 * into account and translated into LEFT/RIGHT values accordingly.
844 */
845 public static String layoutCompoundLabel(JComponent c,
846 FontMetrics fm,
847 String text,
848 Icon icon,
849 int verticalAlignment,
850 int horizontalAlignment,
851 int verticalTextPosition,
852 int horizontalTextPosition,
853 Rectangle viewR,
854 Rectangle iconR,
855 Rectangle textR,
856 int textIconGap)
857 {
858 boolean orientationIsLeftToRight = true;
859 int hAlign = horizontalAlignment;
860 int hTextPos = horizontalTextPosition;
861
862 if (c != null) {
863 if (!(c.getComponentOrientation().isLeftToRight())) {
864 orientationIsLeftToRight = false;
865 }
866 }
867
868 // Translate LEADING/TRAILING values in horizontalAlignment
869 // to LEFT/RIGHT values depending on the components orientation
870 switch (horizontalAlignment) {
871 case LEADING:
872 hAlign = (orientationIsLeftToRight) ? LEFT : RIGHT;
873 break;
874 case TRAILING:
875 hAlign = (orientationIsLeftToRight) ? RIGHT : LEFT;
876 break;
877 }
878
879 // Translate LEADING/TRAILING values in horizontalTextPosition
880 // to LEFT/RIGHT values depending on the components orientation
881 switch (horizontalTextPosition) {
882 case LEADING:
883 hTextPos = (orientationIsLeftToRight) ? LEFT : RIGHT;
884 break;
885 case TRAILING:
886 hTextPos = (orientationIsLeftToRight) ? RIGHT : LEFT;
887 break;
888 }
889
890 return layoutCompoundLabelImpl(c,
891 fm,
892 text,
893 icon,
894 verticalAlignment,
895 hAlign,
896 verticalTextPosition,
897 hTextPos,
898 viewR,
899 iconR,
900 textR,
901 textIconGap);
902 }
903
904 /**
905 * Compute and return the location of the icons origin, the
906 * location of origin of the text baseline, and a possibly clipped
907 * version of the compound labels string. Locations are computed
908 * relative to the viewR rectangle.
909 * This layoutCompoundLabel() does not know how to handle LEADING/TRAILING
910 * values in horizontalTextPosition (they will default to RIGHT) and in
911 * horizontalAlignment (they will default to CENTER).
912 * Use the other version of layoutCompoundLabel() instead.
913 */
914 public static String layoutCompoundLabel(
915 FontMetrics fm,
916 String text,
917 Icon icon,
918 int verticalAlignment,
919 int horizontalAlignment,
920 int verticalTextPosition,
921 int horizontalTextPosition,
922 Rectangle viewR,
923 Rectangle iconR,
924 Rectangle textR,
925 int textIconGap)
926 {
927 return layoutCompoundLabelImpl(null, fm, text, icon,
928 verticalAlignment,
929 horizontalAlignment,
930 verticalTextPosition,
931 horizontalTextPosition,
932 viewR, iconR, textR, textIconGap);
933 }
934
935 /**
936 * Compute and return the location of the icons origin, the
937 * location of origin of the text baseline, and a possibly clipped
938 * version of the compound labels string. Locations are computed
939 * relative to the viewR rectangle.
940 * This layoutCompoundLabel() does not know how to handle LEADING/TRAILING
941 * values in horizontalTextPosition (they will default to RIGHT) and in
942 * horizontalAlignment (they will default to CENTER).
943 * Use the other version of layoutCompoundLabel() instead.
944 */
945 private static String layoutCompoundLabelImpl(
946 JComponent c,
947 FontMetrics fm,
948 String text,
949 Icon icon,
950 int verticalAlignment,
951 int horizontalAlignment,
952 int verticalTextPosition,
953 int horizontalTextPosition,
954 Rectangle viewR,
955 Rectangle iconR,
956 Rectangle textR,
957 int textIconGap)
958 {
959 /* Initialize the icon bounds rectangle iconR.
960 */
961
962 if (icon != null) {
963 iconR.width = icon.getIconWidth();
964 iconR.height = icon.getIconHeight();
965 }
966 else {
967 iconR.width = iconR.height = 0;
968 }
969
970 /* Initialize the text bounds rectangle textR. If a null
971 * or and empty String was specified we substitute "" here
972 * and use 0,0,0,0 for textR.
973 */
974
975 boolean textIsEmpty = (text == null) || text.equals("");
976 int lsb = 0;
977 /* Unless both text and icon are non-null, we effectively ignore
978 * the value of textIconGap.
979 */
980 int gap;
981
982 View v = null;
983 if (textIsEmpty) {
984 textR.width = textR.height = 0;
985 text = "";
986 gap = 0;
987 }
988 else {
989 int availTextWidth;
990 gap = (icon == null) ? 0 : textIconGap;
991
992 if (horizontalTextPosition == CENTER) {
993 availTextWidth = viewR.width;
994 }
995 else {
996 availTextWidth = viewR.width - (iconR.width + gap);
997 }
998 v = (c != null) ? (View) c.getClientProperty("html") : null;
999 if (v != null) {
1000 textR.width = Math.min(availTextWidth,
1001 (int) v.getPreferredSpan(View.X_AXIS));
1002 textR.height = (int) v.getPreferredSpan(View.Y_AXIS);
1003 } else {
1004 textR.width = SwingUtilities2.stringWidth(c, fm, text);
1005
1006 // Take into account the left and right side bearings.
1007 // This gives more space than it is actually needed,
1008 // but there are two reasons:
1009 // 1. If we set the width to the actual bounds,
1010 // all callers would have to account for the bearings
1011 // themselves. NOTE: all pref size calculations don't do it.
1012 // 2. You can do a drawString at the returned location
1013 // and the text won't be clipped.
1014 lsb = SwingUtilities2.getLeftSideBearing(c, fm, text);
1015 if (lsb < 0) {
1016 textR.width -= lsb;
1017 }
1018 int rsb = SwingUtilities2.getRightSideBearing(c, fm, text);
1019 if (rsb > 0) {
1020 textR.width += rsb;
1021 }
1022
1023 if (textR.width > availTextWidth) {
1024 text = SwingUtilities2.clipString(c, fm, text,
1025 availTextWidth);
1026 textR.width = SwingUtilities2.stringWidth(c, fm, text);
1027 }
1028 textR.height = fm.getHeight();
1029 }
1030 }
1031
1032
1033 /* Compute textR.x,y given the verticalTextPosition and
1034 * horizontalTextPosition properties
1035 */
1036
1037 if (verticalTextPosition == TOP) {
1038 if (horizontalTextPosition != CENTER) {
1039 textR.y = 0;
1040 }
1041 else {
1042 textR.y = -(textR.height + gap);
1043 }
1044 }
1045 else if (verticalTextPosition == CENTER) {
1046 textR.y = (iconR.height / 2) - (textR.height / 2);
1047 }
1048 else { // (verticalTextPosition == BOTTOM)
1049 if (horizontalTextPosition != CENTER) {
1050 textR.y = iconR.height - textR.height;
1051 }
1052 else {
1053 textR.y = (iconR.height + gap);
1054 }
1055 }
1056
1057 if (horizontalTextPosition == LEFT) {
1058 textR.x = -(textR.width + gap);
1059 }
1060 else if (horizontalTextPosition == CENTER) {
1061 textR.x = (iconR.width / 2) - (textR.width / 2);
1062 }
1063 else { // (horizontalTextPosition == RIGHT)
1064 textR.x = (iconR.width + gap);
1065 }
1066
1067 // WARNING: DefaultTreeCellEditor uses a shortened version of
1068 // this algorithm to position it's Icon. If you change how this
1069 // is calculated, be sure and update DefaultTreeCellEditor too.
1070
1071 /* labelR is the rectangle that contains iconR and textR.
1072 * Move it to its proper position given the labelAlignment
1073 * properties.
1074 *
1075 * To avoid actually allocating a Rectangle, Rectangle.union
1076 * has been inlined below.
1077 */
1078 int labelR_x = Math.min(iconR.x, textR.x);
1079 int labelR_width = Math.max(iconR.x + iconR.width,
1080 textR.x + textR.width) - labelR_x;
1081 int labelR_y = Math.min(iconR.y, textR.y);
1082 int labelR_height = Math.max(iconR.y + iconR.height,
1083 textR.y + textR.height) - labelR_y;
1084
1085 int dx, dy;
1086
1087 if (verticalAlignment == TOP) {
1088 dy = viewR.y - labelR_y;
1089 }
1090 else if (verticalAlignment == CENTER) {
1091 dy = (viewR.y + (viewR.height / 2)) - (labelR_y + (labelR_height / 2));
1092 }
1093 else { // (verticalAlignment == BOTTOM)
1094 dy = (viewR.y + viewR.height) - (labelR_y + labelR_height);
1095 }
1096
1097 if (horizontalAlignment == LEFT) {
1098 dx = viewR.x - labelR_x;
1099 }
1100 else if (horizontalAlignment == RIGHT) {
1101 dx = (viewR.x + viewR.width) - (labelR_x + labelR_width);
1102 }
1103 else { // (horizontalAlignment == CENTER)
1104 dx = (viewR.x + (viewR.width / 2)) -
1105 (labelR_x + (labelR_width / 2));
1106 }
1107
1108 /* Translate textR and glypyR by dx,dy.
1109 */
1110
1111 textR.x += dx;
1112 textR.y += dy;
1113
1114 iconR.x += dx;
1115 iconR.y += dy;
1116
1117 if (lsb < 0) {
1118 // lsb is negative. Shift the x location so that the text is
1119 // visually drawn at the right location.
1120 textR.x -= lsb;
1121 }
1122
1123 return text;
1124 }
1125
1126
1127 /**
1128 * Paints a component to the specified <code>Graphics</code>.
1129 * This method is primarily useful to render
1130 * <code>Component</code>s that don't exist as part of the visible
1131 * containment hierarchy, but are used for rendering. For
1132 * example, if you are doing your own rendering and want to render
1133 * some text (or even HTML), you could make use of
1134 * <code>JLabel</code>'s text rendering support and have it paint
1135 * directly by way of this method, without adding the label to the
1136 * visible containment hierarchy.
1137 * <p>
1138 * This method makes use of <code>CellRendererPane</code> to handle
1139 * the actual painting, and is only recommended if you use one
1140 * component for rendering. If you make use of multiple components
1141 * to handle the rendering, as <code>JTable</code> does, use
1142 * <code>CellRendererPane</code> directly. Otherwise, as described
1143 * below, you could end up with a <code>CellRendererPane</code>
1144 * per <code>Component</code>.
1145 * <p>
1146 * If <code>c</code>'s parent is not a <code>CellRendererPane</code>,
1147 * a new <code>CellRendererPane</code> is created, <code>c</code> is
1148 * added to it, and the <code>CellRendererPane</code> is added to
1149 * <code>p</code>. If <code>c</code>'s parent is a
1150 * <code>CellRendererPane</code> and the <code>CellRendererPane</code>s
1151 * parent is not <code>p</code>, it is added to <code>p</code>.
1152 * <p>
1153 * The component should either descend from <code>JComponent</code>
1154 * or be another kind of lightweight component.
1155 * A lightweight component is one whose "lightweight" property
1156 * (returned by the <code>Component</code>
1157 * <code>isLightweight</code> method)
1158 * is true. If the Component is not lightweight, bad things map happen:
1159 * crashes, exceptions, painting problems...
1160 *
1161 * @param g the <code>Graphics</code> object to draw on
1162 * @param c the <code>Component</code> to draw
1163 * @param p the intermediate <code>Container</code>
1164 * @param x an int specifying the left side of the area draw in, in pixels,
1165 * measured from the left edge of the graphics context
1166 * @param y an int specifying the top of the area to draw in, in pixels
1167 * measured down from the top edge of the graphics context
1168 * @param w an int specifying the width of the area draw in, in pixels
1169 * @param h an int specifying the height of the area draw in, in pixels
1170 *
1171 * @see CellRendererPane
1172 * @see java.awt.Component#isLightweight
1173 */
1174 public static void paintComponent(Graphics g, Component c, Container p, int x, int y, int w, int h) {
1175 getCellRendererPane(c, p).paintComponent(g, c, p, x, y, w, h,false);
1176 }
1177
1178 /**
1179 * Paints a component to the specified <code>Graphics</code>. This
1180 * is a cover method for
1181 * {@link #paintComponent(Graphics,Component,Container,int,int,int,int)}.
1182 * Refer to it for more information.
1183 *
1184 * @param g the <code>Graphics</code> object to draw on
1185 * @param c the <code>Component</code> to draw
1186 * @param p the intermediate <code>Container</code>
1187 * @param r the <code>Rectangle</code> to draw in
1188 *
1189 * @see #paintComponent(Graphics,Component,Container,int,int,int,int)
1190 * @see CellRendererPane
1191 */
1192 public static void paintComponent(Graphics g, Component c, Container p, Rectangle r) {
1193 paintComponent(g, c, p, r.x, r.y, r.width, r.height);
1194 }
1195
1196
1197 /*
1198 * Ensures that cell renderer <code>c</code> has a
1199 * <code>ComponentShell</code> parent and that
1200 * the shell's parent is p.
1201 */
1202 private static CellRendererPane getCellRendererPane(Component c, Container p) {
1203 Container shell = c.getParent();
1204 if (shell instanceof CellRendererPane) {
1205 if (shell.getParent() != p) {
1206 p.add(shell);
1207 }
1208 } else {
1209 shell = new CellRendererPane();
1210 shell.add(c);
1211 p.add(shell);
1212 }
1213 return (CellRendererPane)shell;
1214 }
1215
1216 /**
1217 * A simple minded look and feel change: ask each node in the tree
1218 * to <code>updateUI()</code> -- that is, to initialize its UI property
1219 * with the current look and feel.
1220 */
1221 public static void updateComponentTreeUI(Component c) {
1222 updateComponentTreeUI0(c);
1223 c.invalidate();
1224 c.validate();
1225 c.repaint();
1226 }
1227
1228 private static void updateComponentTreeUI0(Component c) {
1229 if (c instanceof JComponent) {
1230 JComponent jc = (JComponent) c;
1231 jc.updateUI();
1232 JPopupMenu jpm =jc.getComponentPopupMenu();
1233 if(jpm != null) {
1234 updateComponentTreeUI(jpm);
1235 }
1236 }
1237 Component[] children = null;
1238 if (c instanceof JMenu) {
1239 children = ((JMenu)c).getMenuComponents();
1240 }
1241 else if (c instanceof Container) {
1242 children = ((Container)c).getComponents();
1243 }
1244 if (children != null) {
1245 for(int i = 0; i < children.length; i++) {
1246 updateComponentTreeUI0(children[i]);
1247 }
1248 }
1249 }
1250
1251
1252 /**
1253 * Causes <i>doRun.run()</i> to be executed asynchronously on the
1254 * AWT event dispatching thread. This will happen after all
1255 * pending AWT events have been processed. This method should
1256 * be used when an application thread needs to update the GUI.
1257 * In the following example the <code>invokeLater</code> call queues
1258 * the <code>Runnable</code> object <code>doHelloWorld</code>
1259 * on the event dispatching thread and
1260 * then prints a message.
1261 * <pre>
1262 * Runnable doHelloWorld = new Runnable() {
1263 * public void run() {
1264 * System.out.println("Hello World on " + Thread.currentThread());
1265 * }
1266 * };
1267 *
1268 * SwingUtilities.invokeLater(doHelloWorld);
1269 * System.out.println("This might well be displayed before the other message.");
1270 * </pre>
1271 * If invokeLater is called from the event dispatching thread --
1272 * for example, from a JButton's ActionListener -- the <i>doRun.run()</i> will
1273 * still be deferred until all pending events have been processed.
1274 * Note that if the <i>doRun.run()</i> throws an uncaught exception
1275 * the event dispatching thread will unwind (not the current thread).
1276 * <p>
1277 * Additional documentation and examples for this method can be
1278 * found in
1279 * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How to Use Threads</a>,
1280 * in <em>The Java Tutorial</em>.
1281 * <p>
1282 * As of 1.3 this method is just a cover for <code>java.awt.EventQueue.invokeLater()</code>.
1283 * <p>
1284 * Unlike the rest of Swing, this method can be invoked from any thread.
1285 *
1286 * @see #invokeAndWait
1287 */
1288 public static void invokeLater(Runnable doRun) {
1289 EventQueue.invokeLater(doRun);
1290 }
1291
1292
1293 /**
1294 * Causes <code>doRun.run()</code> to be executed synchronously on the
1295 * AWT event dispatching thread. This call blocks until
1296 * all pending AWT events have been processed and (then)
1297 * <code>doRun.run()</code> returns. This method should
1298 * be used when an application thread needs to update the GUI.
1299 * It shouldn't be called from the event dispatching thread.
1300 * Here's an example that creates a new application thread
1301 * that uses <code>invokeAndWait</code> to print a string from the event
1302 * dispatching thread and then, when that's finished, print
1303 * a string from the application thread.
1304 * <pre>
1305 * final Runnable doHelloWorld = new Runnable() {
1306 * public void run() {
1307 * System.out.println("Hello World on " + Thread.currentThread());
1308 * }
1309 * };
1310 *
1311 * Thread appThread = new Thread() {
1312 * public void run() {
1313 * try {
1314 * SwingUtilities.invokeAndWait(doHelloWorld);
1315 * }
1316 * catch (Exception e) {
1317 * e.printStackTrace();
1318 * }
1319 * System.out.println("Finished on " + Thread.currentThread());
1320 * }
1321 * };
1322 * appThread.start();
1323 * </pre>
1324 * Note that if the <code>Runnable.run</code> method throws an
1325 * uncaught exception
1326 * (on the event dispatching thread) it's caught and rethrown, as
1327 * an <code>InvocationTargetException</code>, on the caller's thread.
1328 * <p>
1329 * Additional documentation and examples for this method can be
1330 * found in
1331 * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How to Use Threads</a>,
1332 * in <em>The Java Tutorial</em>.
1333 * <p>
1334 * As of 1.3 this method is just a cover for
1335 * <code>java.awt.EventQueue.invokeAndWait()</code>.
1336 *
1337 * @exception InterruptedException if we're interrupted while waiting for
1338 * the event dispatching thread to finish excecuting
1339 * <code>doRun.run()</code>
1340 * @exception InvocationTargetException if an exception is thrown
1341 * while running <code>doRun</code>
1342 *
1343 * @see #invokeLater
1344 */
1345 public static void invokeAndWait(final Runnable doRun)
1346 throws InterruptedException, InvocationTargetException
1347 {
1348 EventQueue.invokeAndWait(doRun);
1349 }
1350
1351 /**
1352 * Returns true if the current thread is an AWT event dispatching thread.
1353 * <p>
1354 * As of 1.3 this method is just a cover for
1355 * <code>java.awt.EventQueue.isDispatchThread()</code>.
1356 *
1357 * @return true if the current thread is an AWT event dispatching thread
1358 */
1359 public static boolean isEventDispatchThread()
1360 {
1361 return EventQueue.isDispatchThread();
1362 }
1363
1364
1365 /*
1366 * --- Accessibility Support ---
1367 *
1368 */
1369
1370 /**
1371 * Get the index of this object in its accessible parent.<p>
1372 *
1373 * Note: as of the Java 2 platform v1.3, it is recommended that developers call
1374 * Component.AccessibleAWTComponent.getAccessibleIndexInParent() instead
1375 * of using this method.
1376 *
1377 * @return -1 of this object does not have an accessible parent.
1378 * Otherwise, the index of the child in its accessible parent.
1379 */
1380 public static int getAccessibleIndexInParent(Component c) {
1381 return c.getAccessibleContext().getAccessibleIndexInParent();
1382 }
1383
1384 /**
1385 * Returns the <code>Accessible</code> child contained at the
1386 * local coordinate <code>Point</code>, if one exists.
1387 * Otherwise returns <code>null</code>.
1388 *
1389 * @return the <code>Accessible</code> at the specified location,
1390 * if it exists; otherwise <code>null</code>
1391 */
1392 public static Accessible getAccessibleAt(Component c, Point p) {
1393 if (c instanceof Container) {
1394 return c.getAccessibleContext().getAccessibleComponent().getAccessibleAt(p);
1395 } else if (c instanceof Accessible) {
1396 Accessible a = (Accessible) c;
1397 if (a != null) {
1398 AccessibleContext ac = a.getAccessibleContext();
1399 if (ac != null) {
1400 AccessibleComponent acmp;
1401 Point location;
1402 int nchildren = ac.getAccessibleChildrenCount();
1403 for (int i=0; i < nchildren; i++) {
1404 a = ac.getAccessibleChild(i);
1405 if ((a != null)) {
1406 ac = a.getAccessibleContext();
1407 if (ac != null) {
1408 acmp = ac.getAccessibleComponent();
1409 if ((acmp != null) && (acmp.isShowing())) {
1410 location = acmp.getLocation();
1411 Point np = new Point(p.x-location.x,
1412 p.y-location.y);
1413 if (acmp.contains(np)){
1414 return a;
1415 }
1416 }
1417 }
1418 }
1419 }
1420 }
1421 }
1422 return (Accessible) c;
1423 }
1424 return null;
1425 }
1426
1427 /**
1428 * Get the state of this object. <p>
1429 *
1430 * Note: as of the Java 2 platform v1.3, it is recommended that developers call
1431 * Component.AccessibleAWTComponent.getAccessibleIndexInParent() instead
1432 * of using this method.
1433 *
1434 * @return an instance of AccessibleStateSet containing the current state
1435 * set of the object
1436 * @see AccessibleState
1437 */
1438 public static AccessibleStateSet getAccessibleStateSet(Component c) {
1439 return c.getAccessibleContext().getAccessibleStateSet();
1440 }
1441
1442 /**
1443 * Returns the number of accessible children in the object. If all
1444 * of the children of this object implement Accessible, than this
1445 * method should return the number of children of this object. <p>
1446 *
1447 * Note: as of the Java 2 platform v1.3, it is recommended that developers call
1448 * Component.AccessibleAWTComponent.getAccessibleIndexInParent() instead
1449 * of using this method.
1450 *
1451 * @return the number of accessible children in the object.
1452 */
1453 public static int getAccessibleChildrenCount(Component c) {
1454 return c.getAccessibleContext().getAccessibleChildrenCount();
1455 }
1456
1457 /**
1458 * Return the nth Accessible child of the object. <p>
1459 *
1460 * Note: as of the Java 2 platform v1.3, it is recommended that developers call
1461 * Component.AccessibleAWTComponent.getAccessibleIndexInParent() instead
1462 * of using this method.
1463 *
1464 * @param i zero-based index of child
1465 * @return the nth Accessible child of the object
1466 */
1467 public static Accessible getAccessibleChild(Component c, int i) {
1468 return c.getAccessibleContext().getAccessibleChild(i);
1469 }
1470
1471 /**
1472 * Return the child <code>Component</code> of the specified
1473 * <code>Component</code> that is the focus owner, if any.
1474 *
1475 * @param c the root of the <code>Component</code> hierarchy to
1476 * search for the focus owner
1477 * @return the focus owner, or <code>null</code> if there is no focus
1478 * owner, or if the focus owner is not <code>comp</code>, or a
1479 * descendant of <code>comp</code>
1480 *
1481 * @see java.awt.KeyboardFocusManager#getFocusOwner
1482 * @deprecated As of 1.4, replaced by
1483 * <code>KeyboardFocusManager.getFocusOwner()</code>.
1484 */
1485 @Deprecated
1486 public static Component findFocusOwner(Component c) {
1487 Component focusOwner = KeyboardFocusManager.
1488 getCurrentKeyboardFocusManager().getFocusOwner();
1489
1490 // verify focusOwner is a descendant of c
1491 for (Component temp = focusOwner; temp != null;
1492 temp = (temp instanceof Window) ? null : temp.getParent())
1493 {
1494 if (temp == c) {
1495 return focusOwner;
1496 }
1497 }
1498
1499 return null;
1500 }
1501
1502 /**
1503 * If c is a JRootPane descendant return its JRootPane ancestor.
1504 * If c is a RootPaneContainer then return its JRootPane.
1505 * @return the JRootPane for Component c or {@code null}.
1506 */
1507 public static JRootPane getRootPane(Component c) {
1508 if (c instanceof RootPaneContainer) {
1509 return ((RootPaneContainer)c).getRootPane();
1510 }
1511 for( ; c != null; c = c.getParent()) {
1512 if (c instanceof JRootPane) {
1513 return (JRootPane)c;
1514 }
1515 }
1516 return null;
1517 }
1518
1519
1520 /**
1521 * Returns the root component for the current component tree.
1522 * @return the first ancestor of c that's a Window or the last Applet ancestor
1523 */
1524 public static Component getRoot(Component c) {
1525 Component applet = null;
1526 for(Component p = c; p != null; p = p.getParent()) {
1527 if (p instanceof Window) {
1528 return p;
1529 }
1530 if (p instanceof Applet) {
1531 applet = p;
1532 }
1533 }
1534 return applet;
1535 }
1536
1537 /**
1538 * Process the key bindings for the <code>Component</code> associated with
1539 * <code>event</code>. This method is only useful if
1540 * <code>event.getComponent()</code> does not descend from
1541 * <code>JComponent</code>, or your are not invoking
1542 * <code>super.processKeyEvent</code> from within your
1543 * <code>JComponent</code> subclass. <code>JComponent</code>
1544 * automatically processes bindings from within its
1545 * <code>processKeyEvent</code> method, hence you rarely need
1546 * to directly invoke this method.
1547 *
1548 * @param event KeyEvent used to identify which bindings to process, as
1549 * well as which Component has focus.
1550 * @return true if a binding has found and processed
1551 * @since 1.4
1552 */
1553 public static boolean processKeyBindings(KeyEvent event) {
1554 if (event != null) {
1555 if (event.isConsumed()) {
1556 return false;
1557 }
1558
1559 Component component = event.getComponent();
1560 boolean pressed = (event.getID() == KeyEvent.KEY_PRESSED);
1561
1562 if (!isValidKeyEventForKeyBindings(event)) {
1563 return false;
1564 }
1565 // Find the first JComponent in the ancestor hierarchy, and
1566 // invoke processKeyBindings on it
1567 while (component != null) {
1568 if (component instanceof JComponent) {
1569 return ((JComponent)component).processKeyBindings(
1570 event, pressed);
1571 }
1572 if ((component instanceof Applet) ||
1573 (component instanceof Window)) {
1574 // No JComponents, if Window or Applet parent, process
1575 // WHEN_IN_FOCUSED_WINDOW bindings.
1576 return JComponent.processKeyBindingsForAllComponents(
1577 event, (Container)component, pressed);
1578 }
1579 component = component.getParent();
1580 }
1581 }
1582 return false;
1583 }
1584
1585 /**
1586 * Returns true if the <code>e</code> is a valid KeyEvent to use in
1587 * processing the key bindings associated with JComponents.
1588 */
1589 static boolean isValidKeyEventForKeyBindings(KeyEvent e) {
1590 if (e.getID() == KeyEvent.KEY_TYPED) {
1591 int mod = e.getModifiers();
1592 if (((mod & ActionEvent.ALT_MASK) != 0) &&
1593 ((mod & ActionEvent.CTRL_MASK) == 0)) {
1594 // filter out typed "alt-?" keys, but not those created
1595 // with AltGr, and not control characters
1596 return false;
1597 }
1598 }
1599 return true;
1600 }
1601
1602 /**
1603 * Invokes <code>actionPerformed</code> on <code>action</code> if
1604 * <code>action</code> is enabled (and non-{@code null}). The command for the
1605 * ActionEvent is determined by:
1606 * <ol>
1607 * <li>If the action was registered via
1608 * <code>registerKeyboardAction</code>, then the command string
1609 * passed in ({@code null} will be used if {@code null} was passed in).
1610 * <li>Action value with name Action.ACTION_COMMAND_KEY, unless {@code null}.
1611 * <li>String value of the KeyEvent, unless <code>getKeyChar</code>
1612 * returns KeyEvent.CHAR_UNDEFINED..
1613 * </ol>
1614 * This will return true if <code>action</code> is non-{@code null} and
1615 * actionPerformed is invoked on it.
1616 *
1617 * @since 1.3
1618 */
1619 public static boolean notifyAction(Action action, KeyStroke ks,
1620 KeyEvent event, Object sender,
1621 int modifiers) {
1622 if (action == null) {
1623 return false;
1624 }
1625 if (action instanceof UIAction) {
1626 if (!((UIAction)action).isEnabled(sender)) {
1627 return false;
1628 }
1629 }
1630 else if (!action.isEnabled()) {
1631 return false;
1632 }
1633 Object commandO;
1634 boolean stayNull;
1635
1636 // Get the command object.
1637 commandO = action.getValue(Action.ACTION_COMMAND_KEY);
1638 if (commandO == null && (action instanceof JComponent.ActionStandin)) {
1639 // ActionStandin is used for historical reasons to support
1640 // registerKeyboardAction with a null value.
1641 stayNull = true;
1642 }
1643 else {
1644 stayNull = false;
1645 }
1646
1647 // Convert it to a string.
1648 String command;
1649
1650 if (commandO != null) {
1651 command = commandO.toString();
1652 }
1653 else if (!stayNull && event.getKeyChar() != KeyEvent.CHAR_UNDEFINED) {
1654 command = String.valueOf(event.getKeyChar());
1655 }
1656 else {
1657 // Do null for undefined chars, or if registerKeyboardAction
1658 // was called with a null.
1659 command = null;
1660 }
1661 action.actionPerformed(new ActionEvent(sender,
1662 ActionEvent.ACTION_PERFORMED, command, event.getWhen(),
1663 modifiers));
1664 return true;
1665 }
1666
1667
1668 /**
1669 * Convenience method to change the UI InputMap for <code>component</code>
1670 * to <code>uiInputMap</code>. If <code>uiInputMap</code> is {@code null},
1671 * this removes any previously installed UI InputMap.
1672 *
1673 * @since 1.3
1674 */
1675 public static void replaceUIInputMap(JComponent component, int type,
1676 InputMap uiInputMap) {
1677 InputMap map = component.getInputMap(type, (uiInputMap != null));
1678
1679 while (map != null) {
1680 InputMap parent = map.getParent();
1681 if (parent == null || (parent instanceof UIResource)) {
1682 map.setParent(uiInputMap);
1683 return;
1684 }
1685 map = parent;
1686 }
1687 }
1688
1689
1690 /**
1691 * Convenience method to change the UI ActionMap for <code>component</code>
1692 * to <code>uiActionMap</code>. If <code>uiActionMap</code> is {@code null},
1693 * this removes any previously installed UI ActionMap.
1694 *
1695 * @since 1.3
1696 */
1697 public static void replaceUIActionMap(JComponent component,
1698 ActionMap uiActionMap) {
1699 ActionMap map = component.getActionMap((uiActionMap != null));;
1700
1701 while (map != null) {
1702 ActionMap parent = map.getParent();
1703 if (parent == null || (parent instanceof UIResource)) {
1704 map.setParent(uiActionMap);
1705 return;
1706 }
1707 map = parent;
1708 }
1709 }
1710
1711
1712 /**
1713 * Returns the InputMap provided by the UI for condition
1714 * <code>condition</code> in component <code>component</code>.
1715 * <p>This will return {@code null} if the UI has not installed a InputMap
1716 * of the specified type.
1717 *
1718 * @since 1.3
1719 */
1720 public static InputMap getUIInputMap(JComponent component, int condition) {
1721 InputMap map = component.getInputMap(condition, false);
1722 while (map != null) {
1723 InputMap parent = map.getParent();
1724 if (parent instanceof UIResource) {
1725 return parent;
1726 }
1727 map = parent;
1728 }
1729 return null;
1730 }
1731
1732 /**
1733 * Returns the ActionMap provided by the UI
1734 * in component <code>component</code>.
1735 * <p>This will return {@code null} if the UI has not installed an ActionMap.
1736 *
1737 * @since 1.3
1738 */
1739 public static ActionMap getUIActionMap(JComponent component) {
1740 ActionMap map = component.getActionMap(false);
1741 while (map != null) {
1742 ActionMap parent = map.getParent();
1743 if (parent instanceof UIResource) {
1744 return parent;
1745 }
1746 map = parent;
1747 }
1748 return null;
1749 }
1750
1751
1752 // Don't use String, as it's not guaranteed to be unique in a Hashtable.
1753 private static final Object sharedOwnerFrameKey =
1754 new StringBuffer("SwingUtilities.sharedOwnerFrame");
1755
1756 static class SharedOwnerFrame extends Frame implements WindowListener {
1757 public void addNotify() {
1758 super.addNotify();
1759 installListeners();
1760 }
1761
1762 /**
1763 * Install window listeners on owned windows to watch for displayability changes
1764 */
1765 void installListeners() {
1766 Window[] windows = getOwnedWindows();
1767 for (int ind = 0; ind < windows.length; ind++){
1768 Window window = windows[ind];
1769 if (window != null) {
1770 window.removeWindowListener(this);
1771 window.addWindowListener(this);
1772 }
1773 }
1774 }
1775
1776 /**
1777 * Watches for displayability changes and disposes shared instance if there are no
1778 * displayable children left.
1779 */
1780 public void windowClosed(WindowEvent e) {
1781 synchronized(getTreeLock()) {
1782 Window[] windows = getOwnedWindows();
1783 for (int ind = 0; ind < windows.length; ind++) {
1784 Window window = windows[ind];
1785 if (window != null) {
1786 if (window.isDisplayable()) {
1787 return;
1788 }
1789 window.removeWindowListener(this);
1790 }
1791 }
1792 dispose();
1793 }
1794 }
1795 public void windowOpened(WindowEvent e) {
1796 }
1797 public void windowClosing(WindowEvent e) {
1798 }
1799 public void windowIconified(WindowEvent e) {
1800 }
1801 public void windowDeiconified(WindowEvent e) {
1802 }
1803 public void windowActivated(WindowEvent e) {
1804 }
1805 public void windowDeactivated(WindowEvent e) {
1806 }
1807
1808 public void show() {
1809 // This frame can never be shown
1810 }
1811 public void dispose() {
1812 try {
1813 getToolkit().getSystemEventQueue();
1814 super.dispose();
1815 } catch (Exception e) {
1816 // untrusted code not allowed to dispose
1817 }
1818 }
1819 }
1820
1821 /**
1822 * Returns a toolkit-private, shared, invisible Frame
1823 * to be the owner for JDialogs and JWindows created with
1824 * {@code null} owners.
1825 * @exception HeadlessException if GraphicsEnvironment.isHeadless()
1826 * returns true.
1827 * @see java.awt.GraphicsEnvironment#isHeadless
1828 */
1829 static Frame getSharedOwnerFrame() throws HeadlessException {
1830 Frame sharedOwnerFrame =
1831 (Frame)SwingUtilities.appContextGet(sharedOwnerFrameKey);
1832 if (sharedOwnerFrame == null) {
1833 sharedOwnerFrame = new SharedOwnerFrame();
1834 SwingUtilities.appContextPut(sharedOwnerFrameKey,
1835 sharedOwnerFrame);
1836 }
1837 return sharedOwnerFrame;
1838 }
1839
1840 /**
1841 * Returns a SharedOwnerFrame's shutdown listener to dispose the SharedOwnerFrame
1842 * if it has no more displayable children.
1843 * @exception HeadlessException if GraphicsEnvironment.isHeadless()
1844 * returns true.
1845 * @see java.awt.GraphicsEnvironment#isHeadless
1846 */
1847 static WindowListener getSharedOwnerFrameShutdownListener() throws HeadlessException {
1848 Frame sharedOwnerFrame = getSharedOwnerFrame();
1849 return (WindowListener)sharedOwnerFrame;
1850 }
1851
1852 /* Don't make these AppContext accessors public or protected --
1853 * since AppContext is in sun.awt in 1.2, we shouldn't expose it
1854 * even indirectly with a public API.
1855 */
1856 // REMIND(aim): phase out use of 4 methods below since they
1857 // are just private covers for AWT methods (?)
1858
1859 static Object appContextGet(Object key) {
1860 return AppContext.getAppContext().get(key);
1861 }
1862
1863 static void appContextPut(Object key, Object value) {
1864 AppContext.getAppContext().put(key, value);
1865 }
1866
1867 static void appContextRemove(Object key) {
1868 AppContext.getAppContext().remove(key);
1869 }
1870
1871
1872 static Class loadSystemClass(String className) throws ClassNotFoundException {
1873 return Class.forName(className, true, Thread.currentThread().
1874 getContextClassLoader());
1875 }
1876
1877
1878 /*
1879 * Convenience function for determining ComponentOrientation. Helps us
1880 * avoid having Munge directives throughout the code.
1881 */
1882 static boolean isLeftToRight( Component c ) {
1883 return c.getComponentOrientation().isLeftToRight();
1884 }
1885 private SwingUtilities() {
1886 throw new Error("SwingUtilities is just a container for static methods");
1887 }
1888
1889 /**
1890 * Returns true if the Icon <code>icon</code> is an instance of
1891 * ImageIcon, and the image it contains is the same as <code>image</code>.
1892 */
1893 static boolean doesIconReferenceImage(Icon icon, Image image) {
1894 Image iconImage = (icon != null && (icon instanceof ImageIcon)) ?
1895 ((ImageIcon)icon).getImage() : null;
1896 return (iconImage == image);
1897 }
1898
1899 /**
1900 * Returns index of the first occurrence of <code>mnemonic</code>
1901 * within string <code>text</code>. Matching algorithm is not
1902 * case-sensitive.
1903 *
1904 * @param text The text to search through, may be {@code null}
1905 * @param mnemonic The mnemonic to find the character for.
1906 * @return index into the string if exists, otherwise -1
1907 */
1908 static int findDisplayedMnemonicIndex(String text, int mnemonic) {
1909 if (text == null || mnemonic == '\0') {
1910 return -1;
1911 }
1912
1913 char uc = Character.toUpperCase((char)mnemonic);
1914 char lc = Character.toLowerCase((char)mnemonic);
1915
1916 int uci = text.indexOf(uc);
1917 int lci = text.indexOf(lc);
1918
1919 if (uci == -1) {
1920 return lci;
1921 } else if(lci == -1) {
1922 return uci;
1923 } else {
1924 return (lci < uci) ? lci : uci;
1925 }
1926 }
1927
1928 /**
1929 * Stores the position and size of
1930 * the inner painting area of the specified component
1931 * in <code>r</code> and returns <code>r</code>.
1932 * The position and size specify the bounds of the component,
1933 * adjusted so as not to include the border area (the insets).
1934 * This method is useful for classes
1935 * that implement painting code.
1936 *
1937 * @param c the JComponent in question; if {@code null}, this method returns {@code null}
1938 * @param r the Rectangle instance to be modified;
1939 * may be {@code null}
1940 * @return {@code null} if the Component is {@code null};
1941 * otherwise, returns the passed-in rectangle (if non-{@code null})
1942 * or a new rectangle specifying position and size information
1943 *
1944 * @since 1.4
1945 */
1946 public static Rectangle calculateInnerArea(JComponent c, Rectangle r) {
1947 if (c == null) {
1948 return null;
1949 }
1950 Rectangle rect = r;
1951 Insets insets = c.getInsets();
1952
1953 if (rect == null) {
1954 rect = new Rectangle();
1955 }
1956
1957 rect.x = insets.left;
1958 rect.y = insets.top;
1959 rect.width = c.getWidth() - insets.left - insets.right;
1960 rect.height = c.getHeight() - insets.top - insets.bottom;
1961
1962 return rect;
1963 }
1964
1965 static void updateRendererOrEditorUI(Object rendererOrEditor) {
1966 if (rendererOrEditor == null) {
1967 return;
1968 }
1969
1970 Component component = null;
1971
1972 if (rendererOrEditor instanceof Component) {
1973 component = (Component)rendererOrEditor;
1974 }
1975 if (rendererOrEditor instanceof DefaultCellEditor) {
1976 component = ((DefaultCellEditor)rendererOrEditor).getComponent();
1977 }
1978
1979 if (component != null) {
1980 SwingUtilities.updateComponentTreeUI(component);
1981 }
1982 }
1983}