Imported GNU Classpath 0.20
[official-gcc.git] / libjava / classpath / java / awt / Container.java
blob67f0ed184d4dae67f5be107c7ca34c5f676bab6a
1 /* Container.java -- parent container class in AWT
2 Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005 Free Software Foundation
4 This file is part of GNU Classpath.
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.
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.
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.
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.
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. */
39 package java.awt;
41 import java.awt.event.ComponentListener;
42 import java.awt.event.ContainerEvent;
43 import java.awt.event.ContainerListener;
44 import java.awt.event.KeyEvent;
45 import java.awt.event.MouseEvent;
46 import java.awt.peer.ComponentPeer;
47 import java.awt.peer.ContainerPeer;
48 import java.awt.peer.LightweightPeer;
49 import java.beans.PropertyChangeListener;
50 import java.beans.PropertyChangeSupport;
51 import java.io.IOException;
52 import java.io.ObjectInputStream;
53 import java.io.ObjectOutputStream;
54 import java.io.PrintStream;
55 import java.io.PrintWriter;
56 import java.io.Serializable;
57 import java.util.Collections;
58 import java.util.EventListener;
59 import java.util.HashSet;
60 import java.util.Iterator;
61 import java.util.Set;
63 import javax.accessibility.Accessible;
65 import gnu.java.awt.AWTUtilities;
67 /**
68 * A generic window toolkit object that acts as a container for other objects.
69 * Components are tracked in a list, and new elements are at the end of the
70 * list or bottom of the stacking order.
72 * @author original author unknown
73 * @author Eric Blake (ebb9@email.byu.edu)
75 * @since 1.0
77 * @status still missing 1.4 support
79 public class Container extends Component
81 /**
82 * Compatible with JDK 1.0+.
84 private static final long serialVersionUID = 4613797578919906343L;
86 /* Serialized fields from the serialization spec. */
87 int ncomponents;
88 Component[] component;
89 LayoutManager layoutMgr;
91 LightweightDispatcher dispatcher;
93 Dimension maxSize;
95 /**
96 * @since 1.4
98 boolean focusCycleRoot;
100 int containerSerializedDataVersion;
102 /* Anything else is non-serializable, and should be declared "transient". */
103 transient ContainerListener containerListener;
104 transient PropertyChangeSupport changeSupport;
106 /** The focus traversal policy that determines how focus is
107 transferred between this Container and its children. */
108 private FocusTraversalPolicy focusTraversalPolicy;
111 * The focus traversal keys, if not inherited from the parent or default
112 * keyboard manager. These sets will contain only AWTKeyStrokes that
113 * represent press and release events to use as focus control.
115 * @see #getFocusTraversalKeys(int)
116 * @see #setFocusTraversalKeys(int, Set)
117 * @since 1.4
119 transient Set[] focusTraversalKeys;
122 * Default constructor for subclasses.
124 public Container()
126 // Nothing to do here.
130 * Returns the number of components in this container.
132 * @return The number of components in this container.
134 public int getComponentCount()
136 return countComponents ();
140 * Returns the number of components in this container.
142 * @return The number of components in this container.
144 * @deprecated use {@link #getComponentCount()} instead
146 public int countComponents()
148 return ncomponents;
152 * Returns the component at the specified index.
154 * @param n The index of the component to retrieve.
156 * @return The requested component.
158 * @throws ArrayIndexOutOfBoundsException If the specified index is invalid
160 public Component getComponent(int n)
162 synchronized (getTreeLock ())
164 if (n < 0 || n >= ncomponents)
165 throw new ArrayIndexOutOfBoundsException("no such component");
167 return component[n];
172 * Returns an array of the components in this container.
174 * @return The components in this container.
176 public Component[] getComponents()
178 synchronized (getTreeLock ())
180 Component[] result = new Component[ncomponents];
182 if (ncomponents > 0)
183 System.arraycopy(component, 0, result, 0, ncomponents);
185 return result;
190 * Swaps the components at position i and j, in the container.
193 protected void swapComponents (int i, int j)
195 synchronized (getTreeLock ())
197 if (i < 0
198 || i >= component.length
199 || j < 0
200 || j >= component.length)
201 throw new ArrayIndexOutOfBoundsException ();
202 Component tmp = component[i];
203 component[i] = component[j];
204 component[j] = tmp;
209 * Returns the insets for this container, which is the space used for
210 * borders, the margin, etc.
212 * @return The insets for this container.
214 public Insets getInsets()
216 return insets ();
220 * Returns the insets for this container, which is the space used for
221 * borders, the margin, etc.
223 * @return The insets for this container.
224 * @deprecated use {@link #getInsets()} instead
226 public Insets insets()
228 if (peer == null)
229 return new Insets (0, 0, 0, 0);
231 return ((ContainerPeer) peer).getInsets ();
235 * Adds the specified component to this container at the end of the
236 * component list.
238 * @param comp The component to add to the container.
240 * @return The same component that was added.
242 public Component add(Component comp)
244 addImpl(comp, null, -1);
245 return comp;
249 * Adds the specified component to the container at the end of the
250 * component list. This method should not be used. Instead, use
251 * <code>add(Component, Object)</code>.
253 * @param name The name of the component to be added.
254 * @param comp The component to be added.
256 * @return The same component that was added.
258 * @see #add(Component,Object)
260 public Component add(String name, Component comp)
262 addImpl(comp, name, -1);
263 return comp;
267 * Adds the specified component to this container at the specified index
268 * in the component list.
270 * @param comp The component to be added.
271 * @param index The index in the component list to insert this child
272 * at, or -1 to add at the end of the list.
274 * @return The same component that was added.
276 * @throws ArrayIndexOutOfBoundsException If the specified index is invalid.
278 public Component add(Component comp, int index)
280 addImpl(comp, null, index);
281 return comp;
285 * Adds the specified component to this container at the end of the
286 * component list. The layout manager will use the specified constraints
287 * when laying out this component.
289 * @param comp The component to be added to this container.
290 * @param constraints The layout constraints for this component.
292 public void add(Component comp, Object constraints)
294 addImpl(comp, constraints, -1);
298 * Adds the specified component to this container at the specified index
299 * in the component list. The layout manager will use the specified
300 * constraints when layout out this component.
302 * @param comp The component to be added.
303 * @param constraints The layout constraints for this component.
304 * @param index The index in the component list to insert this child
305 * at, or -1 to add at the end of the list.
307 * @throws ArrayIndexOutOfBoundsException If the specified index is invalid.
309 public void add(Component comp, Object constraints, int index)
311 addImpl(comp, constraints, index);
315 * This method is called by all the <code>add()</code> methods to perform
316 * the actual adding of the component. Subclasses who wish to perform
317 * their own processing when a component is added should override this
318 * method. Any subclass doing this must call the superclass version of
319 * this method in order to ensure proper functioning of the container.
321 * @param comp The component to be added.
322 * @param constraints The layout constraints for this component, or
323 * <code>null</code> if there are no constraints.
324 * @param index The index in the component list to insert this child
325 * at, or -1 to add at the end of the list.
327 * @throws ArrayIndexOutOfBoundsException If the specified index is invalid.
329 protected void addImpl(Component comp, Object constraints, int index)
331 synchronized (getTreeLock ())
333 if (index > ncomponents
334 || (index < 0 && index != -1)
335 || comp instanceof Window
336 || (comp instanceof Container
337 && ((Container) comp).isAncestorOf(this)))
338 throw new IllegalArgumentException();
340 // Reparent component, and make sure component is instantiated if
341 // we are.
342 if (comp.parent != null)
343 comp.parent.remove(comp);
344 comp.parent = this;
346 if (peer != null)
348 // Notify the component that it has a new parent.
349 comp.addNotify();
351 if (comp.isLightweight ())
353 enableEvents (comp.eventMask);
354 if (!isLightweight ())
355 enableEvents (AWTEvent.PAINT_EVENT_MASK);
359 // Invalidate the layout of the added component and its ancestors.
360 comp.invalidate();
362 if (component == null)
363 component = new Component[4]; // FIXME, better initial size?
365 // This isn't the most efficient implementation. We could do less
366 // copying when growing the array. It probably doesn't matter.
367 if (ncomponents >= component.length)
369 int nl = component.length * 2;
370 Component[] c = new Component[nl];
371 System.arraycopy(component, 0, c, 0, ncomponents);
372 component = c;
375 if (index == -1)
376 component[ncomponents++] = comp;
377 else
379 System.arraycopy(component, index, component, index + 1,
380 ncomponents - index);
381 component[index] = comp;
382 ++ncomponents;
385 // Notify the layout manager.
386 if (layoutMgr != null)
388 if (layoutMgr instanceof LayoutManager2)
390 LayoutManager2 lm2 = (LayoutManager2) layoutMgr;
391 lm2.addLayoutComponent(comp, constraints);
393 else if (constraints instanceof String)
394 layoutMgr.addLayoutComponent((String) constraints, comp);
395 else
396 layoutMgr.addLayoutComponent(null, comp);
399 // We previously only sent an event when this container is showing.
400 // Also, the event was posted to the event queue. A Mauve test shows
401 // that this event is not delivered using the event queue and it is
402 // also sent when the container is not showing.
403 ContainerEvent ce = new ContainerEvent(this,
404 ContainerEvent.COMPONENT_ADDED,
405 comp);
406 ContainerListener[] listeners = getContainerListeners();
407 for (int i = 0; i < listeners.length; i++)
408 listeners[i].componentAdded(ce);
410 // Repaint this container.
411 repaint(comp.getX(), comp.getY(), comp.getWidth(),
412 comp.getHeight());
417 * Removes the component at the specified index from this container.
419 * @param index The index of the component to remove.
421 public void remove(int index)
423 synchronized (getTreeLock ())
425 Component r = component[index];
427 ComponentListener[] list = r.getComponentListeners();
428 for (int j = 0; j < list.length; j++)
429 r.removeComponentListener(list[j]);
431 if (r.isShowing())
432 r.removeNotify();
434 System.arraycopy(component, index + 1, component, index,
435 ncomponents - index - 1);
436 component[--ncomponents] = null;
438 invalidate();
440 if (layoutMgr != null)
441 layoutMgr.removeLayoutComponent(r);
443 r.parent = null;
445 if (isShowing ())
447 // Post event to notify of removing the component.
448 ContainerEvent ce = new ContainerEvent(this,
449 ContainerEvent.COMPONENT_REMOVED,
451 getToolkit().getSystemEventQueue().postEvent(ce);
457 * Removes the specified component from this container.
459 * @param comp The component to remove from this container.
461 public void remove(Component comp)
463 synchronized (getTreeLock ())
465 for (int i = 0; i < ncomponents; ++i)
467 if (component[i] == comp)
469 remove(i);
470 break;
477 * Removes all components from this container.
479 public void removeAll()
481 synchronized (getTreeLock ())
483 while (ncomponents > 0)
484 remove(0);
489 * Returns the current layout manager for this container.
491 * @return The layout manager for this container.
493 public LayoutManager getLayout()
495 return layoutMgr;
499 * Sets the layout manager for this container to the specified layout
500 * manager.
502 * @param mgr The new layout manager for this container.
504 public void setLayout(LayoutManager mgr)
506 layoutMgr = mgr;
507 invalidate();
511 * Layout the components in this container.
513 public void doLayout()
515 layout ();
519 * Layout the components in this container.
521 * @deprecated use {@link #doLayout()} instead
523 public void layout()
525 if (layoutMgr != null)
526 layoutMgr.layoutContainer (this);
530 * Invalidates this container to indicate that it (and all parent
531 * containers) need to be laid out.
533 public void invalidate()
535 super.invalidate();
536 if (layoutMgr != null && layoutMgr instanceof LayoutManager2)
538 LayoutManager2 lm2 = (LayoutManager2) layoutMgr;
539 lm2.invalidateLayout(this);
544 * Re-lays out the components in this container.
546 public void validate()
548 synchronized (getTreeLock ())
550 if (! isValid() && peer != null)
552 validateTree();
558 * Recursively invalidates the container tree.
560 void invalidateTree()
562 super.invalidate(); // Clean cached layout state.
563 for (int i = 0; i < ncomponents; i++)
565 Component comp = component[i];
566 comp.invalidate();
567 if (comp instanceof Container)
568 ((Container) comp).invalidateTree();
571 if (layoutMgr != null && layoutMgr instanceof LayoutManager2)
573 LayoutManager2 lm2 = (LayoutManager2) layoutMgr;
574 lm2.invalidateLayout(this);
579 * Recursively validates the container tree, recomputing any invalid
580 * layouts.
582 protected void validateTree()
584 if (valid)
585 return;
587 ContainerPeer cPeer = null;
588 if (peer != null && ! (peer instanceof LightweightPeer))
590 cPeer = (ContainerPeer) peer;
591 cPeer.beginValidate();
594 for (int i = 0; i < ncomponents; ++i)
596 Component comp = component[i];
598 if (comp.getPeer () == null)
599 comp.addNotify();
602 doLayout ();
603 for (int i = 0; i < ncomponents; ++i)
605 Component comp = component[i];
607 if (! comp.isValid())
609 if (comp instanceof Container)
611 ((Container) comp).validateTree();
613 else
615 component[i].validate();
620 /* children will call invalidate() when they are layed out. It
621 is therefore important that valid is not set to true
622 until after the children have been layed out. */
623 valid = true;
625 if (cPeer != null)
626 cPeer.endValidate();
629 public void setFont(Font f)
631 if( (f != null && (font == null || !font.equals(f)))
632 || f == null)
634 super.setFont(f);
635 // FIXME: Although it might make more sense to invalidate only
636 // those children whose font == null, Sun invalidates all children.
637 // So we'll do the same.
638 invalidateTree();
643 * Returns the preferred size of this container.
645 * @return The preferred size of this container.
647 public Dimension getPreferredSize()
649 return preferredSize ();
653 * Returns the preferred size of this container.
655 * @return The preferred size of this container.
657 * @deprecated use {@link #getPreferredSize()} instead
659 public Dimension preferredSize()
661 synchronized(treeLock)
663 if(valid && prefSize != null)
664 return new Dimension(prefSize);
665 LayoutManager layout = getLayout();
666 if (layout != null)
668 Dimension layoutSize = layout.preferredLayoutSize(this);
669 if(valid)
670 prefSize = layoutSize;
671 return new Dimension(layoutSize);
673 else
674 return super.preferredSize ();
679 * Returns the minimum size of this container.
681 * @return The minimum size of this container.
683 public Dimension getMinimumSize()
685 return minimumSize ();
689 * Returns the minimum size of this container.
691 * @return The minimum size of this container.
693 * @deprecated use {@link #getMinimumSize()} instead
695 public Dimension minimumSize()
697 if(valid && minSize != null)
698 return new Dimension(minSize);
700 LayoutManager layout = getLayout();
701 if (layout != null)
703 minSize = layout.minimumLayoutSize (this);
704 return minSize;
706 else
707 return super.minimumSize ();
711 * Returns the maximum size of this container.
713 * @return The maximum size of this container.
715 public Dimension getMaximumSize()
717 if (valid && maxSize != null)
718 return new Dimension(maxSize);
720 LayoutManager layout = getLayout();
721 if (layout != null && layout instanceof LayoutManager2)
723 LayoutManager2 lm2 = (LayoutManager2) layout;
724 maxSize = lm2.maximumLayoutSize(this);
725 return maxSize;
727 else
728 return super.getMaximumSize();
732 * Returns the preferred alignment along the X axis. This is a value
733 * between 0 and 1 where 0 represents alignment flush left and
734 * 1 means alignment flush right, and 0.5 means centered.
736 * @return The preferred alignment along the X axis.
738 public float getAlignmentX()
740 LayoutManager layout = getLayout();
741 float alignmentX = 0.0F;
742 if (layout != null && layout instanceof LayoutManager2)
744 LayoutManager2 lm2 = (LayoutManager2) layout;
745 alignmentX = lm2.getLayoutAlignmentX(this);
747 else
748 alignmentX = super.getAlignmentX();
749 return alignmentX;
753 * Returns the preferred alignment along the Y axis. This is a value
754 * between 0 and 1 where 0 represents alignment flush top and
755 * 1 means alignment flush bottom, and 0.5 means centered.
757 * @return The preferred alignment along the Y axis.
759 public float getAlignmentY()
761 LayoutManager layout = getLayout();
762 float alignmentY = 0.0F;
763 if (layout != null && layout instanceof LayoutManager2)
765 LayoutManager2 lm2 = (LayoutManager2) layout;
766 alignmentY = lm2.getLayoutAlignmentY(this);
768 else
769 alignmentY = super.getAlignmentY();
770 return alignmentY;
774 * Paints this container. The implementation of this method in this
775 * class forwards to any lightweight components in this container. If
776 * this method is subclassed, this method should still be invoked as
777 * a superclass method so that lightweight components are properly
778 * drawn.
780 * @param g The graphics context for this paint job.
782 public void paint(Graphics g)
784 if (!isShowing())
785 return;
787 // Visit heavyweights as well, in case they were
788 // erased when we cleared the background for this container.
789 visitChildren(g, GfxPaintVisitor.INSTANCE, false);
793 * Updates this container. The implementation of this method in this
794 * class forwards to any lightweight components in this container. If
795 * this method is subclassed, this method should still be invoked as
796 * a superclass method so that lightweight components are properly
797 * drawn.
799 * @param g The graphics context for this update.
801 * @specnote The specification suggests that this method forwards the
802 * update() call to all its lightweight children. Tests show
803 * that this is not done either in the JDK. The exact behaviour
804 * seems to be that the background is cleared in heavyweight
805 * Containers, and all other containers
806 * directly call paint(), causing the (lightweight) children to
807 * be painted.
809 public void update(Graphics g)
811 // It seems that the JDK clears the background of containers like Panel
812 // and Window (within this method) but not of 'plain' Containers or
813 // JComponents. This could
814 // lead to the assumption that it only clears heavyweight containers.
815 // However that is not quite true. In a test with a custom Container
816 // that overrides isLightweight() to return false, the background is
817 // also not cleared. So we do a check on !(peer instanceof LightweightPeer)
818 // instead.
819 ComponentPeer p = peer;
820 if (p != null && !(p instanceof LightweightPeer))
821 g.clearRect(0, 0, getWidth(), getHeight());
823 paint(g);
827 * Prints this container. The implementation of this method in this
828 * class forwards to any lightweight components in this container. If
829 * this method is subclassed, this method should still be invoked as
830 * a superclass method so that lightweight components are properly
831 * drawn.
833 * @param g The graphics context for this print job.
835 public void print(Graphics g)
837 super.print(g);
838 visitChildren(g, GfxPrintVisitor.INSTANCE, true);
842 * Paints all of the components in this container.
844 * @param g The graphics context for this paint job.
846 public void paintComponents(Graphics g)
848 paint(g);
849 visitChildren(g, GfxPaintAllVisitor.INSTANCE, true);
853 * Prints all of the components in this container.
855 * @param g The graphics context for this print job.
857 public void printComponents(Graphics g)
859 super.paint(g);
860 visitChildren(g, GfxPrintAllVisitor.INSTANCE, true);
864 * Adds the specified container listener to this object's list of
865 * container listeners.
867 * @param listener The listener to add.
869 public synchronized void addContainerListener(ContainerListener listener)
871 containerListener = AWTEventMulticaster.add(containerListener, listener);
875 * Removes the specified container listener from this object's list of
876 * container listeners.
878 * @param listener The listener to remove.
880 public synchronized void removeContainerListener(ContainerListener listener)
882 containerListener = AWTEventMulticaster.remove(containerListener, listener);
886 * @since 1.4
888 public synchronized ContainerListener[] getContainerListeners()
890 return (ContainerListener[])
891 AWTEventMulticaster.getListeners(containerListener,
892 ContainerListener.class);
896 * Returns all registered {@link EventListener}s of the given
897 * <code>listenerType</code>.
899 * @param listenerType the class of listeners to filter (<code>null</code>
900 * not permitted).
902 * @return An array of registered listeners.
904 * @throws ClassCastException if <code>listenerType</code> does not implement
905 * the {@link EventListener} interface.
906 * @throws NullPointerException if <code>listenerType</code> is
907 * <code>null</code>.
909 * @see #getContainerListeners()
911 * @since 1.3
913 public EventListener[] getListeners(Class listenerType)
915 if (listenerType == ContainerListener.class)
916 return getContainerListeners();
917 return super.getListeners(listenerType);
921 * Processes the specified event. This method calls
922 * <code>processContainerEvent()</code> if this method is a
923 * <code>ContainerEvent</code>, otherwise it calls the superclass
924 * method.
926 * @param e The event to be processed.
928 protected void processEvent(AWTEvent e)
930 if (e instanceof ContainerEvent)
931 processContainerEvent((ContainerEvent) e);
932 else
933 super.processEvent(e);
937 * Called when a container event occurs if container events are enabled.
938 * This method calls any registered listeners.
940 * @param e The event that occurred.
942 protected void processContainerEvent(ContainerEvent e)
944 if (containerListener == null)
945 return;
946 switch (e.id)
948 case ContainerEvent.COMPONENT_ADDED:
949 containerListener.componentAdded(e);
950 break;
952 case ContainerEvent.COMPONENT_REMOVED:
953 containerListener.componentRemoved(e);
954 break;
959 * AWT 1.0 event processor.
961 * @param e The event that occurred.
963 * @deprecated use {@link #dispatchEvent(AWTEvent)} instead
965 public void deliverEvent(Event e)
967 if (!handleEvent (e))
969 synchronized (getTreeLock ())
971 Component parent = getParent ();
973 if (parent != null)
974 parent.deliverEvent (e);
980 * Returns the component located at the specified point. This is done
981 * by checking whether or not a child component claims to contain this
982 * point. The first child component that does is returned. If no
983 * child component claims the point, the container itself is returned,
984 * unless the point does not exist within this container, in which
985 * case <code>null</code> is returned.
987 * @param x The X coordinate of the point.
988 * @param y The Y coordinate of the point.
990 * @return The component containing the specified point, or
991 * <code>null</code> if there is no such point.
993 public Component getComponentAt(int x, int y)
995 return locate (x, y);
999 * Returns the component located at the specified point. This is done
1000 * by checking whether or not a child component claims to contain this
1001 * point. The first child component that does is returned. If no
1002 * child component claims the point, the container itself is returned,
1003 * unless the point does not exist within this container, in which
1004 * case <code>null</code> is returned.
1006 * @param x The x position of the point to return the component at.
1007 * @param y The y position of the point to return the component at.
1009 * @return The component containing the specified point, or <code>null</code>
1010 * if there is no such point.
1012 * @deprecated use {@link #getComponentAt(int, int)} instead
1014 public Component locate(int x, int y)
1016 synchronized (getTreeLock ())
1018 if (!contains (x, y))
1019 return null;
1020 for (int i = 0; i < ncomponents; ++i)
1022 // Ignore invisible children...
1023 if (!component[i].isVisible ())
1024 continue;
1026 int x2 = x - component[i].x;
1027 int y2 = y - component[i].y;
1028 if (component[i].contains (x2, y2))
1029 return component[i];
1031 return this;
1036 * Returns the component located at the specified point. This is done
1037 * by checking whether or not a child component claims to contain this
1038 * point. The first child component that does is returned. If no
1039 * child component claims the point, the container itself is returned,
1040 * unless the point does not exist within this container, in which
1041 * case <code>null</code> is returned.
1043 * @param p The point to return the component at.
1044 * @return The component containing the specified point, or <code>null</code>
1045 * if there is no such point.
1047 public Component getComponentAt(Point p)
1049 return getComponentAt (p.x, p.y);
1052 public Component findComponentAt(int x, int y)
1054 synchronized (getTreeLock ())
1056 if (! contains(x, y))
1057 return null;
1059 for (int i = 0; i < ncomponents; ++i)
1061 // Ignore invisible children...
1062 if (!component[i].isVisible())
1063 continue;
1065 int x2 = x - component[i].x;
1066 int y2 = y - component[i].y;
1067 // We don't do the contains() check right away because
1068 // findComponentAt would redundantly do it first thing.
1069 if (component[i] instanceof Container)
1071 Container k = (Container) component[i];
1072 Component r = k.findComponentAt(x2, y2);
1073 if (r != null)
1074 return r;
1076 else if (component[i].contains(x2, y2))
1077 return component[i];
1080 return this;
1085 * Finds the visible child component that contains the specified position.
1086 * The top-most child is returned in the case where there is overlap.
1087 * If the top-most child is transparent and has no MouseListeners attached,
1088 * we discard it and return the next top-most component containing the
1089 * specified position.
1090 * @param x the x coordinate
1091 * @param y the y coordinate
1092 * @return null if the <code>this</code> does not contain the position,
1093 * otherwise the top-most component (out of this container itself and
1094 * its descendants) meeting the criteria above.
1096 Component findComponentForMouseEventAt(int x, int y)
1098 synchronized (getTreeLock())
1100 if (!contains(x, y))
1101 return null;
1103 for (int i = 0; i < ncomponents; ++i)
1105 // Ignore invisible children...
1106 if (!component[i].isVisible())
1107 continue;
1109 int x2 = x - component[i].x;
1110 int y2 = y - component[i].y;
1111 // We don't do the contains() check right away because
1112 // findComponentAt would redundantly do it first thing.
1113 if (component[i] instanceof Container)
1115 Container k = (Container) component[i];
1116 Component r = k.findComponentForMouseEventAt(x2, y2);
1117 if (r != null)
1118 return r;
1120 else if (component[i].contains(x2, y2))
1121 return component[i];
1124 //don't return transparent components with no MouseListeners
1125 if (getMouseListeners().length == 0
1126 && getMouseMotionListeners().length == 0)
1127 return null;
1128 return this;
1132 public Component findComponentAt(Point p)
1134 return findComponentAt(p.x, p.y);
1138 * Called when this container is added to another container to inform it
1139 * to create its peer. Peers for any child components will also be
1140 * created.
1142 public void addNotify()
1144 super.addNotify();
1145 addNotifyContainerChildren();
1149 * Called when this container is removed from its parent container to
1150 * inform it to destroy its peer. This causes the peers of all child
1151 * component to be destroyed as well.
1153 public void removeNotify()
1155 synchronized (getTreeLock ())
1157 for (int i = 0; i < ncomponents; ++i)
1158 component[i].removeNotify();
1159 super.removeNotify();
1164 * Tests whether or not the specified component is contained within
1165 * this components subtree.
1167 * @param comp The component to test.
1169 * @return <code>true</code> if this container is an ancestor of the
1170 * specified component, <code>false</code> otherwise.
1172 public boolean isAncestorOf(Component comp)
1174 synchronized (getTreeLock ())
1176 while (true)
1178 if (comp == null)
1179 return false;
1180 if (comp == this)
1181 return true;
1182 comp = comp.getParent();
1188 * Returns a string representing the state of this container for
1189 * debugging purposes.
1191 * @return A string representing the state of this container.
1193 protected String paramString()
1195 if (layoutMgr == null)
1196 return super.paramString();
1198 StringBuffer sb = new StringBuffer();
1199 sb.append(super.paramString());
1200 sb.append(",layout=");
1201 sb.append(layoutMgr.getClass().getName());
1202 return sb.toString();
1206 * Writes a listing of this container to the specified stream starting
1207 * at the specified indentation point.
1209 * @param out The <code>PrintStream</code> to write to.
1210 * @param indent The indentation point.
1212 public void list(PrintStream out, int indent)
1214 synchronized (getTreeLock ())
1216 super.list(out, indent);
1217 for (int i = 0; i < ncomponents; ++i)
1218 component[i].list(out, indent + 2);
1223 * Writes a listing of this container to the specified stream starting
1224 * at the specified indentation point.
1226 * @param out The <code>PrintWriter</code> to write to.
1227 * @param indent The indentation point.
1229 public void list(PrintWriter out, int indent)
1231 synchronized (getTreeLock ())
1233 super.list(out, indent);
1234 for (int i = 0; i < ncomponents; ++i)
1235 component[i].list(out, indent + 2);
1240 * Sets the focus traversal keys for a given traversal operation for this
1241 * Container.
1243 * @exception IllegalArgumentException If id is not one of
1244 * KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
1245 * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
1246 * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS,
1247 * or KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS,
1248 * or if keystrokes contains null, or if any Object in keystrokes is not an
1249 * AWTKeyStroke, or if any keystroke represents a KEY_TYPED event, or if any
1250 * keystroke already maps to another focus traversal operation for this
1251 * Container.
1253 * @since 1.4
1255 public void setFocusTraversalKeys(int id, Set keystrokes)
1257 if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS &&
1258 id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS &&
1259 id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS &&
1260 id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS)
1261 throw new IllegalArgumentException ();
1263 if (keystrokes == null)
1265 Container parent = getParent ();
1267 while (parent != null)
1269 if (parent.areFocusTraversalKeysSet (id))
1271 keystrokes = parent.getFocusTraversalKeys (id);
1272 break;
1274 parent = parent.getParent ();
1277 if (keystrokes == null)
1278 keystrokes = KeyboardFocusManager.getCurrentKeyboardFocusManager ().
1279 getDefaultFocusTraversalKeys (id);
1282 Set sa;
1283 Set sb;
1284 Set sc;
1285 String name;
1286 switch (id)
1288 case KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS:
1289 sa = getFocusTraversalKeys
1290 (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
1291 sb = getFocusTraversalKeys
1292 (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS);
1293 sc = getFocusTraversalKeys
1294 (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS);
1295 name = "forwardFocusTraversalKeys";
1296 break;
1297 case KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS:
1298 sa = getFocusTraversalKeys
1299 (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
1300 sb = getFocusTraversalKeys
1301 (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS);
1302 sc = getFocusTraversalKeys
1303 (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS);
1304 name = "backwardFocusTraversalKeys";
1305 break;
1306 case KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS:
1307 sa = getFocusTraversalKeys
1308 (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
1309 sb = getFocusTraversalKeys
1310 (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
1311 sc = getFocusTraversalKeys
1312 (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS);
1313 name = "upCycleFocusTraversalKeys";
1314 break;
1315 case KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS:
1316 sa = getFocusTraversalKeys
1317 (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
1318 sb = getFocusTraversalKeys
1319 (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
1320 sc = getFocusTraversalKeys
1321 (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS);
1322 name = "downCycleFocusTraversalKeys";
1323 break;
1324 default:
1325 throw new IllegalArgumentException ();
1328 int i = keystrokes.size ();
1329 Iterator iter = keystrokes.iterator ();
1331 while (--i >= 0)
1333 Object o = iter.next ();
1334 if (!(o instanceof AWTKeyStroke)
1335 || sa.contains (o) || sb.contains (o) || sc.contains (o)
1336 || ((AWTKeyStroke) o).keyCode == KeyEvent.VK_UNDEFINED)
1337 throw new IllegalArgumentException ();
1340 if (focusTraversalKeys == null)
1341 focusTraversalKeys = new Set[4];
1343 keystrokes = Collections.unmodifiableSet (new HashSet (keystrokes));
1344 firePropertyChange (name, focusTraversalKeys[id], keystrokes);
1346 focusTraversalKeys[id] = keystrokes;
1350 * Returns the Set of focus traversal keys for a given traversal operation for
1351 * this Container.
1353 * @exception IllegalArgumentException If id is not one of
1354 * KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
1355 * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
1356 * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS,
1357 * or KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS.
1359 * @since 1.4
1361 public Set getFocusTraversalKeys (int id)
1363 if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS &&
1364 id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS &&
1365 id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS &&
1366 id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS)
1367 throw new IllegalArgumentException ();
1369 Set s = null;
1371 if (focusTraversalKeys != null)
1372 s = focusTraversalKeys[id];
1374 if (s == null && parent != null)
1375 s = parent.getFocusTraversalKeys (id);
1377 return s == null ? (KeyboardFocusManager.getCurrentKeyboardFocusManager()
1378 .getDefaultFocusTraversalKeys(id)) : s;
1382 * Returns whether the Set of focus traversal keys for the given focus
1383 * traversal operation has been explicitly defined for this Container.
1384 * If this method returns false, this Container is inheriting the Set from
1385 * an ancestor, or from the current KeyboardFocusManager.
1387 * @exception IllegalArgumentException If id is not one of
1388 * KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
1389 * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
1390 * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS,
1391 * or KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS.
1393 * @since 1.4
1395 public boolean areFocusTraversalKeysSet (int id)
1397 if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS &&
1398 id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS &&
1399 id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS &&
1400 id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS)
1401 throw new IllegalArgumentException ();
1403 return focusTraversalKeys != null && focusTraversalKeys[id] != null;
1407 * Check whether the given Container is the focus cycle root of this
1408 * Container's focus traversal cycle. If this Container is a focus
1409 * cycle root itself, then it will be in two different focus cycles
1410 * -- it's own, and that of its ancestor focus cycle root's. In
1411 * that case, if <code>c</code> is either of those containers, this
1412 * method will return true.
1414 * @param c the candidate Container
1416 * @return true if c is the focus cycle root of the focus traversal
1417 * cycle to which this Container belongs, false otherwise
1419 * @since 1.4
1421 public boolean isFocusCycleRoot (Container c)
1423 if (this == c
1424 && isFocusCycleRoot ())
1425 return true;
1427 Container ancestor = getFocusCycleRootAncestor ();
1429 if (c == ancestor)
1430 return true;
1432 return false;
1436 * If this Container is a focus cycle root, set the focus traversal
1437 * policy that determines the focus traversal order for its
1438 * children. If non-null, this policy will be inherited by all
1439 * inferior focus cycle roots. If <code>policy</code> is null, this
1440 * Container will inherit its policy from the closest ancestor focus
1441 * cycle root that's had its policy set.
1443 * @param policy the new focus traversal policy for this Container or null
1445 * @since 1.4
1447 public void setFocusTraversalPolicy (FocusTraversalPolicy policy)
1449 focusTraversalPolicy = policy;
1453 * Return the focus traversal policy that determines the focus
1454 * traversal order for this Container's children. This method
1455 * returns null if this Container is not a focus cycle root. If the
1456 * focus traversal policy has not been set explicitly, then this
1457 * method will return an ancestor focus cycle root's policy instead.
1459 * @return this Container's focus traversal policy or null
1461 * @since 1.4
1463 public FocusTraversalPolicy getFocusTraversalPolicy ()
1465 if (!isFocusCycleRoot ())
1466 return null;
1468 if (focusTraversalPolicy == null)
1470 Container ancestor = getFocusCycleRootAncestor ();
1472 if (ancestor != this)
1473 return ancestor.getFocusTraversalPolicy ();
1474 else
1476 KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager ();
1478 return manager.getDefaultFocusTraversalPolicy ();
1481 else
1482 return focusTraversalPolicy;
1486 * Check whether this Container's focus traversal policy has been
1487 * explicitly set. If it has not, then this Container will inherit
1488 * its focus traversal policy from one of its ancestor focus cycle
1489 * roots.
1491 * @return true if focus traversal policy is set, false otherwise
1493 public boolean isFocusTraversalPolicySet ()
1495 return focusTraversalPolicy == null;
1499 * Set whether or not this Container is the root of a focus
1500 * traversal cycle. This Container's focus traversal policy
1501 * determines the order of focus traversal. Some policies prevent
1502 * the focus from being transferred between two traversal cycles
1503 * until an up or down traversal operation is performed. In that
1504 * case, normal traversal (not up or down) is limited to this
1505 * Container and all of this Container's descendents that are not
1506 * descendents of inferior focus cycle roots. In the default case
1507 * however, ContainerOrderFocusTraversalPolicy is in effect, and it
1508 * supports implicit down-cycle traversal operations.
1510 * @param focusCycleRoot true if this is a focus cycle root, false otherwise
1512 * @since 1.4
1514 public void setFocusCycleRoot (boolean focusCycleRoot)
1516 this.focusCycleRoot = focusCycleRoot;
1520 * Check whether this Container is a focus cycle root.
1522 * @return true if this is a focus cycle root, false otherwise
1524 * @since 1.4
1526 public boolean isFocusCycleRoot ()
1528 return focusCycleRoot;
1532 * Transfer focus down one focus traversal cycle. If this Container
1533 * is a focus cycle root, then its default component becomes the
1534 * focus owner, and this Container becomes the current focus cycle
1535 * root. No traversal will occur if this Container is not a focus
1536 * cycle root.
1538 * @since 1.4
1540 public void transferFocusDownCycle ()
1542 KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager ();
1544 manager.downFocusCycle (this);
1548 * Sets the ComponentOrientation property of this container and all components
1549 * contained within it.
1551 * @exception NullPointerException If orientation is null
1553 * @since 1.4
1555 public void applyComponentOrientation (ComponentOrientation orientation)
1557 if (orientation == null)
1558 throw new NullPointerException ();
1561 public void addPropertyChangeListener (PropertyChangeListener listener)
1563 if (listener == null)
1564 return;
1566 if (changeSupport == null)
1567 changeSupport = new PropertyChangeSupport (this);
1569 changeSupport.addPropertyChangeListener (listener);
1572 public void addPropertyChangeListener (String name,
1573 PropertyChangeListener listener)
1575 if (listener == null)
1576 return;
1578 if (changeSupport == null)
1579 changeSupport = new PropertyChangeSupport (this);
1581 changeSupport.addPropertyChangeListener (name, listener);
1584 // Hidden helper methods.
1587 * Perform a graphics operation on the children of this container.
1588 * For each applicable child, the visitChild() method will be called
1589 * to perform the graphics operation.
1591 * @param gfx The graphics object that will be used to derive new
1592 * graphics objects for the children.
1594 * @param visitor Object encapsulating the graphics operation that
1595 * should be performed.
1597 * @param lightweightOnly If true, only lightweight components will
1598 * be visited.
1600 private void visitChildren(Graphics gfx, GfxVisitor visitor,
1601 boolean lightweightOnly)
1603 synchronized (getTreeLock ())
1605 for (int i = ncomponents - 1; i >= 0; --i)
1607 Component comp = component[i];
1608 boolean applicable = comp.isVisible()
1609 && (comp.isLightweight() || !lightweightOnly);
1611 if (applicable)
1612 visitChild(gfx, visitor, comp);
1618 * Perform a graphics operation on a child. A translated and clipped
1619 * graphics object will be created, and the visit() method of the
1620 * visitor will be called to perform the operation.
1622 * @param gfx The graphics object that will be used to derive new
1623 * graphics objects for the child.
1625 * @param visitor Object encapsulating the graphics operation that
1626 * should be performed.
1628 * @param comp The child component that should be visited.
1630 private void visitChild(Graphics gfx, GfxVisitor visitor,
1631 Component comp)
1633 Rectangle bounds = comp.getBounds();
1635 if(!gfx.hitClip(bounds.x,bounds.y, bounds.width, bounds.height))
1636 return;
1638 Graphics g2 = gfx.create(bounds.x, bounds.y, bounds.width,
1639 bounds.height);
1642 visitor.visit(comp, g2);
1644 finally
1646 g2.dispose();
1650 void dispatchEventImpl(AWTEvent e)
1652 // Give lightweight dispatcher a chance to handle it.
1653 if (dispatcher != null && dispatcher.handleEvent (e))
1654 return;
1656 if ((e.id <= ContainerEvent.CONTAINER_LAST
1657 && e.id >= ContainerEvent.CONTAINER_FIRST)
1658 && (containerListener != null
1659 || (eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0))
1660 processEvent(e);
1661 else
1662 super.dispatchEventImpl(e);
1666 * Tests if this container has an interest in the given event id.
1668 * @param eventId The event id to check.
1670 * @return <code>true</code> if a listener for the event id exists or
1671 * if the eventMask is set for the event id.
1673 * @see java.awt.Component#eventTypeEnabled(int)
1675 boolean eventTypeEnabled(int eventId)
1677 if(eventId <= ContainerEvent.CONTAINER_LAST
1678 && eventId >= ContainerEvent.CONTAINER_FIRST)
1679 return containerListener != null
1680 || (eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0;
1681 else
1682 return super.eventTypeEnabled(eventId);
1685 // This is used to implement Component.transferFocus.
1686 Component findNextFocusComponent(Component child)
1688 synchronized (getTreeLock ())
1690 int start, end;
1691 if (child != null)
1693 for (start = 0; start < ncomponents; ++start)
1695 if (component[start] == child)
1696 break;
1698 end = start;
1699 // This special case lets us be sure to terminate.
1700 if (end == 0)
1701 end = ncomponents;
1702 ++start;
1704 else
1706 start = 0;
1707 end = ncomponents;
1710 for (int j = start; j != end; ++j)
1712 if (j >= ncomponents)
1714 // The JCL says that we should wrap here. However, that
1715 // seems wrong. To me it seems that focus order should be
1716 // global within in given window. So instead if we reach
1717 // the end we try to look in our parent, if we have one.
1718 if (parent != null)
1719 return parent.findNextFocusComponent(this);
1720 j -= ncomponents;
1722 if (component[j] instanceof Container)
1724 Component c = component[j];
1725 c = c.findNextFocusComponent(null);
1726 if (c != null)
1727 return c;
1729 else if (component[j].isFocusTraversable())
1730 return component[j];
1733 return null;
1737 private void addNotifyContainerChildren()
1739 synchronized (getTreeLock ())
1741 for (int i = ncomponents; --i >= 0; )
1743 component[i].addNotify();
1744 if (component[i].isLightweight ())
1747 // If we're not lightweight, and we just got a lightweight
1748 // child, we need a lightweight dispatcher to feed it events.
1749 if (!this.isLightweight() && dispatcher == null)
1750 dispatcher = new LightweightDispatcher (this);
1752 if (dispatcher != null)
1753 dispatcher.enableEvents(component[i].eventMask);
1755 enableEvents(component[i].eventMask);
1756 if (peer != null && !isLightweight ())
1757 enableEvents (AWTEvent.PAINT_EVENT_MASK);
1764 * Deserialize this Container:
1765 * <ol>
1766 * <li>Read from the stream the default serializable fields.</li>
1767 * <li>Read a list of serializable ContainerListeners as optional
1768 * data. If the list is null, no listeners will be registered.</li>
1769 * <li>Read this Container's FocusTraversalPolicy as optional data.
1770 * If this is null, then this Container will use a
1771 * DefaultFocusTraversalPolicy.</li>
1772 * </ol>
1774 * @param s the stream to read from
1775 * @throws ClassNotFoundException if deserialization fails
1776 * @throws IOException if the stream fails
1778 private void readObject (ObjectInputStream s)
1779 throws ClassNotFoundException, IOException
1781 s.defaultReadObject ();
1782 String key = (String) s.readObject ();
1783 while (key != null)
1785 Object object = s.readObject ();
1786 if ("containerL".equals (key))
1787 addContainerListener((ContainerListener) object);
1788 // FIXME: under what key is the focus traversal policy stored?
1789 else if ("focusTraversalPolicy".equals (key))
1790 setFocusTraversalPolicy ((FocusTraversalPolicy) object);
1792 key = (String) s.readObject();
1797 * Serialize this Container:
1798 * <ol>
1799 * <li>Write to the stream the default serializable fields.</li>
1800 * <li>Write the list of serializable ContainerListeners as optional
1801 * data.</li>
1802 * <li>Write this Container's FocusTraversalPolicy as optional data.</li>
1803 * </ol>
1805 * @param s the stream to write to
1806 * @throws IOException if the stream fails
1808 private void writeObject (ObjectOutputStream s) throws IOException
1810 s.defaultWriteObject ();
1811 AWTEventMulticaster.save (s, "containerL", containerListener);
1812 if (focusTraversalPolicy instanceof Serializable)
1813 s.writeObject (focusTraversalPolicy);
1814 else
1815 s.writeObject (null);
1818 // Nested classes.
1820 /* The following classes are used in concert with the
1821 visitChildren() method to implement all the graphics operations
1822 that requires traversal of the containment hierarchy. */
1824 abstract static class GfxVisitor
1826 public abstract void visit(Component c, Graphics gfx);
1829 static class GfxPaintVisitor extends GfxVisitor
1831 public static final GfxVisitor INSTANCE = new GfxPaintVisitor();
1833 public void visit(Component c, Graphics gfx)
1835 c.paint(gfx);
1839 static class GfxPrintVisitor extends GfxVisitor
1841 public static final GfxVisitor INSTANCE = new GfxPrintVisitor();
1843 public void visit(Component c, Graphics gfx)
1845 c.print(gfx);
1849 static class GfxPaintAllVisitor extends GfxVisitor
1851 public static final GfxVisitor INSTANCE = new GfxPaintAllVisitor();
1853 public void visit(Component c, Graphics gfx)
1855 c.paintAll(gfx);
1859 static class GfxPrintAllVisitor extends GfxVisitor
1861 public static final GfxVisitor INSTANCE = new GfxPrintAllVisitor();
1863 public void visit(Component c, Graphics gfx)
1865 c.printAll(gfx);
1870 * This class provides accessibility support for subclasses of container.
1872 * @author Eric Blake (ebb9@email.byu.edu)
1874 * @since 1.3
1876 protected class AccessibleAWTContainer extends AccessibleAWTComponent
1879 * Compatible with JDK 1.4+.
1881 private static final long serialVersionUID = 5081320404842566097L;
1884 * The handler to fire PropertyChange when children are added or removed.
1886 * @serial the handler for property changes
1888 protected ContainerListener accessibleContainerHandler
1889 = new AccessibleContainerHandler();
1892 * The default constructor.
1894 protected AccessibleAWTContainer()
1896 Container.this.addContainerListener(accessibleContainerHandler);
1900 * Return the number of accessible children of the containing accessible
1901 * object (at most the total number of its children).
1903 * @return the number of accessible children
1905 public int getAccessibleChildrenCount()
1907 synchronized (getTreeLock ())
1909 int count = 0;
1910 int i = component == null ? 0 : component.length;
1911 while (--i >= 0)
1912 if (component[i] instanceof Accessible)
1913 count++;
1914 return count;
1919 * Return the nth accessible child of the containing accessible object.
1921 * @param i the child to grab, zero-based
1922 * @return the accessible child, or null
1924 public Accessible getAccessibleChild(int i)
1926 synchronized (getTreeLock ())
1928 if (component == null)
1929 return null;
1930 int index = -1;
1931 while (i >= 0 && ++index < component.length)
1932 if (component[index] instanceof Accessible)
1933 i--;
1934 if (i < 0)
1935 return (Accessible) component[index];
1936 return null;
1941 * Return the accessible child located at point (in the parent's
1942 * coordinates), if one exists.
1944 * @param p the point to look at
1946 * @return an accessible object at that point, or null
1948 * @throws NullPointerException if p is null
1950 public Accessible getAccessibleAt(Point p)
1952 Component c = getComponentAt(p.x, p.y);
1953 return c != Container.this && c instanceof Accessible ? (Accessible) c
1954 : null;
1958 * This class fires a <code>PropertyChange</code> listener, if registered,
1959 * when children are added or removed from the enclosing accessible object.
1961 * @author Eric Blake (ebb9@email.byu.edu)
1963 * @since 1.3
1965 protected class AccessibleContainerHandler implements ContainerListener
1968 * Default constructor.
1970 protected AccessibleContainerHandler()
1972 // Nothing to do here.
1976 * Fired when a component is added; forwards to the PropertyChange
1977 * listener.
1979 * @param e the container event for adding
1981 public void componentAdded(ContainerEvent e)
1983 AccessibleAWTContainer.this.firePropertyChange
1984 (ACCESSIBLE_CHILD_PROPERTY, null, e.getChild());
1988 * Fired when a component is removed; forwards to the PropertyChange
1989 * listener.
1991 * @param e the container event for removing
1993 public void componentRemoved(ContainerEvent e)
1995 AccessibleAWTContainer.this.firePropertyChange
1996 (ACCESSIBLE_CHILD_PROPERTY, e.getChild(), null);
1998 } // class AccessibleContainerHandler
1999 } // class AccessibleAWTContainer
2000 } // class Container
2003 * There is a helper class implied from stack traces called
2004 * LightweightDispatcher, but since it is not part of the public API,
2005 * rather than mimic it exactly we write something which does "roughly
2006 * the same thing".
2008 class LightweightDispatcher implements Serializable
2010 private static final long serialVersionUID = 5184291520170872969L;
2011 private Container nativeContainer;
2012 private Cursor nativeCursor;
2013 private long eventMask;
2015 private transient Component pressedComponent;
2016 private transient Component lastComponentEntered;
2017 private transient int pressCount;
2019 LightweightDispatcher(Container c)
2021 nativeContainer = c;
2024 void enableEvents(long l)
2026 eventMask |= l;
2030 * Returns the deepest visible descendent of parent that contains the
2031 * specified location and that is not transparent and MouseListener-less.
2032 * @param parent the root component to begin the search
2033 * @param x the x coordinate
2034 * @param y the y coordinate
2035 * @return null if <code>parent</code> doesn't contain the location,
2036 * parent if parent is not a container or has no child that contains the
2037 * location, otherwise the appropriate component from the conditions
2038 * above.
2040 Component getDeepestComponentForMouseEventAt(Component parent, int x, int y)
2042 if (parent == null || (! parent.contains(x, y)))
2043 return null;
2045 if (! (parent instanceof Container))
2046 return parent;
2048 Container c = (Container) parent;
2049 return c.findComponentForMouseEventAt(x, y);
2052 Component acquireComponentForMouseEvent(MouseEvent me)
2054 int x = me.getX ();
2055 int y = me.getY ();
2057 Component mouseEventTarget = null;
2058 // Find the candidate which should receive this event.
2059 Component parent = nativeContainer;
2060 Component candidate = null;
2061 Point p = me.getPoint();
2062 while (candidate == null && parent != null)
2064 candidate = getDeepestComponentForMouseEventAt(parent, p.x, p.y);
2065 if (candidate == null || (candidate.eventMask & me.getID()) == 0)
2067 candidate = null;
2068 p = AWTUtilities.convertPoint(parent, p.x, p.y, parent.parent);
2069 parent = parent.parent;
2073 // If the only candidate we found was the native container itself,
2074 // don't dispatch any event at all. We only care about the lightweight
2075 // children here.
2076 if (candidate == nativeContainer)
2077 candidate = null;
2079 // If our candidate is new, inform the old target we're leaving.
2080 if (lastComponentEntered != null
2081 && lastComponentEntered.isShowing()
2082 && lastComponentEntered != candidate)
2084 // Old candidate could have been removed from
2085 // the nativeContainer so we check first.
2086 if (AWTUtilities.isDescendingFrom(lastComponentEntered,
2087 nativeContainer))
2089 Point tp = AWTUtilities.convertPoint(nativeContainer,
2090 x, y, lastComponentEntered);
2091 MouseEvent exited = new MouseEvent (lastComponentEntered,
2092 MouseEvent.MOUSE_EXITED,
2093 me.getWhen (),
2094 me.getModifiersEx (),
2095 tp.x, tp.y,
2096 me.getClickCount (),
2097 me.isPopupTrigger (),
2098 me.getButton ());
2099 lastComponentEntered.dispatchEvent (exited);
2101 lastComponentEntered = null;
2104 // If we have a candidate, maybe enter it.
2105 if (candidate != null)
2107 mouseEventTarget = candidate;
2108 if (candidate.isLightweight()
2109 && candidate.isShowing()
2110 && candidate != nativeContainer
2111 && candidate != lastComponentEntered)
2113 lastComponentEntered = mouseEventTarget;
2114 Point cp = AWTUtilities.convertPoint(nativeContainer,
2115 x, y, lastComponentEntered);
2116 MouseEvent entered = new MouseEvent (lastComponentEntered,
2117 MouseEvent.MOUSE_ENTERED,
2118 me.getWhen (),
2119 me.getModifiersEx (),
2120 cp.x, cp.y,
2121 me.getClickCount (),
2122 me.isPopupTrigger (),
2123 me.getButton ());
2124 lastComponentEntered.dispatchEvent (entered);
2128 // Check which buttons where pressed except the last button that
2129 // changed state.
2130 int modifiers = me.getModifiersEx() & (MouseEvent.BUTTON1_DOWN_MASK
2131 | MouseEvent.BUTTON2_DOWN_MASK
2132 | MouseEvent.BUTTON3_DOWN_MASK);
2133 switch(me.getButton())
2135 case MouseEvent.BUTTON1:
2136 modifiers &= ~MouseEvent.BUTTON1_DOWN_MASK;
2137 break;
2138 case MouseEvent.BUTTON2:
2139 modifiers &= ~MouseEvent.BUTTON2_DOWN_MASK;
2140 break;
2141 case MouseEvent.BUTTON3:
2142 modifiers &= ~MouseEvent.BUTTON3_DOWN_MASK;
2143 break;
2146 if (me.getID() == MouseEvent.MOUSE_RELEASED
2147 || me.getID() == MouseEvent.MOUSE_PRESSED && modifiers > 0
2148 || me.getID() == MouseEvent.MOUSE_DRAGGED)
2150 // If any of the following events occur while a button is held down,
2151 // they should be dispatched to the same component to which the
2152 // original MOUSE_PRESSED event was dispatched:
2153 // - MOUSE_RELEASED: This is important for correct dragging
2154 // behaviour, otherwise the release goes to an arbitrary component
2155 // outside of the dragged component. OTOH, if there is no mouse
2156 // drag while the mouse is pressed, the component under the mouse
2157 // is the same as the previously pressed component anyway.
2158 // - MOUSE_PRESSED: another button pressed while the first is held
2159 // down
2160 // - MOUSE_DRAGGED
2161 if (AWTUtilities.isDescendingFrom(pressedComponent, nativeContainer))
2162 mouseEventTarget = pressedComponent;
2164 else if (me.getID() == MouseEvent.MOUSE_CLICKED)
2166 // Don't dispatch CLICKED events whose target is not the same as the
2167 // target for the original PRESSED event.
2168 if (candidate != pressedComponent)
2170 mouseEventTarget = null;
2171 pressCount = 0;
2173 else if (pressCount == 0)
2174 pressedComponent = null;
2176 return mouseEventTarget;
2179 boolean handleEvent(AWTEvent e)
2181 if (e instanceof MouseEvent)
2183 MouseEvent me = (MouseEvent) e;
2185 // Make the LightWeightDispatcher reentrant. This is necessary when
2186 // a lightweight component does its own modal event queue.
2187 Component mouseEventTarget = acquireComponentForMouseEvent(me);
2189 // Avoid dispatching ENTERED and EXITED events twice.
2190 if (mouseEventTarget != null
2191 && mouseEventTarget.isShowing()
2192 && e.getID() != MouseEvent.MOUSE_ENTERED
2193 && e.getID() != MouseEvent.MOUSE_EXITED)
2195 switch (e.getID())
2197 case MouseEvent.MOUSE_PRESSED:
2198 if (pressCount++ == 0)
2199 pressedComponent = mouseEventTarget;
2200 break;
2201 case MouseEvent.MOUSE_RELEASED:
2202 // Clear our memory of the original PRESSED event, only if
2203 // we're not expecting a CLICKED event after this. If
2204 // there is a CLICKED event after this, it will do clean up.
2205 if (--pressCount == 0
2206 && mouseEventTarget != pressedComponent)
2208 pressedComponent = null;
2209 pressCount = 0;
2211 break;
2214 MouseEvent newEvt =
2215 AWTUtilities.convertMouseEvent(nativeContainer, me,
2216 mouseEventTarget);
2217 mouseEventTarget.dispatchEvent(newEvt);
2219 if (newEvt.isConsumed())
2220 e.consume();
2224 return e.isConsumed();