1 /* Copyright (C) 1999, 2000, 2002 Free Software Foundation
3 This file is part of GNU Classpath.
5 GNU Classpath is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
10 GNU Classpath is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNU Classpath; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 As a special exception, if you link this library with other files to
21 produce an executable, this library does not by itself cause the
22 resulting executable to be covered by the GNU General Public License.
23 This exception does not however invalidate any other reasons why the
24 executable file might be covered by the GNU General Public License. */
28 import java.awt.event.*;
29 import java.io.PrintStream;
30 import java.io.PrintWriter;
31 import java.util.EventListener;
32 import java.awt.peer.ComponentPeer;
33 import java.awt.peer.ContainerPeer;
34 import java.awt.peer.LightweightPeer;
36 /* A somewhat incomplete class. */
38 public class Container extends Component
40 /* Serialized fields from the serialization spec. */
42 Component[] component;
43 LayoutManager layoutMgr;
44 /* LightweightDispatcher dispatcher; */ // wtf?
46 int containerSerializedDataVersion;
48 /* Anything else is non-serializable, and should be declared "transient". */
49 transient ContainerListener containerListener;
52 * Default constructor for subclasses.
59 * Returns the number of components in this container.
61 * @return The number of components in this container.
63 public int getComponentCount()
69 * Returns the number of components in this container.
71 * @return The number of components in this container.
73 * @deprecated This method is deprecated in favor of
74 * <code>getComponentCount()</code>.
76 public int countComponents()
82 * Returns the component at the specified index.
84 * @param index The index of the component to retrieve.
86 * @return The requested component.
88 * @exception ArrayIndexOutOfBoundsException If the specified index is not
91 public Component getComponent (int n)
93 if (n < 0 || n >= ncomponents)
94 throw new ArrayIndexOutOfBoundsException("no such component");
99 * Returns an array of the components in this container.
101 * @return The components in this container.
103 public Component[] getComponents()
105 Component[] result = new Component[ncomponents];
107 System.arraycopy(component, 0, result, 0, ncomponents);
112 * Returns the insets for this container, which is the space used for
113 * borders, the margin, etc.
115 * @return The insets for this container.
117 public Insets getInsets()
120 return new Insets(0, 0, 0, 0);
121 return ((ContainerPeer) peer).getInsets();
125 * Returns the insets for this container, which is the space used for
126 * borders, the margin, etc.
128 * @return The insets for this container.
130 * @deprecated This method is deprecated in favor of
131 * <code>getInsets()</code>.
133 public Insets insets()
139 * Adds the specified component to this container at the end of the
142 * @param component The component to add to the container.
144 * @return The same component that was added.
146 public Component add (Component comp)
148 addImpl (comp, null, -1);
153 * Adds the specified component to the container at the end of the
154 * component list. This method should not be used. Instead, use
155 * <code>add(Component, Object</code>.
158 * @param component The component to be added.
160 * @return The same component that was added.
162 public Component add (String name, Component comp)
164 addImpl (comp, name, -1);
169 * Adds the specified component to this container at the specified index
170 * in the component list.
172 * @param component The component to be added.
173 * @param index The index in the component list to insert this child
174 * at, or -1 to add at the end of the list.
176 * @return The same component that was added.
178 * @param throws ArrayIndexOutOfBounds If the specified index is invalid.
180 public Component add (Component comp, int index)
182 addImpl (comp, null, index);
187 * Adds the specified component to this container at the end of the
188 * component list. The layout manager will use the specified constraints
189 * when laying out this component.
191 * @param component The component to be added to this container.
192 * @param constraints The layout constraints for this component.
194 public void add (Component comp, Object constraints)
196 addImpl (comp, constraints, -1);
200 * Adds the specified component to this container at the specified index
201 * in the component list. The layout manager will use the specified
202 * constraints when layout out this component.
204 * @param component The component to be added.
205 * @param constraints The layout constraints for this component.
206 * @param index The index in the component list to insert this child
207 * at, or -1 to add at the end of the list.
209 * @param throws ArrayIndexOutOfBounds If the specified index is invalid.
211 public void add (Component comp, Object constraints, int index)
213 addImpl (comp, constraints, index);
217 * This method is called by all the <code>add()</code> methods to perform
218 * the actual adding of the component. Subclasses who wish to perform
219 * their own processing when a component is added should override this
220 * method. Any subclass doing this must call the superclass version of
221 * this method in order to ensure proper functioning of the container.
223 * @param component The component to be added.
224 * @param constraints The layout constraints for this component, or
225 * <code>null</code> if there are no constraints.
226 * @param index The index in the component list to insert this child
227 * at, or -1 to add at the end of the list.
229 * @param throws ArrayIndexOutOfBounds If the specified index is invalid.
231 protected void addImpl (Component comp, Object constraints, int index)
233 if (index > ncomponents
234 || (index < 0 && index != -1)
235 || comp instanceof Window
236 || (comp instanceof Container
237 && ((Container) comp).isAncestorOf (this)))
238 throw new IllegalArgumentException ();
240 // Reparent component, and make sure component is instantiated if
242 if (comp.parent != null)
243 comp.parent.remove (comp);
249 if (comp.isLightweight())
250 enableEvents(comp.eventMask);
255 if (component == null)
256 component = new Component[4]; // FIXME, better initial size?
258 // This isn't the most efficient implementation. We could do less
259 // copying when growing the array. It probably doesn't matter.
260 if (ncomponents >= component.length)
262 int nl = component.length * 2;
263 Component[] c = new Component[nl];
264 System.arraycopy (component, 0, c, 0, ncomponents);
268 component[ncomponents++] = comp;
271 System.arraycopy (component, index, component, index + 1,
272 ncomponents - index);
273 component[index] = comp;
277 // Notify the layout manager.
278 if (layoutMgr != null)
280 if (layoutMgr instanceof LayoutManager2)
282 LayoutManager2 lm2 = (LayoutManager2) layoutMgr;
283 lm2.addLayoutComponent (comp, constraints);
285 else if (constraints instanceof String)
286 layoutMgr.addLayoutComponent ((String) constraints, comp);
288 layoutMgr.addLayoutComponent (null, comp);
291 // Post event to notify of adding the container.
292 ContainerEvent ce = new ContainerEvent (this,
293 ContainerEvent.COMPONENT_ADDED,
295 getToolkit().getSystemEventQueue().postEvent(ce);
299 * Removes the component at the specified index from this container.
301 * @param index The index of the component to remove.
303 public void remove (int index)
305 Component r = component[index];
309 System.arraycopy (component, index + 1, component, index,
310 ncomponents - index - 1);
311 component[--ncomponents] = null;
315 if (layoutMgr != null)
316 layoutMgr.removeLayoutComponent (r);
318 // Post event to notify of adding the container.
319 ContainerEvent ce = new ContainerEvent (this,
320 ContainerEvent.COMPONENT_REMOVED,
322 getToolkit().getSystemEventQueue().postEvent(ce);
326 * Removes the specified component from this container.
328 * @return component The component to remove from this container.
330 public void remove (Component comp)
332 for (int i = 0; i < ncomponents; ++i)
334 if (component[i] == comp)
343 * Removes all components from this container.
345 public void removeAll()
347 while (ncomponents > 0)
352 * Returns the current layout manager for this container.
354 * @return The layout manager for this container.
356 public LayoutManager getLayout()
362 * Sets the layout manager for this container to the specified layout
365 * @param mgr The new layout manager for this container.
367 public void setLayout(LayoutManager mgr)
374 * Layout the components in this container.
376 public void doLayout()
378 if (layoutMgr != null)
379 layoutMgr.layoutContainer (this);
383 * Layout the components in this container.
385 * @deprecated This method is deprecated in favor of
386 * <code>doLayout()</code>.
394 * Invalidates this container to indicate that it (and all parent
395 * containers) need to be laid out.
397 public void invalidate()
403 * Re-lays out the components in this container.
405 public void validate()
407 // FIXME: use the tree lock?
418 * Recursively validates the container tree, recomputing any invalid
421 protected void validateTree()
426 ContainerPeer cPeer = null;
427 if ((peer != null) && !(peer instanceof LightweightPeer))
429 cPeer = (ContainerPeer) peer;
430 cPeer.beginValidate();
434 for (int i = 0; i < ncomponents; ++i)
436 Component comp = component[i];
437 if (! comp.isValid ())
439 if (comp instanceof Container)
441 ((Container) comp).validateTree();
445 component[i].validate();
450 /* children will call invalidate() when they are layed out. It
451 is therefore imporant that valid is not set to true
452 before after the children has been layed out. */
459 public void setFont(Font f)
462 // FIXME, should invalidate all children with font == null
466 * Returns the preferred size of this container.
468 * @return The preferred size of this container.
470 public Dimension getPreferredSize()
472 if (layoutMgr != null)
473 return layoutMgr.preferredLayoutSize (this);
475 return super.getPreferredSize ();
479 * Returns the preferred size of this container.
481 * @return The preferred size of this container.
483 * @deprecated This method is deprecated in favor of
484 * <code>getPreferredSize()</code>.
486 public Dimension preferredSize()
488 return getPreferredSize();
492 * Returns the minimum size of this container.
494 * @return The minimum size of this container.
496 public Dimension getMinimumSize()
498 if (layoutMgr != null)
499 return layoutMgr.minimumLayoutSize (this);
501 return super.getMinimumSize ();
505 * Returns the minimum size of this container.
507 * @return The minimum size of this container.
509 * @deprecated This method is deprecated in favor of
510 * <code>getMinimumSize()</code>.
512 public Dimension minimumSize()
514 return getMinimumSize();
518 * Returns the maximum size of this container.
520 * @return The maximum size of this container.
522 public Dimension getMaximumSize()
524 if (layoutMgr != null && layoutMgr instanceof LayoutManager2)
526 LayoutManager2 lm2 = (LayoutManager2) layoutMgr;
527 return lm2.maximumLayoutSize (this);
530 return super.getMaximumSize ();
534 * Returns the preferred alignment along the X axis. This is a value
535 * between 0 and 1 where 0 represents alignment flush left and
536 * 1 means alignment flush right, and 0.5 means centered.
538 * @return The preferred alignment along the X axis.
540 public float getAlignmentX()
542 if (layoutMgr instanceof LayoutManager2)
544 LayoutManager2 lm2 = (LayoutManager2) layoutMgr;
545 return lm2.getLayoutAlignmentX (this);
548 return super.getAlignmentX();
552 * Returns the preferred alignment along the Y axis. This is a value
553 * between 0 and 1 where 0 represents alignment flush top and
554 * 1 means alignment flush bottom, and 0.5 means centered.
556 * @return The preferred alignment along the Y axis.
558 public float getAlignmentY()
560 if (layoutMgr instanceof LayoutManager2)
562 LayoutManager2 lm2 = (LayoutManager2) layoutMgr;
563 return lm2.getLayoutAlignmentY (this);
566 return super.getAlignmentY();
570 * Paints this container. The implementation of this method in this
571 * class forwards to any lightweight components in this container. If
572 * this method is subclassed, this method should still be invoked as
573 * a superclass method so that lightweight components are properly
576 * @param graphics The graphics context for this paint job.
578 public void paint(Graphics g)
583 visitChildren(g, GfxPaintVisitor.INSTANCE, true);
587 * Perform a graphics operation on the children of this container.
588 * For each applicable child, the visitChild() method will be called
589 * to perform the graphics operation.
591 * @param gfx The graphics object that will be used to derive new
592 * graphics objects for the children.
594 * @param visitor Object encapsulating the graphics operation that
595 * should be performed.
597 * @param lightweightOnly If true, only lightweight components will
600 private void visitChildren(Graphics gfx, GfxVisitor visitor,
601 boolean lightweightOnly)
605 for (int i = 0; i < ncomponents; ++i)
607 Component comp = component[i];
608 boolean applicable = comp.isVisible()
609 && (comp.isLightweight() || !lightweightOnly);
612 visitChild(gfx, visitor, comp);
617 * Perform a graphics operation on a child. A translated and clipped
618 * graphics object will be created, and the visit() method of the
619 * visitor will be called to perform the operation.
621 * @param gfx The graphics object that will be used to derive new
622 * graphics objects for the child.
624 * @param visitor Object encapsulating the graphics operation that
625 * should be performed.
627 * @param comp The child component that should be visited.
629 private void visitChild(Graphics gfx, GfxVisitor visitor,
632 Rectangle bounds = comp.getBounds();
633 Rectangle clip = gfx.getClipBounds().intersection(bounds);
635 if (clip.isEmpty()) return;
637 Graphics gfx2 = gfx.create();
638 gfx2.setClip(clip.x, clip.y, clip.width, clip.height);
639 gfx2.translate(bounds.x, bounds.y);
641 visitor.visit(comp, gfx2);
645 * Updates this container. The implementation of this method in this
646 * class forwards to any lightweight components in this container. If
647 * this method is subclassed, this method should still be invoked as
648 * a superclass method so that lightweight components are properly
651 * @param graphics The graphics context for this update.
653 public void update(Graphics g)
659 * Prints this container. The implementation of this method in this
660 * class forwards to any lightweight components in this container. If
661 * this method is subclassed, this method should still be invoked as
662 * a superclass method so that lightweight components are properly
665 * @param graphics The graphics context for this print job.
667 public void print(Graphics g)
670 visitChildren(g, GfxPrintVisitor.INSTANCE, true);
674 * Paints all of the components in this container.
676 * @param graphics The graphics context for this paint job.
678 public void paintComponents(Graphics g)
681 visitChildren(g, GfxPaintAllVisitor.INSTANCE, true);
685 * Prints all of the components in this container.
687 * @param graphics The graphics context for this print job.
689 public void printComponents(Graphics g)
692 visitChildren(g, GfxPrintAllVisitor.INSTANCE, true);
695 void dispatchEventImpl(AWTEvent e)
697 if ((e.id <= ContainerEvent.CONTAINER_LAST
698 && e.id >= ContainerEvent.CONTAINER_FIRST)
699 && (containerListener != null
700 || (eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0))
703 super.dispatchEventImpl(e);
707 * Adds the specified container listener to this object's list of
708 * container listeners.
710 * @param listener The listener to add.
712 public synchronized void addContainerListener(ContainerListener l)
714 containerListener = AWTEventMulticaster.add (containerListener, l);
718 * Removes the specified container listener from this object's list of
719 * container listeners.
721 * @param listener The listener to remove.
723 public synchronized void removeContainerListener(ContainerListener l)
725 containerListener = AWTEventMulticaster.remove(containerListener, l);
729 public EventListener[] getListeners(Class listenerType)
731 if (listenerType == ContainerListener.class)
732 return getListenersImpl(listenerType, containerListener);
733 else return super.getListeners(listenerType);
737 * Processes the specified event. This method calls
738 * <code>processContainerEvent()</code> if this method is a
739 * <code>ContainerEvent</code>, otherwise it calls the superclass
742 * @param event The event to be processed.
744 protected void processEvent(AWTEvent e)
746 if (e instanceof ContainerEvent)
747 processContainerEvent((ContainerEvent) e);
748 else super.processEvent(e);
752 * Called when a container event occurs if container events are enabled.
753 * This method calls any registered listeners.
755 * @param event The event that occurred.
757 protected void processContainerEvent(ContainerEvent e)
759 if (containerListener == null)
763 case ContainerEvent.COMPONENT_ADDED:
764 containerListener.componentAdded(e);
767 case ContainerEvent.COMPONENT_REMOVED:
768 containerListener.componentRemoved(e);
774 * AWT 1.0 event processor.
776 * @param event The event that occurred.
778 * @deprecated This method is deprecated in favor of
779 * <code>dispatchEvent()</code>.
781 public void deliverEvent(Event e)
786 * Returns the component located at the specified point. This is done
787 * by checking whether or not a child component claims to contain this
788 * point. The first child component that does is returned. If no
789 * child component claims the point, the container itself is returned,
790 * unless the point does not exist within this container, in which
791 * case <code>null</code> is returned.
793 * @param x The X coordinate of the point.
794 * @param y The Y coordinate of the point.
796 * @return The component containing the specified point, or
797 * <code>null</code> if there is no such point.
799 public Component getComponentAt (int x, int y)
801 if (! contains (x, y))
803 for (int i = 0; i < ncomponents; ++i)
805 // Ignore invisible children...
806 if (!component[i].isVisible())
809 int x2 = x - component[i].x;
810 int y2 = y - component[i].y;
811 if (component[i].contains (x2, y2))
818 * Returns the component located at the specified point. This is done
819 * by checking whether or not a child component claims to contain this
820 * point. The first child component that does is returned. If no
821 * child component claims the point, the container itself is returned,
822 * unless the point does not exist within this container, in which
823 * case <code>null</code> is returned.
825 * @param point The point to return the component at.
827 * @return The component containing the specified point, or <code>null</code>
828 * if there is no such point.
830 * @deprecated This method is deprecated in favor of
831 * <code>getComponentAt(int, int)</code>.
833 public Component locate(int x, int y)
835 return getComponentAt(x, y);
839 * Returns the component located at the specified point. This is done
840 * by checking whether or not a child component claims to contain this
841 * point. The first child component that does is returned. If no
842 * child component claims the point, the container itself is returned,
843 * unless the point does not exist within this container, in which
844 * case <code>null</code> is returned.
846 * @param point The point to return the component at.
848 * @return The component containing the specified point, or <code>null</code>
849 * if there is no such point.
851 public Component getComponentAt(Point p)
853 return getComponentAt(p.x, p.y);
856 public Component findComponentAt (int x, int y)
858 if (! contains (x, y))
861 for (int i = 0; i < ncomponents; ++i)
863 // Ignore invisible children...
864 if (!component[i].isVisible())
867 int x2 = x - component[i].x;
868 int y2 = y - component[i].y;
869 // We don't do the contains() check right away because
870 // findComponentAt would redundantly do it first thing.
871 if (component[i] instanceof Container)
873 Container k = (Container) component[i];
874 Component r = k.findComponentAt (x2, y2);
878 else if (component[i].contains (x2, y2))
885 public Component findComponentAt(Point p)
887 return findComponentAt(p.x, p.y);
891 * Called when this container is added to another container to inform it
892 * to create its peer. Peers for any child components will also be
895 public void addNotify ()
899 addNotifyContainerChildren ();
904 private void addNotifyContainerChildren()
906 for (int i = ncomponents; --i >= 0; )
908 component[i].addNotify();
909 if (component[i].isLightweight())
910 enableEvents(component[i].eventMask);
915 * Called when this container is removed from its parent container to
916 * inform it to destroy its peer. This causes the peers of all child
917 * component to be destroyed as well.
919 public void removeNotify()
921 for (int i = 0; i < ncomponents; ++i)
922 component[i].removeNotify ();
923 super.removeNotify();
927 * Tests whether or not the specified component is contained within
928 * this components subtree.
930 * @param component The component to test.
932 * @return <code>true</code> if this container is an ancestor of the
933 * specified component, <code>false</code>.
935 public boolean isAncestorOf (Component comp)
943 comp = comp.getParent();
948 * Returns a string representing the state of this container for
949 * debugging purposes.
951 * @return A string representing the state of this container.
953 protected String paramString()
955 String param = super.paramString();
956 if (layoutMgr != null)
957 param = param + "," + layoutMgr.getClass().getName();
963 * Writes a listing of this container to the specified stream starting
964 * at the specified indentation point.
966 * @param stream The <code>PrintStream</code> to write to.
967 * @param indent The indentation point.
969 public void list (PrintStream out, int indent)
971 super.list (out, indent);
972 for (int i = 0; i < ncomponents; ++i)
973 component[i].list (out, indent + 2);
977 * Writes a listing of this container to the specified stream starting
978 * at the specified indentation point.
980 * @param stream The <code>PrintWriter</code> to write to.
981 * @param indent The indentation point.
983 public void list(PrintWriter out, int indent)
985 super.list (out, indent);
986 for (int i = 0; i < ncomponents; ++i)
987 component[i].list (out, indent + 2);
991 /* The following classes are used in concert with the
992 visitChildren() method to implement all the graphics operations
993 that requires traversal of the containment hierarchy. */
995 abstract static class GfxVisitor
997 public abstract void visit(Component c, Graphics gfx);
1000 static class GfxPaintVisitor extends GfxVisitor
1002 public void visit(Component c, Graphics gfx) { c.paint(gfx); }
1003 public static final GfxVisitor INSTANCE = new GfxPaintVisitor();
1006 static class GfxPrintVisitor extends GfxVisitor
1008 public void visit(Component c, Graphics gfx) { c.print(gfx); }
1009 public static final GfxVisitor INSTANCE = new GfxPrintVisitor();
1012 static class GfxPaintAllVisitor extends GfxVisitor
1014 public void visit(Component c, Graphics gfx) { c.paintAll(gfx); }
1015 public static final GfxVisitor INSTANCE = new GfxPaintAllVisitor();
1018 static class GfxPrintAllVisitor extends GfxVisitor
1020 public void visit(Component c, Graphics gfx) { c.printAll(gfx); }
1021 public static final GfxVisitor INSTANCE = new GfxPrintAllVisitor();
1024 // This is used to implement Component.transferFocus.
1025 Component findNextFocusComponent (Component child)
1030 for (start = 0; start < ncomponents; ++start)
1032 if (component[start] == child)
1036 // This special case lets us be sure to terminate.
1047 for (int j = start; j != end; ++j)
1049 if (j >= ncomponents)
1051 // The JCL says that we should wrap here. However, that
1052 // seems wrong. To me it seems that focus order should be
1053 // global within in given window. So instead if we reach
1054 // the end we try to look in our parent, if we have one.
1056 return parent.findNextFocusComponent (this);
1059 if (component[j] instanceof Container)
1061 Component c = component[j];
1062 c = c.findNextFocusComponent (null);
1066 else if (component[j].isFocusTraversable ())
1067 return component[j];