Source code: java/awt/Container.java
1 /* Container.java -- parent container class in AWT
2 Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005 Free Software Foundation
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
39 package java.awt;
40
41 import java.awt.event.ContainerEvent;
42 import java.awt.event.ContainerListener;
43 import java.awt.event.KeyEvent;
44 import java.awt.event.MouseEvent;
45 import java.awt.peer.ComponentPeer;
46 import java.awt.peer.ContainerPeer;
47 import java.awt.peer.LightweightPeer;
48 import java.beans.PropertyChangeListener;
49 import java.beans.PropertyChangeSupport;
50 import java.io.IOException;
51 import java.io.ObjectInputStream;
52 import java.io.ObjectOutputStream;
53 import java.io.PrintStream;
54 import java.io.PrintWriter;
55 import java.io.Serializable;
56 import java.util.Collections;
57 import java.util.EventListener;
58 import java.util.HashSet;
59 import java.util.Iterator;
60 import java.util.Set;
61
62 import javax.accessibility.Accessible;
63
64 import gnu.java.awt.AWTUtilities;
65
66 /**
67 * A generic window toolkit object that acts as a container for other objects.
68 * Components are tracked in a list, and new elements are at the end of the
69 * list or bottom of the stacking order.
70 *
71 * @author original author unknown
72 * @author Eric Blake (ebb9@email.byu.edu)
73 *
74 * @since 1.0
75 *
76 * @status still missing 1.4 support
77 */
78 public class Container extends Component
79 {
80 /**
81 * Compatible with JDK 1.0+.
82 */
83 private static final long serialVersionUID = 4613797578919906343L;
84
85 /* Serialized fields from the serialization spec. */
86 int ncomponents;
87 Component[] component;
88 LayoutManager layoutMgr;
89
90 LightweightDispatcher dispatcher;
91
92 Dimension maxSize;
93
94 /**
95 * @since 1.4
96 */
97 boolean focusCycleRoot;
98
99 int containerSerializedDataVersion;
100
101 /* Anything else is non-serializable, and should be declared "transient". */
102 transient ContainerListener containerListener;
103 transient PropertyChangeSupport changeSupport;
104
105 /** The focus traversal policy that determines how focus is
106 transferred between this Container and its children. */
107 private FocusTraversalPolicy focusTraversalPolicy;
108
109 /**
110 * The focus traversal keys, if not inherited from the parent or default
111 * keyboard manager. These sets will contain only AWTKeyStrokes that
112 * represent press and release events to use as focus control.
113 *
114 * @see #getFocusTraversalKeys(int)
115 * @see #setFocusTraversalKeys(int, Set)
116 * @since 1.4
117 */
118 transient Set[] focusTraversalKeys;
119
120 /**
121 * Default constructor for subclasses.
122 */
123 public Container()
124 {
125 }
126
127 /**
128 * Returns the number of components in this container.
129 *
130 * @return The number of components in this container.
131 */
132 public int getComponentCount()
133 {
134 return countComponents ();
135 }
136
137 /**
138 * Returns the number of components in this container.
139 *
140 * @return The number of components in this container.
141 *
142 * @deprecated use {@link #getComponentCount()} instead
143 */
144 public int countComponents()
145 {
146 return ncomponents;
147 }
148
149 /**
150 * Returns the component at the specified index.
151 *
152 * @param n The index of the component to retrieve.
153 *
154 * @return The requested component.
155 *
156 * @throws ArrayIndexOutOfBoundsException If the specified index is invalid
157 */
158 public Component getComponent(int n)
159 {
160 synchronized (getTreeLock ())
161 {
162 if (n < 0 || n >= ncomponents)
163 throw new ArrayIndexOutOfBoundsException("no such component");
164
165 return component[n];
166 }
167 }
168
169 /**
170 * Returns an array of the components in this container.
171 *
172 * @return The components in this container.
173 */
174 public Component[] getComponents()
175 {
176 synchronized (getTreeLock ())
177 {
178 Component[] result = new Component[ncomponents];
179
180 if (ncomponents > 0)
181 System.arraycopy(component, 0, result, 0, ncomponents);
182
183 return result;
184 }
185 }
186
187 /**
188 * Swaps the components at position i and j, in the container.
189 */
190
191 protected void swapComponents (int i, int j)
192 {
193 synchronized (getTreeLock ())
194 {
195 if (i < 0
196 || i >= component.length
197 || j < 0
198 || j >= component.length)
199 throw new ArrayIndexOutOfBoundsException ();
200 Component tmp = component[i];
201 component[i] = component[j];
202 component[j] = tmp;
203 }
204 }
205
206 /**
207 * Returns the insets for this container, which is the space used for
208 * borders, the margin, etc.
209 *
210 * @return The insets for this container.
211 */
212 public Insets getInsets()
213 {
214 return insets ();
215 }
216
217 /**
218 * Returns the insets for this container, which is the space used for
219 * borders, the margin, etc.
220 *
221 * @return The insets for this container.
222 * @deprecated use {@link #getInsets()} instead
223 */
224 public Insets insets()
225 {
226 if (peer == null)
227 return new Insets (0, 0, 0, 0);
228
229 return ((ContainerPeer) peer).getInsets ();
230 }
231
232 /**
233 * Adds the specified component to this container at the end of the
234 * component list.
235 *
236 * @param comp The component to add to the container.
237 *
238 * @return The same component that was added.
239 */
240 public Component add(Component comp)
241 {
242 addImpl(comp, null, -1);
243 return comp;
244 }
245
246 /**
247 * Adds the specified component to the container at the end of the
248 * component list. This method should not be used. Instead, use
249 * <code>add(Component, Object)</code>.
250 *
251 * @param name The name of the component to be added.
252 * @param comp The component to be added.
253 *
254 * @return The same component that was added.
255 *
256 * @see #add(Component,Object)
257 */
258 public Component add(String name, Component comp)
259 {
260 addImpl(comp, name, -1);
261 return comp;
262 }
263
264 /**
265 * Adds the specified component to this container at the specified index
266 * in the component list.
267 *
268 * @param comp The component to be added.
269 * @param index The index in the component list to insert this child
270 * at, or -1 to add at the end of the list.
271 *
272 * @return The same component that was added.
273 *
274 * @throws ArrayIndexOutOfBoundsException If the specified index is invalid.
275 */
276 public Component add(Component comp, int index)
277 {
278 addImpl(comp, null, index);
279 return comp;
280 }
281
282 /**
283 * Adds the specified component to this container at the end of the
284 * component list. The layout manager will use the specified constraints
285 * when laying out this component.
286 *
287 * @param comp The component to be added to this container.
288 * @param constraints The layout constraints for this component.
289 */
290 public void add(Component comp, Object constraints)
291 {
292 addImpl(comp, constraints, -1);
293 }
294
295 /**
296 * Adds the specified component to this container at the specified index
297 * in the component list. The layout manager will use the specified
298 * constraints when layout out this component.
299 *
300 * @param comp The component to be added.
301 * @param constraints The layout constraints for this component.
302 * @param index The index in the component list to insert this child
303 * at, or -1 to add at the end of the list.
304 *
305 * @throws ArrayIndexOutOfBoundsException If the specified index is invalid.
306 */
307 public void add(Component comp, Object constraints, int index)
308 {
309 addImpl(comp, constraints, index);
310 }
311
312 /**
313 * This method is called by all the <code>add()</code> methods to perform
314 * the actual adding of the component. Subclasses who wish to perform
315 * their own processing when a component is added should override this
316 * method. Any subclass doing this must call the superclass version of
317 * this method in order to ensure proper functioning of the container.
318 *
319 * @param comp The component to be added.
320 * @param constraints The layout constraints for this component, or
321 * <code>null</code> if there are no constraints.
322 * @param index The index in the component list to insert this child
323 * at, or -1 to add at the end of the list.
324 *
325 * @throws ArrayIndexOutOfBoundsException If the specified index is invalid.
326 */
327 protected void addImpl(Component comp, Object constraints, int index)
328 {
329 synchronized (getTreeLock ())
330 {
331 if (index > ncomponents
332 || (index < 0 && index != -1)
333 || comp instanceof Window
334 || (comp instanceof Container
335 && ((Container) comp).isAncestorOf(this)))
336 throw new IllegalArgumentException();
337
338 // Reparent component, and make sure component is instantiated if
339 // we are.
340 if (comp.parent != null)
341 comp.parent.remove(comp);
342 comp.parent = this;
343
344 if (peer != null)
345 {
346 // Notify the component that it has a new parent.
347 comp.addNotify();
348
349 if (comp.isLightweight ())
350 {
351 enableEvents (comp.eventMask);
352 if (!isLightweight ())
353 enableEvents (AWTEvent.PAINT_EVENT_MASK);
354 }
355 }
356
357 // Invalidate the layout of the added component and its ancestors.
358 comp.invalidate();
359
360 if (component == null)
361 component = new Component[4]; // FIXME, better initial size?
362
363 // This isn't the most efficient implementation. We could do less
364 // copying when growing the array. It probably doesn't matter.
365 if (ncomponents >= component.length)
366 {
367 int nl = component.length * 2;
368 Component[] c = new Component[nl];
369 System.arraycopy(component, 0, c, 0, ncomponents);
370 component = c;
371 }
372
373 if (index == -1)
374 component[ncomponents++] = comp;
375 else
376 {
377 System.arraycopy(component, index, component, index + 1,
378 ncomponents - index);
379 component[index] = comp;
380 ++ncomponents;
381 }
382
383 // Notify the layout manager.
384 if (layoutMgr != null)
385 {
386 if (layoutMgr instanceof LayoutManager2)
387 {
388 LayoutManager2 lm2 = (LayoutManager2) layoutMgr;
389 lm2.addLayoutComponent(comp, constraints);
390 }
391 else if (constraints instanceof String)
392 layoutMgr.addLayoutComponent((String) constraints, comp);
393 else
394 layoutMgr.addLayoutComponent(null, comp);
395 }
396
397 if (isShowing ())
398 {
399 // Post event to notify of adding the component.
400 ContainerEvent ce = new ContainerEvent(this,
401 ContainerEvent.COMPONENT_ADDED,
402 comp);
403 getToolkit().getSystemEventQueue().postEvent(ce);
404
405 // Repaint this container.
406 repaint();
407 }
408 }
409 }
410
411 /**
412 * Removes the component at the specified index from this container.
413 *
414 * @param index The index of the component to remove.
415 */
416 public void remove(int index)
417 {
418 synchronized (getTreeLock ())
419 {
420 Component r = component[index];
421
422 r.removeNotify();
423
424 System.arraycopy(component, index + 1, component, index,
425 ncomponents - index - 1);
426 component[--ncomponents] = null;
427
428 invalidate();
429
430 if (layoutMgr != null)
431 layoutMgr.removeLayoutComponent(r);
432
433 r.parent = null;
434
435 if (isShowing ())
436 {
437 // Post event to notify of removing the component.
438 ContainerEvent ce = new ContainerEvent(this,
439 ContainerEvent.COMPONENT_REMOVED,
440 r);
441 getToolkit().getSystemEventQueue().postEvent(ce);
442
443 // Repaint this container.
444 repaint();
445 }
446 }
447 }
448
449 /**
450 * Removes the specified component from this container.
451 *
452 * @param comp The component to remove from this container.
453 */
454 public void remove(Component comp)
455 {
456 synchronized (getTreeLock ())
457 {
458 for (int i = 0; i < ncomponents; ++i)
459 {
460 if (component[i] == comp)
461 {
462 remove(i);
463 break;
464 }
465 }
466 }
467 }
468
469 /**
470 * Removes all components from this container.
471 */
472 public void removeAll()
473 {
474 synchronized (getTreeLock ())
475 {
476 while (ncomponents > 0)
477 remove(0);
478 }
479 }
480
481 /**
482 * Returns the current layout manager for this container.
483 *
484 * @return The layout manager for this container.
485 */
486 public LayoutManager getLayout()
487 {
488 return layoutMgr;
489 }
490
491 /**
492 * Sets the layout manager for this container to the specified layout
493 * manager.
494 *
495 * @param mgr The new layout manager for this container.
496 */
497 public void setLayout(LayoutManager mgr)
498 {
499 layoutMgr = mgr;
500 invalidate();
501 }
502
503 /**
504 * Layout the components in this container.
505 */
506 public void doLayout()
507 {
508 layout ();
509 }
510
511 /**
512 * Layout the components in this container.
513 *
514 * @deprecated use {@link #doLayout()} instead
515 */
516 public void layout()
517 {
518 if (layoutMgr != null)
519 layoutMgr.layoutContainer (this);
520 }
521
522 /**
523 * Invalidates this container to indicate that it (and all parent
524 * containers) need to be laid out.
525 */
526 public void invalidate()
527 {
528 super.invalidate();
529 if (layoutMgr != null && layoutMgr instanceof LayoutManager2)
530 {
531 LayoutManager2 lm2 = (LayoutManager2) layoutMgr;
532 lm2.invalidateLayout(this);
533 }
534 }
535
536 /**
537 * Re-lays out the components in this container.
538 */
539 public void validate()
540 {
541 synchronized (getTreeLock ())
542 {
543 if (! isValid() && peer != null)
544 {
545 validateTree();
546 }
547 }
548 }
549
550 /**
551 * Recursively invalidates the container tree.
552 */
553 void invalidateTree()
554 {
555 super.invalidate(); // Clean cached layout state.
556 for (int i = 0; i < ncomponents; i++)
557 {
558 Component comp = component[i];
559 comp.invalidate();
560 if (comp instanceof Container)
561 ((Container) comp).invalidateTree();
562 }
563
564 if (layoutMgr != null && layoutMgr instanceof LayoutManager2)
565 {
566 LayoutManager2 lm2 = (LayoutManager2) layoutMgr;
567 lm2.invalidateLayout(this);
568 }
569 }
570
571 /**
572 * Recursively validates the container tree, recomputing any invalid
573 * layouts.
574 */
575 protected void validateTree()
576 {
577 if (valid)
578 return;
579
580 ContainerPeer cPeer = null;
581 if (peer != null && ! (peer instanceof LightweightPeer))
582 {
583 cPeer = (ContainerPeer) peer;
584 cPeer.beginValidate();
585 }
586
587 for (int i = 0; i < ncomponents; ++i)
588 {
589 Component comp = component[i];
590
591 if (comp.getPeer () == null)
592 comp.addNotify();
593 }
594
595 doLayout ();
596 for (int i = 0; i < ncomponents; ++i)
597 {
598 Component comp = component[i];
599
600 if (! comp.isValid())
601 {
602 if (comp instanceof Container)
603 {
604 ((Container) comp).validateTree();
605 }
606 else
607 {
608 component[i].validate();
609 }
610 }
611 }
612
613 /* children will call invalidate() when they are layed out. It
614 is therefore important that valid is not set to true
615 until after the children have been layed out. */
616 valid = true;
617
618 if (cPeer != null)
619 cPeer.endValidate();
620 }
621
622 public void setFont(Font f)
623 {
624 if( (f != null && (font == null || !font.equals(f)))
625 || f == null)
626 {
627 super.setFont(f);
628 // FIXME: Although it might make more sense to invalidate only
629 // those children whose font == null, Sun invalidates all children.
630 // So we'll do the same.
631 invalidateTree();
632 }
633 }
634
635 /**
636 * Returns the preferred size of this container.
637 *
638 * @return The preferred size of this container.
639 */
640 public Dimension getPreferredSize()
641 {
642 return preferredSize ();
643 }
644
645 /**
646 * Returns the preferred size of this container.
647 *
648 * @return The preferred size of this container.
649 *
650 * @deprecated use {@link #getPreferredSize()} instead
651 */
652 public Dimension preferredSize()
653 {
654 synchronized(treeLock)
655 {
656 if(valid && prefSize != null)
657 return new Dimension(prefSize);
658 LayoutManager layout = getLayout();
659 if (layout != null)
660 {
661 Dimension layoutSize = layout.preferredLayoutSize(this);
662 if(valid)
663 prefSize = layoutSize;
664 return new Dimension(layoutSize);
665 }
666 else
667 return super.preferredSize ();
668 }
669 }
670
671 /**
672 * Returns the minimum size of this container.
673 *
674 * @return The minimum size of this container.
675 */
676 public Dimension getMinimumSize()
677 {
678 return minimumSize ();
679 }
680
681 /**
682 * Returns the minimum size of this container.
683 *
684 * @return The minimum size of this container.
685 *
686 * @deprecated use {@link #getMinimumSize()} instead
687 */
688 public Dimension minimumSize()
689 {
690 if(valid && minSize != null)
691 return new Dimension(minSize);
692
693 LayoutManager layout = getLayout();
694 if (layout != null)
695 {
696 minSize = layout.minimumLayoutSize (this);
697 return minSize;
698 }
699 else
700 return super.minimumSize ();
701 }
702
703 /**
704 * Returns the maximum size of this container.
705 *
706 * @return The maximum size of this container.
707 */
708 public Dimension getMaximumSize()
709 {
710 if (valid && maxSize != null)
711 return new Dimension(maxSize);
712
713 LayoutManager layout = getLayout();
714 if (layout != null && layout instanceof LayoutManager2)
715 {
716 LayoutManager2 lm2 = (LayoutManager2) layout;
717 maxSize = lm2.maximumLayoutSize(this);
718 return maxSize;
719 }
720 else
721 return super.getMaximumSize();
722 }
723
724 /**
725 * Returns the preferred alignment along the X axis. This is a value
726 * between 0 and 1 where 0 represents alignment flush left and
727 * 1 means alignment flush right, and 0.5 means centered.
728 *
729 * @return The preferred alignment along the X axis.
730 */
731 public float getAlignmentX()
732 {
733 return super.getAlignmentX();
734 }
735
736 /**
737 * Returns the preferred alignment along the Y axis. This is a value
738 * between 0 and 1 where 0 represents alignment flush top and
739 * 1 means alignment flush bottom, and 0.5 means centered.
740 *
741 * @return The preferred alignment along the Y axis.
742 */
743 public float getAlignmentY()
744 {
745 return super.getAlignmentY();
746 }
747
748 /**
749 * Paints this container. The implementation of this method in this
750 * class forwards to any lightweight components in this container. If
751 * this method is subclassed, this method should still be invoked as
752 * a superclass method so that lightweight components are properly
753 * drawn.
754 *
755 * @param g The graphics context for this paint job.
756 */
757 public void paint(Graphics g)
758 {
759 if (!isShowing())
760 return;
761
762 // Visit heavyweights as well, in case they were
763 // erased when we cleared the background for this container.
764 visitChildren(g, GfxPaintVisitor.INSTANCE, false);
765 }
766
767 /**
768 * Updates this container. The implementation of this method in this
769 * class forwards to any lightweight components in this container. If
770 * this method is subclassed, this method should still be invoked as
771 * a superclass method so that lightweight components are properly
772 * drawn.
773 *
774 * @param g The graphics context for this update.
775 *
776 * @specnote The specification suggests that this method forwards the
777 * update() call to all its lightweight children. Tests show
778 * that this is not done either in the JDK. The exact behaviour
779 * seems to be that the background is cleared in heavyweight
780 * Containers, and all other containers
781 * directly call paint(), causing the (lightweight) children to
782 * be painted.
783 */
784 public void update(Graphics g)
785 {
786 // It seems that the JDK clears the background of containers like Panel
787 // and Window (within this method) but not of 'plain' Containers or
788 // JComponents. This could
789 // lead to the assumption that it only clears heavyweight containers.
790 // However that is not quite true. In a test with a custom Container
791 // that overrides isLightweight() to return false, the background is
792 // also not cleared. So we do a check on !(peer instanceof LightweightPeer)
793 // instead.
794 ComponentPeer p = peer;
795 if (p != null && !(p instanceof LightweightPeer))
796 g.clearRect(0, 0, getWidth(), getHeight());
797
798 paint(g);
799 }
800
801 /**
802 * Prints this container. The implementation of this method in this
803 * class forwards to any lightweight components in this container. If
804 * this method is subclassed, this method should still be invoked as
805 * a superclass method so that lightweight components are properly
806 * drawn.
807 *
808 * @param g The graphics context for this print job.
809 */
810 public void print(Graphics g)
811 {
812 super.print(g);
813 visitChildren(g, GfxPrintVisitor.INSTANCE, true);
814 }
815
816 /**
817 * Paints all of the components in this container.
818 *
819 * @param g The graphics context for this paint job.
820 */
821 public void paintComponents(Graphics g)
822 {
823 super.paint(g);
824 visitChildren(g, GfxPaintAllVisitor.INSTANCE, true);
825 }
826
827 /**
828 * Prints all of the components in this container.
829 *
830 * @param g The graphics context for this print job.
831 */
832 public void printComponents(Graphics g)
833 {
834 super.paint(g);
835 visitChildren(g, GfxPrintAllVisitor.INSTANCE, true);
836 }
837
838 /**
839 * Adds the specified container listener to this object's list of
840 * container listeners.
841 *
842 * @param listener The listener to add.
843 */
844 public synchronized void addContainerListener(ContainerListener listener)
845 {
846 containerListener = AWTEventMulticaster.add(containerListener, listener);
847 }
848
849 /**
850 * Removes the specified container listener from this object's list of
851 * container listeners.
852 *
853 * @param listener The listener to remove.
854 */
855 public synchronized void removeContainerListener(ContainerListener listener)
856 {
857 containerListener = AWTEventMulticaster.remove(containerListener, listener);
858 }
859
860 /**
861 * @since 1.4
862 */
863 public synchronized ContainerListener[] getContainerListeners()
864 {
865 return (ContainerListener[])
866 AWTEventMulticaster.getListeners(containerListener,
867 ContainerListener.class);
868 }
869
870 /**
871 * Returns an array of all the objects currently registered as FooListeners
872 * upon this Container. FooListeners are registered using the addFooListener
873 * method.
874 *
875 * @exception ClassCastException If listenerType doesn't specify a class or
876 * interface that implements @see java.util.EventListener.
877 *
878 * @since 1.3
879 */
880 public EventListener[] getListeners(Class listenerType)
881 {
882 if (listenerType == ContainerListener.class)
883 return getContainerListeners();
884 return super.getListeners(listenerType);
885 }
886
887 /**
888 * Processes the specified event. This method calls
889 * <code>processContainerEvent()</code> if this method is a
890 * <code>ContainerEvent</code>, otherwise it calls the superclass
891 * method.
892 *
893 * @param e The event to be processed.
894 */
895 protected void processEvent(AWTEvent e)
896 {
897 if (e instanceof ContainerEvent)
898 processContainerEvent((ContainerEvent) e);
899 else
900 super.processEvent(e);
901 }
902
903 /**
904 * Called when a container event occurs if container events are enabled.
905 * This method calls any registered listeners.
906 *
907 * @param e The event that occurred.
908 */
909 protected void processContainerEvent(ContainerEvent e)
910 {
911 if (containerListener == null)
912 return;
913 switch (e.id)
914 {
915 case ContainerEvent.COMPONENT_ADDED:
916 containerListener.componentAdded(e);
917 break;
918
919 case ContainerEvent.COMPONENT_REMOVED:
920 containerListener.componentRemoved(e);
921 break;
922 }
923 }
924
925 /**
926 * AWT 1.0 event processor.
927 *
928 * @param e The event that occurred.
929 *
930 * @deprecated use {@link #dispatchEvent(AWTEvent)} instead
931 */
932 public void deliverEvent(Event e)
933 {
934 if (!handleEvent (e))
935 {
936 synchronized (getTreeLock ())
937 {
938 Component parent = getParent ();
939
940 if (parent != null)
941 parent.deliverEvent (e);
942 }
943 }
944 }
945
946 /**
947 * Returns the component located at the specified point. This is done
948 * by checking whether or not a child component claims to contain this
949 * point. The first child component that does is returned. If no
950 * child component claims the point, the container itself is returned,
951 * unless the point does not exist within this container, in which
952 * case <code>null</code> is returned.
953 *
954 * @param x The X coordinate of the point.
955 * @param y The Y coordinate of the point.
956 *
957 * @return The component containing the specified point, or
958 * <code>null</code> if there is no such point.
959 */
960 public Component getComponentAt(int x, int y)
961 {
962 return locate (x, y);
963 }
964
965 /**
966 * Returns the component located at the specified point. This is done
967 * by checking whether or not a child component claims to contain this
968 * point. The first child component that does is returned. If no
969 * child component claims the point, the container itself is returned,
970 * unless the point does not exist within this container, in which
971 * case <code>null</code> is returned.
972 *
973 * @param x The x position of the point to return the component at.
974 * @param y The y position of the point to return the component at.
975 *
976 * @return The component containing the specified point, or <code>null</code>
977 * if there is no such point.
978 *
979 * @deprecated use {@link #getComponentAt(int, int)} instead
980 */
981 public Component locate(int x, int y)
982 {
983 synchronized (getTreeLock ())
984 {
985 if (!contains (x, y))
986 return null;
987 for (int i = 0; i < ncomponents; ++i)
988 {
989 // Ignore invisible children...
990 if (!component[i].isVisible ())
991 continue;
992
993 int x2 = x - component[i].x;
994 int y2 = y - component[i].y;
995 if (component[i].contains (x2, y2))
996 return component[i];
997 }
998 return this;
999 }
1000 }
1001
1002 /**
1003 * Returns the component located at the specified point. This is done
1004 * by checking whether or not a child component claims to contain this
1005 * point. The first child component that does is returned. If no
1006 * child component claims the point, the container itself is returned,
1007 * unless the point does not exist within this container, in which
1008 * case <code>null</code> is returned.
1009 *
1010 * @param p The point to return the component at.
1011 * @return The component containing the specified point, or <code>null</code>
1012 * if there is no such point.
1013 */
1014 public Component getComponentAt(Point p)
1015 {
1016 return getComponentAt (p.x, p.y);
1017 }
1018
1019 public Component findComponentAt(int x, int y)
1020 {
1021 synchronized (getTreeLock ())
1022 {
1023 if (! contains(x, y))
1024 return null;
1025
1026 for (int i = 0; i < ncomponents; ++i)
1027 {
1028 // Ignore invisible children...
1029 if (!component[i].isVisible())
1030 continue;
1031
1032 int x2 = x - component[i].x;
1033 int y2 = y - component[i].y;
1034 // We don't do the contains() check right away because
1035 // findComponentAt would redundantly do it first thing.
1036 if (component[i] instanceof Container)
1037 {
1038 Container k = (Container) component[i];
1039 Component r = k.findComponentAt(x2, y2);
1040 if (r != null)
1041 return r;
1042 }
1043 else if (component[i].contains(x2, y2))
1044 return component[i];
1045 }
1046
1047 return this;
1048 }
1049 }
1050
1051 public Component findComponentAt(Point p)
1052 {
1053 return findComponentAt(p.x, p.y);
1054 }
1055
1056 /**
1057 * Called when this container is added to another container to inform it
1058 * to create its peer. Peers for any child components will also be
1059 * created.
1060 */
1061 public void addNotify()
1062 {
1063 super.addNotify();
1064 addNotifyContainerChildren();
1065 }
1066
1067 /**
1068 * Called when this container is removed from its parent container to
1069 * inform it to destroy its peer. This causes the peers of all child
1070 * component to be destroyed as well.
1071 */
1072 public void removeNotify()
1073 {
1074 synchronized (getTreeLock ())
1075 {
1076 for (int i = 0; i < ncomponents; ++i)
1077 component[i].removeNotify();
1078 super.removeNotify();
1079 }
1080 }
1081
1082 /**
1083 * Tests whether or not the specified component is contained within
1084 * this components subtree.
1085 *
1086 * @param comp The component to test.
1087 *
1088 * @return <code>true</code> if this container is an ancestor of the
1089 * specified component, <code>false</code> otherwise.
1090 */
1091 public boolean isAncestorOf(Component comp)
1092 {
1093 synchronized (getTreeLock ())
1094 {
1095 while (true)
1096 {
1097 if (comp == null)
1098 return false;
1099 if (comp == this)
1100 return true;
1101 comp = comp.getParent();
1102 }
1103 }
1104 }
1105
1106 /**
1107 * Returns a string representing the state of this container for
1108 * debugging purposes.
1109 *
1110 * @return A string representing the state of this container.
1111 */
1112 protected String paramString()
1113 {
1114 if (layoutMgr == null)
1115 return super.paramString();
1116
1117 StringBuffer sb = new StringBuffer();
1118 sb.append(super.paramString());
1119 sb.append(",layout=");
1120 sb.append(layoutMgr.getClass().getName());
1121 return sb.toString();
1122 }
1123
1124 /**
1125 * Writes a listing of this container to the specified stream starting
1126 * at the specified indentation point.
1127 *
1128 * @param out The <code>PrintStream</code> to write to.
1129 * @param indent The indentation point.
1130 */
1131 public void list(PrintStream out, int indent)
1132 {
1133 synchronized (getTreeLock ())
1134 {
1135 super.list(out, indent);
1136 for (int i = 0; i < ncomponents; ++i)
1137 component[i].list(out, indent + 2);
1138 }
1139 }
1140
1141 /**
1142 * Writes a listing of this container to the specified stream starting
1143 * at the specified indentation point.
1144 *
1145 * @param out The <code>PrintWriter</code> to write to.
1146 * @param indent The indentation point.
1147 */
1148 public void list(PrintWriter out, int indent)
1149 {
1150 synchronized (getTreeLock ())
1151 {
1152 super.list(out, indent);
1153 for (int i = 0; i < ncomponents; ++i)
1154 component[i].list(out, indent + 2);
1155 }
1156 }
1157
1158 /**
1159 * Sets the focus traversal keys for a given traversal operation for this
1160 * Container.
1161 *
1162 * @exception IllegalArgumentException If id is not one of
1163 * KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
1164 * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
1165 * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS,
1166 * or KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS,
1167 * or if keystrokes contains null, or if any Object in keystrokes is not an
1168 * AWTKeyStroke, or if any keystroke represents a KEY_TYPED event, or if any
1169 * keystroke already maps to another focus traversal operation for this
1170 * Container.
1171 *
1172 * @since 1.4
1173 */
1174 public void setFocusTraversalKeys(int id, Set keystrokes)
1175 {
1176 if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS &&
1177 id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS &&
1178 id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS &&
1179 id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS)
1180 throw new IllegalArgumentException ();
1181
1182 if (keystrokes == null)
1183 {
1184 Container parent = getParent ();
1185
1186 while (parent != null)
1187 {
1188 if (parent.areFocusTraversalKeysSet (id))
1189 {
1190 keystrokes = parent.getFocusTraversalKeys (id);
1191 break;
1192 }
1193 parent = parent.getParent ();
1194 }
1195
1196 if (keystrokes == null)
1197 keystrokes = KeyboardFocusManager.getCurrentKeyboardFocusManager ().
1198 getDefaultFocusTraversalKeys (id);
1199 }
1200
1201 Set sa;
1202 Set sb;
1203 Set sc;
1204 String name;
1205 switch (id)
1206 {
1207 case KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS:
1208 sa = getFocusTraversalKeys
1209 (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
1210 sb = getFocusTraversalKeys
1211 (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS);
1212 sc = getFocusTraversalKeys
1213 (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS);
1214 name = "forwardFocusTraversalKeys";
1215 break;
1216 case KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS:
1217 sa = getFocusTraversalKeys
1218 (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
1219 sb = getFocusTraversalKeys
1220 (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS);
1221 sc = getFocusTraversalKeys
1222 (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS);
1223 name = "backwardFocusTraversalKeys";
1224 break;
1225 case KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS:
1226 sa = getFocusTraversalKeys
1227 (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
1228 sb = getFocusTraversalKeys
1229 (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
1230 sc = getFocusTraversalKeys
1231 (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS);
1232 name = "upCycleFocusTraversalKeys";
1233 break;
1234 case KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS:
1235 sa = getFocusTraversalKeys
1236 (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
1237 sb = getFocusTraversalKeys
1238 (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
1239 sc = getFocusTraversalKeys
1240 (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS);
1241 name = "downCycleFocusTraversalKeys";
1242 break;
1243 default:
1244 throw new IllegalArgumentException ();
1245 }
1246
1247 int i = keystrokes.size ();
1248 Iterator iter = keystrokes.iterator ();
1249
1250 while (--i >= 0)
1251 {
1252 Object o = iter.next ();
1253 if (!(o instanceof AWTKeyStroke)
1254 || sa.contains (o) || sb.contains (o) || sc.contains (o)
1255 || ((AWTKeyStroke) o).keyCode == KeyEvent.VK_UNDEFINED)
1256 throw new IllegalArgumentException ();
1257 }
1258
1259 if (focusTraversalKeys == null)
1260 focusTraversalKeys = new Set[4];
1261
1262 keystrokes = Collections.unmodifiableSet (new HashSet (keystrokes));
1263 firePropertyChange (name, focusTraversalKeys[id], keystrokes);
1264
1265 focusTraversalKeys[id] = keystrokes;
1266 }
1267
1268 /**
1269 * Returns the Set of focus traversal keys for a given traversal operation for
1270 * this Container.
1271 *
1272 * @exception IllegalArgumentException If id is not one of
1273 * KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
1274 * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
1275 * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS,
1276 * or KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS.
1277 *
1278 * @since 1.4
1279 */
1280 public Set getFocusTraversalKeys (int id)
1281 {
1282 if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS &&
1283 id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS &&
1284 id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS &&
1285 id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS)
1286 throw new IllegalArgumentException ();
1287
1288 Set s = null;
1289
1290 if (focusTraversalKeys != null)
1291 s = focusTraversalKeys[id];
1292
1293 if (s == null && parent != null)
1294 s = parent.getFocusTraversalKeys (id);
1295
1296 return s == null ? (KeyboardFocusManager.getCurrentKeyboardFocusManager()
1297 .getDefaultFocusTraversalKeys(id)) : s;
1298 }
1299
1300 /**
1301 * Returns whether the Set of focus traversal keys for the given focus
1302 * traversal operation has been explicitly defined for this Container.
1303 * If this method returns false, this Container is inheriting the Set from
1304 * an ancestor, or from the current KeyboardFocusManager.
1305 *
1306 * @exception IllegalArgumentException If id is not one of
1307 * KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
1308 * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
1309 * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS,
1310 * or KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS.
1311 *
1312 * @since 1.4
1313 */
1314 public boolean areFocusTraversalKeysSet (int id)
1315 {
1316 if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS &&
1317 id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS &&
1318 id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS &&
1319 id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS)
1320 throw new IllegalArgumentException ();
1321
1322 return focusTraversalKeys != null && focusTraversalKeys[id] != null;
1323 }
1324
1325 /**
1326 * Check whether the given Container is the focus cycle root of this
1327 * Container's focus traversal cycle. If this Container is a focus
1328 * cycle root itself, then it will be in two different focus cycles
1329 * -- it's own, and that of its ancestor focus cycle root's. In
1330 * that case, if <code>c</code> is either of those containers, this
1331 * method will return true.
1332 *
1333 * @param c the candidate Container
1334 *
1335 * @return true if c is the focus cycle root of the focus traversal
1336 * cycle to which this Container belongs, false otherwise
1337 *
1338 * @since 1.4
1339 */
1340 public boolean isFocusCycleRoot (Container c)
1341 {
1342 if (this == c
1343 && isFocusCycleRoot ())
1344 return true;
1345
1346 Container ancestor = getFocusCycleRootAncestor ();
1347
1348 if (c == ancestor)
1349 return true;
1350
1351 return false;
1352 }
1353
1354 /**
1355 * If this Container is a focus cycle root, set the focus traversal
1356 * policy that determines the focus traversal order for its
1357 * children. If non-null, this policy will be inherited by all
1358 * inferior focus cycle roots. If <code>policy</code> is null, this
1359 * Container will inherit its policy from the closest ancestor focus
1360 * cycle root that's had its policy set.
1361 *
1362 * @param policy the new focus traversal policy for this Container or null
1363 *
1364 * @since 1.4
1365 */
1366 public void setFocusTraversalPolicy (FocusTraversalPolicy policy)
1367 {
1368 focusTraversalPolicy = policy;
1369 }
1370
1371 /**
1372 * Return the focus traversal policy that determines the focus
1373 * traversal order for this Container's children. This method
1374 * returns null if this Container is not a focus cycle root. If the
1375 * focus traversal policy has not been set explicitly, then this
1376 * method will return an ancestor focus cycle root's policy instead.
1377 *
1378 * @return this Container's focus traversal policy or null
1379 *
1380 * @since 1.4
1381 */
1382 public FocusTraversalPolicy getFocusTraversalPolicy ()
1383 {
1384 if (!isFocusCycleRoot ())
1385 return null;
1386
1387 if (focusTraversalPolicy == null)
1388 {
1389 Container ancestor = getFocusCycleRootAncestor ();
1390
1391 if (ancestor != this)
1392 return ancestor.getFocusTraversalPolicy ();
1393 else
1394 {
1395 KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager ();
1396
1397 return manager.getDefaultFocusTraversalPolicy ();
1398 }
1399 }
1400 else
1401 return focusTraversalPolicy;
1402 }
1403
1404 /**
1405 * Check whether this Container's focus traversal policy has been
1406 * explicitly set. If it has not, then this Container will inherit
1407 * its focus traversal policy from one of its ancestor focus cycle
1408 * roots.
1409 *
1410 * @return true if focus traversal policy is set, false otherwise
1411 */
1412 public boolean isFocusTraversalPolicySet ()
1413 {
1414 return focusTraversalPolicy == null;
1415 }
1416
1417 /**
1418 * Set whether or not this Container is the root of a focus
1419 * traversal cycle. This Container's focus traversal policy
1420 * determines the order of focus traversal. Some policies prevent
1421 * the focus from being transferred between two traversal cycles
1422 * until an up or down traversal operation is performed. In that
1423 * case, normal traversal (not up or down) is limited to this
1424 * Container and all of this Container's descendents that are not
1425 * descendents of inferior focus cycle roots. In the default case
1426 * however, ContainerOrderFocusTraversalPolicy is in effect, and it
1427 * supports implicit down-cycle traversal operations.
1428 *
1429 * @param focusCycleRoot true if this is a focus cycle root, false otherwise
1430 *
1431 * @since 1.4
1432 */
1433 public void setFocusCycleRoot (boolean focusCycleRoot)
1434 {
1435 this.focusCycleRoot = focusCycleRoot;
1436 }
1437
1438 /**
1439 * Check whether this Container is a focus cycle root.
1440 *
1441 * @return true if this is a focus cycle root, false otherwise
1442 *
1443 * @since 1.4
1444 */
1445 public boolean isFocusCycleRoot ()
1446 {
1447 return focusCycleRoot;
1448 }
1449
1450 /**
1451 * Transfer focus down one focus traversal cycle. If this Container
1452 * is a focus cycle root, then its default component becomes the
1453 * focus owner, and this Container becomes the current focus cycle
1454 * root. No traversal will occur if this Container is not a focus
1455 * cycle root.
1456 *
1457 * @since 1.4
1458 */
1459 public void transferFocusDownCycle ()
1460 {
1461 KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager ();
1462
1463 manager.downFocusCycle (this);
1464 }
1465
1466 /**
1467 * Sets the ComponentOrientation property of this container and all components
1468 * contained within it.
1469 *
1470 * @exception NullPointerException If orientation is null
1471 *
1472 * @since 1.4
1473 */
1474 public void applyComponentOrientation (ComponentOrientation orientation)
1475 {
1476 if (orientation == null)
1477 throw new NullPointerException ();
1478 }
1479
1480 public void addPropertyChangeListener (PropertyChangeListener listener)
1481 {
1482 if (listener == null)
1483 return;
1484
1485 if (changeSupport == null)
1486 changeSupport = new PropertyChangeSupport (this);
1487
1488 changeSupport.addPropertyChangeListener (listener);
1489 }
1490
1491 public void addPropertyChangeListener (String name,
1492 PropertyChangeListener listener)
1493 {
1494 if (listener == null)
1495 return;
1496
1497 if (changeSupport == null)
1498 changeSupport = new PropertyChangeSupport (this);
1499
1500 changeSupport.addPropertyChangeListener (name, listener);
1501 }
1502
1503 // Hidden helper methods.
1504
1505 /**
1506 * Perform a graphics operation on the children of this container.
1507 * For each applicable child, the visitChild() method will be called
1508 * to perform the graphics operation.
1509 *
1510 * @param gfx The graphics object that will be used to derive new
1511 * graphics objects for the children.
1512 *
1513 * @param visitor Object encapsulating the graphics operation that
1514 * should be performed.
1515 *
1516 * @param lightweightOnly If true, only lightweight components will
1517 * be visited.
1518 */
1519 private void visitChildren(Graphics gfx, GfxVisitor visitor,
1520 boolean lightweightOnly)
1521 {
1522 synchronized (getTreeLock ())
1523 {
1524 for (int i = ncomponents - 1; i >= 0; --i)
1525 {
1526 Component comp = component[i];
1527 boolean applicable = comp.isVisible()
1528 && (comp.isLightweight() || !lightweightOnly);
1529
1530 if (applicable)
1531 visitChild(gfx, visitor, comp);
1532 }
1533 }
1534 }
1535
1536 /**
1537 * Perform a graphics operation on a child. A translated and clipped
1538 * graphics object will be created, and the visit() method of the
1539 * visitor will be called to perform the operation.
1540 *
1541 * @param gfx The graphics object that will be used to derive new
1542 * graphics objects for the child.
1543 *
1544 * @param visitor Object encapsulating the graphics operation that
1545 * should be performed.
1546 *
1547 * @param comp The child component that should be visited.
1548 */
1549 private void visitChild(Graphics gfx, GfxVisitor visitor,
1550 Component comp)
1551 {
1552 Rectangle bounds = comp.getBounds();
1553 Rectangle oldClip = gfx.getClipBounds();
1554 if (oldClip == null)
1555 oldClip = bounds;
1556
1557 Rectangle clip = oldClip.intersection(bounds);
1558
1559 if (clip.isEmpty()) return;
1560
1561 boolean clipped = false;
1562 boolean translated = false;
1563 try
1564 {
1565 gfx.setClip(clip.x, clip.y, clip.width, clip.height);
1566 clipped = true;
1567 gfx.translate(bounds.x, bounds.y);
1568 translated = true;
1569 visitor.visit(comp, gfx);
1570 }
1571 finally
1572 {
1573 if (translated)
1574 gfx.translate (-bounds.x, -bounds.y);
1575 if (clipped)
1576 gfx.setClip (oldClip.x, oldClip.y, oldClip.width, oldClip.height);
1577 }
1578 }
1579
1580 void dispatchEventImpl(AWTEvent e)
1581 {
1582 // Give lightweight dispatcher a chance to handle it.
1583 if (dispatcher != null && dispatcher.handleEvent (e))
1584 return;
1585
1586 if ((e.id <= ContainerEvent.CONTAINER_LAST
1587 && e.id >= ContainerEvent.CONTAINER_FIRST)
1588 && (containerListener != null
1589 || (eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0))
1590 processEvent(e);
1591 else
1592 super.dispatchEventImpl(e);
1593 }
1594
1595 /**
1596 * Tests if this container has an interest in the given event id.
1597 *
1598 * @param eventId The event id to check.
1599 *
1600 * @return <code>true</code> if a listener for the event id exists or
1601 * if the eventMask is set for the event id.
1602 *
1603 * @see java.awt.Component#eventTypeEnabled(int)
1604 */
1605 boolean eventTypeEnabled(int eventId)
1606 {
1607 if(eventId <= ContainerEvent.CONTAINER_LAST
1608 && eventId >= ContainerEvent.CONTAINER_FIRST)
1609