Source code: gnu/java/awt/AWTUtilities.java
1 /* AWTUtilities.java -- Common utility methods for AWT and Swing.
2 Copyright (C) 2005 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
37
38 package gnu.java.awt;
39
40 import java.applet.Applet;
41 import java.awt.Component;
42 import java.awt.Container;
43 import java.awt.Font;
44 import java.awt.FontMetrics;
45 import java.awt.Insets;
46 import java.awt.Point;
47 import java.awt.Rectangle;
48 import java.awt.Toolkit;
49 import java.awt.Window;
50 import java.awt.event.MouseEvent;
51 import java.util.AbstractSequentialList;
52 import java.util.List;
53 import java.util.ListIterator;
54 import java.util.NoSuchElementException;
55 import java.util.WeakHashMap;
56 import java.lang.reflect.InvocationTargetException;
57
58 /**
59 * This class mirrors the javax.swing.SwingUtilities class. It
60 * provides commonly needed functionalities for AWT classes without
61 * the need to reference classes in the javax.swing package.
62 */
63 public class AWTUtilities
64 {
65
66 /**
67 * This List implementation wraps the Component[] returned by
68 * {@link Container#getComponents()} and iterates over the visible Components
69 * in that array. This class is used in {@link #getVisibleChildren}.
70 */
71 static class VisibleComponentList extends AbstractSequentialList
72 {
73 /**
74 * The ListIterator for this List.
75 */
76 class VisibleComponentIterator implements ListIterator
77 {
78 /** The current index in the Component[]. */
79 int index;
80
81 /** The index in the List of visible Components. */
82 int listIndex;
83
84 /**
85 * Creates a new VisibleComponentIterator that starts at the specified
86 * <code>listIndex</code>. The array of Components is searched from
87 * the beginning to find the matching array index.
88 *
89 * @param listIndex the index from where to begin iterating
90 */
91 VisibleComponentIterator(int listIndex)
92 {
93 this.listIndex = listIndex;
94 int visibleComponentsFound = 0;
95 for (index = 0; visibleComponentsFound != listIndex; index++)
96 {
97 if (components[index].isVisible())
98 visibleComponentsFound++;
99 }
100 }
101
102 /**
103 * Returns <code>true</code> if there are more visible components in the
104 * array, <code>false</code> otherwise.
105 *
106 * @return <code>true</code> if there are more visible components in the
107 * array, <code>false</code> otherwise
108 */
109 public boolean hasNext()
110 {
111 boolean hasNext = false;
112 for (int i = index; i < components.length; i++)
113 {
114 if (components[i].isVisible())
115 {
116 hasNext = true;
117 break;
118 }
119 }
120 return hasNext;
121 }
122
123 /**
124 * Returns the next visible <code>Component</code> in the List.
125 *
126 * @return the next visible <code>Component</code> in the List
127 *
128 * @throws if there is no next element
129 */
130 public Object next()
131 {
132 Object o = null;
133 for (; index < components.length; index++)
134 {
135 if (components[index].isVisible())
136 {
137 o = components[index];
138 break;
139 }
140 }
141 if (o != null)
142 {
143 index++;
144 listIndex++;
145 return o;
146 }
147 else
148 throw new NoSuchElementException();
149 }
150
151 /**
152 * Returns <code>true</code> if there are more visible components in the
153 * array in the reverse direction, <code>false</code> otherwise.
154 *
155 * @return <code>true</code> if there are more visible components in the
156 * array in the reverse direction, <code>false</code> otherwise
157 */
158 public boolean hasPrevious()
159 {
160 boolean hasPrevious = false;
161 for (int i = index - 1; i >= 0; i--)
162 {
163 if (components[i].isVisible())
164 {
165 hasPrevious = true;
166 break;
167 }
168 }
169 return hasPrevious;
170 }
171
172 /**
173 * Returns the previous visible <code>Component</code> in the List.
174 *
175 * @return the previous visible <code>Component</code> in the List
176 *
177 * @throws NoSuchElementException if there is no previous element
178 */
179 public Object previous()
180 {
181 Object o = null;
182 for (index--; index >= 0; index--)
183 {
184 if (components[index].isVisible())
185 {
186 o = components[index];
187 break;
188 }
189 }
190 if (o != null)
191 {
192 listIndex--;
193 return o;
194 }
195 else
196 throw new NoSuchElementException();
197 }
198
199 /**
200 * Returns the index of the next element in the List.
201 *
202 * @return the index of the next element in the List
203 */
204 public int nextIndex()
205 {
206 return listIndex + 1;
207 }
208
209 /**
210 * Returns the index of the previous element in the List.
211 *
212 * @return the index of the previous element in the List
213 */
214 public int previousIndex()
215 {
216 return listIndex - 1;
217 }
218
219 /**
220 * This operation is not supported because the List is immutable.
221 *
222 * @throws UnsupportedOperationException because the List is immutable
223 */
224 public void remove()
225 {
226 throw new UnsupportedOperationException
227 ("VisibleComponentList is immutable");
228 }
229
230 /**
231 * This operation is not supported because the List is immutable.
232 *
233 * @param o not used here
234 *
235 * @throws UnsupportedOperationException because the List is immutable
236 */
237 public void set(Object o)
238 {
239 throw new UnsupportedOperationException
240 ("VisibleComponentList is immutable");
241 }
242
243 /**
244 * This operation is not supported because the List is immutable.
245 *
246 * @param o not used here
247 *
248 * @throws UnsupportedOperationException because the List is immutable
249 */
250 public void add(Object o)
251 {
252 throw new UnsupportedOperationException
253 ("VisibleComponentList is immutable");
254 }
255 }
256
257 /**
258 * The components over which we iterate. Only the visible components
259 * are returned by this List.
260 */
261 Component[] components;
262
263 /**
264 * Creates a new instance of VisibleComponentList that wraps the specified
265 * <code>Component[]</code>.
266 *
267 * @param c the <code>Component[]</code> to be wrapped.
268 */
269 VisibleComponentList(Component[] c)
270 {
271 components = c;
272 }
273
274 /**
275 * Returns a {@link ListIterator} for iterating over this List.
276 *
277 * @return a {@link ListIterator} for iterating over this List
278 */
279 public ListIterator listIterator(int index)
280 {
281 return new VisibleComponentIterator(index);
282 }
283
284 /**
285 * Returns the number of visible components in the wrapped Component[].
286 *
287 * @return the number of visible components
288 */
289 public int size()
290 {
291 int visibleComponents = 0;
292 for (int i = 0; i < components.length; i++)
293 if (components[i].isVisible())
294 visibleComponents++;
295 return visibleComponents;
296 }
297 }
298
299 /**
300 * The cache for our List instances. We try to hold one instance of
301 * VisibleComponentList for each Component[] that is requested. Note
302 * that we use a WeakHashMap for caching, so that the cache itself
303 * does not keep the array or the List from beeing garbage collected
304 * if no other objects hold references to it.
305 */
306 static WeakHashMap visibleChildrenCache = new WeakHashMap();
307
308 /**
309 * Returns the visible children of a {@link Container}. This method is
310 * commonly needed in LayoutManagers, because they only have to layout
311 * the visible children of a Container.
312 *
313 * @param c the Container from which to extract the visible children
314 *
315 * @return the visible children of <code>c</code>
316 */
317 public static List getVisibleChildren(Container c)
318 {
319 Component[] children = c.getComponents();
320 Object o = visibleChildrenCache.get(children);
321 VisibleComponentList visibleChildren = null;
322 if (o == null)
323 {
324 visibleChildren = new VisibleComponentList(children);
325 visibleChildrenCache.put(children, visibleChildren);
326 }
327 else
328 visibleChildren = (VisibleComponentList) o;
329
330 return visibleChildren;
331 }
332
333 /**
334 * Calculates the portion of the base rectangle which is inside the
335 * insets.
336 *
337 * @param base The rectangle to apply the insets to
338 * @param insets The insets to apply to the base rectangle
339 * @param ret A rectangle to use for storing the return value, or
340 * <code>null</code>
341 *
342 * @return The calculated area inside the base rectangle and its insets,
343 * either stored in ret or a new Rectangle if ret is <code>null</code>
344 *
345 * @see #calculateInnerArea
346 */
347 public static Rectangle calculateInsetArea(Rectangle base, Insets insets,
348 Rectangle ret)
349 {
350 if (ret == null)
351 ret = new Rectangle();
352 ret.setBounds(base.x + insets.left, base.y + insets.top,
353 base.width - (insets.left + insets.right),
354 base.height - (insets.top + insets.bottom));
355 return ret;
356 }
357
358 /**
359 * Calculates the bounds of a component in the component's own coordinate
360 * space. The result has the same height and width as the component's
361 * bounds, but its location is set to (0,0).
362 *
363 * @param aComponent The component to measure
364 *
365 * @return The component's bounds in its local coordinate space
366 */
367 public static Rectangle getLocalBounds(Component aComponent)
368 {
369 Rectangle bounds = aComponent.getBounds();
370 return new Rectangle(0, 0, bounds.width, bounds.height);
371 }
372
373 /**
374 * Returns the font metrics object for a given font. The metrics can be
375 * used to calculate crude bounding boxes and positioning information,
376 * for laying out components with textual elements.
377 *
378 * @param font The font to get metrics for
379 *
380 * @return The font's metrics
381 *
382 * @see java.awt.font.GlyphMetrics
383 */
384 public static FontMetrics getFontMetrics(Font font)
385 {
386 return Toolkit.getDefaultToolkit().getFontMetrics(font);
387 }
388
389 /**
390 * Returns the least ancestor of <code>comp</code> which has the
391 * specified name.
392 *
393 * @param name The name to search for
394 * @param comp The component to search the ancestors of
395 *
396 * @return The nearest ancestor of <code>comp</code> with the given
397 * name, or <code>null</code> if no such ancestor exists
398 *
399 * @see java.awt.Component#getName
400 * @see #getAncestorOfClass
401 */
402 public static Container getAncestorNamed(String name, Component comp)
403 {
404 while (comp != null && (comp.getName() != name))
405 comp = comp.getParent();
406 return (Container) comp;
407 }
408
409 /**
410 * Returns the least ancestor of <code>comp</code> which is an instance
411 * of the specified class.
412 *
413 * @param c The class to search for
414 * @param comp The component to search the ancestors of
415 *
416 * @return The nearest ancestor of <code>comp</code> which is an instance
417 * of the given class, or <code>null</code> if no such ancestor exists
418 *
419 * @see #getAncestorOfClass
420 * @see #windowForComponent
421 * @see
422 *
423 */
424 public static Container getAncestorOfClass(Class c, Component comp)
425 {
426 while (comp != null && (! c.isInstance(comp)))
427 comp = comp.getParent();
428 return (Container) comp;
429 }
430
431 /**
432 * Equivalent to calling <code>getAncestorOfClass(Window, comp)</code>.
433 *
434 * @param comp The component to search for an ancestor window
435 *
436 * @return An ancestral window, or <code>null</code> if none exists
437 */
438 public static Window windowForComponent(Component comp)
439 {
440 return (Window) getAncestorOfClass(Window.class, comp);
441 }
442
443 /**
444 * Returns the "root" of the component tree containint <code>comp</code>
445 * The root is defined as either the <em>least</em> ancestor of
446 * <code>comp</code> which is a {@link Window}, or the <em>greatest</em>
447 * ancestor of <code>comp</code> which is a {@link Applet} if no {@link
448 * Window} ancestors are found.
449 *
450 * @param comp The component to search for a root
451 *
452 * @return The root of the component's tree, or <code>null</code>
453 */
454 public static Component getRoot(Component comp)
455 {
456 Applet app = null;
457 Window win = null;
458
459 while (comp != null)
460 {
461 if (win == null && comp instanceof Window)
462 win = (Window) comp;
463 else if (comp instanceof Applet)
464 app = (Applet) comp;
465 comp = comp.getParent();
466 }
467
468 if (win != null)
469 return win;
470 else
471 return app;
472 }
473
474 /**
475 * Return true if a descends from b, in other words if b is an
476 * ancestor of a.
477 *
478 * @param a The child to search the ancestry of
479 * @param b The potential ancestor to search for
480 *
481 * @return true if a is a descendent of b, false otherwise
482 */
483 public static boolean isDescendingFrom(Component a, Component b)
484 {
485 while (true)
486 {
487 if (a == null || b == null)
488 return false;
489 if (a == b)
490 return true;
491 a = a.getParent();
492 }
493 }
494
495 /**
496 * Returns the deepest descendent of parent which is both visible and
497 * contains the point <code>(x,y)</code>. Returns parent when either
498 * parent is not a container, or has no children which contain
499 * <code>(x,y)</code>. Returns <code>null</code> when either
500 * <code>(x,y)</code> is outside the bounds of parent, or parent is
501 * <code>null</code>.
502 *
503 * @param parent The component to search the descendents of
504 * @param x Horizontal coordinate to search for
505 * @param y Vertical coordinate to search for
506 *
507 * @return A component containing <code>(x,y)</code>, or
508 * <code>null</code>
509 *
510 * @see java.awt.Container#findComponentAt
511 */
512 public static Component getDeepestComponentAt(Component parent, int x, int y)
513 {
514 if (parent == null || (! parent.contains(x, y)))
515 return null;
516
517 if (! (parent instanceof Container))
518 return parent;
519
520 Container c = (Container) parent;
521 return c.findComponentAt(x, y);
522 }
523
524 /**
525 * Converts a point from a component's local coordinate space to "screen"
526 * coordinates (such as the coordinate space mouse events are delivered
527 * in). This operation is equivalent to translating the point by the
528 * location of the component (which is the origin of its coordinate
529 * space).
530 *
531 * @param p The point to convert
532 * @param c The component which the point is expressed in terms of
533 *
534 * @see convertPointFromScreen
535 */
536 public static void convertPointToScreen(Point p, Component c)
537 {
538 Point c0 = c.getLocationOnScreen();
539 p.translate(c0.x, c0.y);
540 }
541
542 /**
543 * Converts a point from "screen" coordinates (such as the coordinate
544 * space mouse events are delivered in) to a component's local coordinate
545 * space. This operation is equivalent to translating the point by the
546 * negation of the component's location (which is the origin of its
547 * coordinate space).
548 *
549 * @param p The point to convert
550 * @param c The component which the point should be expressed in terms of
551 */
552 public static void convertPointFromScreen(Point p, Component c)
553 {
554 Point c0 = c.getLocationOnScreen();
555 p.translate(-c0.x, -c0.y);
556 }
557
558 /**
559 * Converts a point <code>(x,y)</code> from the coordinate space of one
560 * component to another. This is equivalent to converting the point from
561 * <code>source</code> space to screen space, then back from screen space
562 * to <code>destination</code> space. If exactly one of the two
563 * Components is <code>null</code>, it is taken to refer to the root
564 * ancestor of the other component. If both are <code>null</code>, no
565 * transformation is done.
566 *
567 * @param source The component which the point is expressed in terms of
568 * @param x Horizontal coordinate of point to transform
569 * @param y Vertical coordinate of point to transform
570 * @param destination The component which the return value will be
571 * expressed in terms of
572 *
573 * @return The point <code>(x,y)</code> converted from the coordinate
574 * space of the
575 * source component to the coordinate space of the destination component
576 *
577 * @see #convertPointToScreen
578 * @see #convertPointFromScreen
579 * @see #convertRectangle
580 * @see #getRoot
581 */
582 public static Point convertPoint(Component source, int x, int y,
583 Component destination)
584 {
585 Point pt = new Point(x, y);
586
587 if (source == null && destination == null)
588 return pt;
589
590 if (source == null)
591 source = getRoot(destination);
592
593 if (destination == null)
594 destination = getRoot(source);
595
596 convertPointToScreen(pt, source);
597 convertPointFromScreen(pt, destination);
598
599 return pt;
600 }
601
602
603 /**
604 * Converts a rectangle from the coordinate space of one component to
605 * another. This is equivalent to converting the rectangle from
606 * <code>source</code> space to screen space, then back from screen space
607 * to <code>destination</code> space. If exactly one of the two
608 * Components is <code>null</code>, it is taken to refer to the root
609 * ancestor of the other component. If both are <code>null</code>, no
610 * transformation is done.
611 *
612 * @param source The component which the rectangle is expressed in terms of
613 * @param rect The rectangle to convert
614 * @param destination The component which the return value will be
615 * expressed in terms of
616 *
617 * @return A new rectangle, equal in size to the input rectangle, but
618 * with its position converted from the coordinate space of the source
619 * component to the coordinate space of the destination component
620 *
621 * @see #convertPointToScreen
622 * @see #convertPointFromScreen
623 * @see #convertPoint
624 * @see #getRoot
625 */
626 public static Rectangle convertRectangle(Component source, Rectangle rect,
627 Component destination)
628 {
629 Point pt = convertPoint(source, rect.x, rect.y, destination);
630 return new Rectangle(pt.x, pt.y, rect.width, rect.height);
631 }
632
633 /**
634 * Convert a mouse event which refrers to one component to another. This
635 * includes changing the mouse event's coordinate space, as well as the
636 * source property of the event. If <code>source</code> is
637 * <code>null</code>, it is taken to refer to <code>destination</code>'s
638 * root component. If <code>destination</code> is <code>null</code>, the
639 * new event will remain expressed in <code>source</code>'s coordinate
640 * system.
641 *
642 * @param source The component the mouse event currently refers to
643 * @param sourceEvent The mouse event to convert
644 * @param destination The component the new mouse event should refer to
645 *
646 * @return A new mouse event expressed in terms of the destination
647 * component's coordinate space, and with the destination component as
648 * its source
649 *
650 * @see #convertPoint
651 */
652 public static MouseEvent convertMouseEvent(Component source,
653 MouseEvent sourceEvent,
654 Component destination)
655 {
656 Point newpt = convertPoint(source, sourceEvent.getX(), sourceEvent.getY(),
657 destination);
658
659 return new MouseEvent(destination, sourceEvent.getID(),
660 sourceEvent.getWhen(), sourceEvent.getModifiers(),
661 newpt.x, newpt.y, sourceEvent.getClickCount(),
662 sourceEvent.isPopupTrigger(),
663 sourceEvent.getButton());
664 }
665
666
667 /**
668 * Calls {@link java.awt.EventQueue.invokeLater} with the
669 * specified {@link Runnable}.
670 */
671 public static void invokeLater(Runnable doRun)
672 {
673 java.awt.EventQueue.invokeLater(doRun);
674 }
675
676 /**
677 * Calls {@link java.awt.EventQueue.invokeAndWait} with the
678 * specified {@link Runnable}.
679 */
680 public static void invokeAndWait(Runnable doRun)
681 throws InterruptedException,
682 InvocationTargetException
683 {
684 java.awt.EventQueue.invokeAndWait(doRun);
685 }
686
687 /**
688 * Calls {@link java.awt.EventQueue.isEventDispatchThread}.
689 */
690 public static boolean isEventDispatchThread()
691 {
692 return java.awt.EventQueue.isDispatchThread();
693 }
694 }