OSDN Git Service

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