| /* Container.java -- parent container class in AWT |
| Copyright (C) 1999, 2000, 2002, 2003, 2004, 2005 Free Software Foundation |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package java.awt; |
| |
| import java.awt.event.ContainerEvent; |
| import java.awt.event.ContainerListener; |
| import java.awt.event.KeyEvent; |
| import java.awt.event.MouseEvent; |
| import java.awt.peer.ContainerPeer; |
| import java.awt.peer.LightweightPeer; |
| import java.beans.PropertyChangeListener; |
| import java.beans.PropertyChangeSupport; |
| import java.io.IOException; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.PrintStream; |
| import java.io.PrintWriter; |
| import java.io.Serializable; |
| import java.util.Collections; |
| import java.util.EventListener; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Set; |
| |
| import javax.accessibility.Accessible; |
| import javax.swing.SwingUtilities; |
| |
| /** |
| * A generic window toolkit object that acts as a container for other objects. |
| * Components are tracked in a list, and new elements are at the end of the |
| * list or bottom of the stacking order. |
| * |
| * @author original author unknown |
| * @author Eric Blake (ebb9@email.byu.edu) |
| * |
| * @since 1.0 |
| * |
| * @status still missing 1.4 support |
| */ |
| public class Container extends Component |
| { |
| /** |
| * Compatible with JDK 1.0+. |
| */ |
| private static final long serialVersionUID = 4613797578919906343L; |
| |
| /* Serialized fields from the serialization spec. */ |
| int ncomponents; |
| Component[] component; |
| LayoutManager layoutMgr; |
| |
| LightweightDispatcher dispatcher; |
| |
| Dimension maxSize; |
| |
| /** |
| * @since 1.4 |
| */ |
| boolean focusCycleRoot; |
| |
| int containerSerializedDataVersion; |
| |
| /* Anything else is non-serializable, and should be declared "transient". */ |
| transient ContainerListener containerListener; |
| transient PropertyChangeSupport changeSupport; |
| |
| /** The focus traversal policy that determines how focus is |
| transferred between this Container and its children. */ |
| private FocusTraversalPolicy focusTraversalPolicy; |
| |
| /** |
| * The focus traversal keys, if not inherited from the parent or default |
| * keyboard manager. These sets will contain only AWTKeyStrokes that |
| * represent press and release events to use as focus control. |
| * |
| * @see #getFocusTraversalKeys(int) |
| * @see #setFocusTraversalKeys(int, Set) |
| * @since 1.4 |
| */ |
| transient Set[] focusTraversalKeys; |
| |
| /** |
| * Default constructor for subclasses. |
| */ |
| public Container() |
| { |
| } |
| |
| /** |
| * Returns the number of components in this container. |
| * |
| * @return The number of components in this container. |
| */ |
| public int getComponentCount() |
| { |
| return countComponents (); |
| } |
| |
| /** |
| * Returns the number of components in this container. |
| * |
| * @return The number of components in this container. |
| * |
| * @deprecated use {@link #getComponentCount()} instead |
| */ |
| public int countComponents() |
| { |
| return ncomponents; |
| } |
| |
| /** |
| * Returns the component at the specified index. |
| * |
| * @param n The index of the component to retrieve. |
| * |
| * @return The requested component. |
| * |
| * @throws ArrayIndexOutOfBoundsException If the specified index is invalid |
| */ |
| public Component getComponent(int n) |
| { |
| synchronized (getTreeLock ()) |
| { |
| if (n < 0 || n >= ncomponents) |
| throw new ArrayIndexOutOfBoundsException("no such component"); |
| |
| return component[n]; |
| } |
| } |
| |
| /** |
| * Returns an array of the components in this container. |
| * |
| * @return The components in this container. |
| */ |
| public Component[] getComponents() |
| { |
| synchronized (getTreeLock ()) |
| { |
| Component[] result = new Component[ncomponents]; |
| |
| if (ncomponents > 0) |
| System.arraycopy(component, 0, result, 0, ncomponents); |
| |
| return result; |
| } |
| } |
| |
| /** |
| * Swaps the components at position i and j, in the container. |
| */ |
| |
| protected void swapComponents (int i, int j) |
| { |
| synchronized (getTreeLock ()) |
| { |
| if (i < 0 |
| || i >= component.length |
| || j < 0 |
| || j >= component.length) |
| throw new ArrayIndexOutOfBoundsException (); |
| Component tmp = component[i]; |
| component[i] = component[j]; |
| component[j] = tmp; |
| } |
| } |
| |
| /** |
| * Returns the insets for this container, which is the space used for |
| * borders, the margin, etc. |
| * |
| * @return The insets for this container. |
| */ |
| public Insets getInsets() |
| { |
| return insets (); |
| } |
| |
| /** |
| * Returns the insets for this container, which is the space used for |
| * borders, the margin, etc. |
| * |
| * @return The insets for this container. |
| * @deprecated use {@link #getInsets()} instead |
| */ |
| public Insets insets() |
| { |
| if (peer == null) |
| return new Insets (0, 0, 0, 0); |
| |
| return ((ContainerPeer) peer).getInsets (); |
| } |
| |
| /** |
| * Adds the specified component to this container at the end of the |
| * component list. |
| * |
| * @param comp The component to add to the container. |
| * |
| * @return The same component that was added. |
| */ |
| public Component add(Component comp) |
| { |
| addImpl(comp, null, -1); |
| return comp; |
| } |
| |
| /** |
| * Adds the specified component to the container at the end of the |
| * component list. This method should not be used. Instead, use |
| * <code>add(Component, Object)</code>. |
| * |
| * @param name The name of the component to be added. |
| * @param comp The component to be added. |
| * |
| * @return The same component that was added. |
| * |
| * @see #add(Component,Object) |
| */ |
| public Component add(String name, Component comp) |
| { |
| addImpl(comp, name, -1); |
| return comp; |
| } |
| |
| /** |
| * Adds the specified component to this container at the specified index |
| * in the component list. |
| * |
| * @param comp The component to be added. |
| * @param index The index in the component list to insert this child |
| * at, or -1 to add at the end of the list. |
| * |
| * @return The same component that was added. |
| * |
| * @throws ArrayIndexOutOfBounds If the specified index is invalid. |
| */ |
| public Component add(Component comp, int index) |
| { |
| addImpl(comp, null, index); |
| return comp; |
| } |
| |
| /** |
| * Adds the specified component to this container at the end of the |
| * component list. The layout manager will use the specified constraints |
| * when laying out this component. |
| * |
| * @param comp The component to be added to this container. |
| * @param constraints The layout constraints for this component. |
| */ |
| public void add(Component comp, Object constraints) |
| { |
| addImpl(comp, constraints, -1); |
| } |
| |
| /** |
| * Adds the specified component to this container at the specified index |
| * in the component list. The layout manager will use the specified |
| * constraints when layout out this component. |
| * |
| * @param comp The component to be added. |
| * @param constraints The layout constraints for this component. |
| * @param index The index in the component list to insert this child |
| * at, or -1 to add at the end of the list. |
| * |
| * @throws ArrayIndexOutOfBounds If the specified index is invalid. |
| */ |
| public void add(Component comp, Object constraints, int index) |
| { |
| addImpl(comp, constraints, index); |
| } |
| |
| /** |
| * This method is called by all the <code>add()</code> methods to perform |
| * the actual adding of the component. Subclasses who wish to perform |
| * their own processing when a component is added should override this |
| * method. Any subclass doing this must call the superclass version of |
| * this method in order to ensure proper functioning of the container. |
| * |
| * @param comp The component to be added. |
| * @param constraints The layout constraints for this component, or |
| * <code>null</code> if there are no constraints. |
| * @param index The index in the component list to insert this child |
| * at, or -1 to add at the end of the list. |
| * |
| * @throws ArrayIndexOutOfBounds If the specified index is invalid. |
| */ |
| protected void addImpl(Component comp, Object constraints, int index) |
| { |
| synchronized (getTreeLock ()) |
| { |
| if (index > ncomponents |
| || (index < 0 && index != -1) |
| || comp instanceof Window |
| || (comp instanceof Container |
| && ((Container) comp).isAncestorOf(this))) |
| throw new IllegalArgumentException(); |
| |
| // Reparent component, and make sure component is instantiated if |
| // we are. |
| if (comp.parent != null) |
| comp.parent.remove(comp); |
| comp.parent = this; |
| if (peer != null) |
| { |
| if (comp.isLightweight ()) |
| { |
| enableEvents (comp.eventMask); |
| if (!isLightweight ()) |
| enableEvents (AWTEvent.PAINT_EVENT_MASK); |
| } |
| } |
| |
| invalidate(); |
| |
| if (component == null) |
| component = new Component[4]; // FIXME, better initial size? |
| |
| // This isn't the most efficient implementation. We could do less |
| // copying when growing the array. It probably doesn't matter. |
| if (ncomponents >= component.length) |
| { |
| int nl = component.length * 2; |
| Component[] c = new Component[nl]; |
| System.arraycopy(component, 0, c, 0, ncomponents); |
| component = c; |
| } |
| |
| if (index == -1) |
| component[ncomponents++] = comp; |
| else |
| { |
| System.arraycopy(component, index, component, index + 1, |
| ncomponents - index); |
| component[index] = comp; |
| ++ncomponents; |
| } |
| |
| // Notify the layout manager. |
| if (layoutMgr != null) |
| { |
| if (layoutMgr instanceof LayoutManager2) |
| { |
| LayoutManager2 lm2 = (LayoutManager2) layoutMgr; |
| lm2.addLayoutComponent(comp, constraints); |
| } |
| else if (constraints instanceof String) |
| layoutMgr.addLayoutComponent((String) constraints, comp); |
| else |
| layoutMgr.addLayoutComponent(null, comp); |
| } |
| |
| if (isShowing ()) |
| { |
| // Post event to notify of adding the component. |
| ContainerEvent ce = new ContainerEvent(this, |
| ContainerEvent.COMPONENT_ADDED, |
| comp); |
| getToolkit().getSystemEventQueue().postEvent(ce); |
| } |
| } |
| } |
| |
| /** |
| * Removes the component at the specified index from this container. |
| * |
| * @param index The index of the component to remove. |
| */ |
| public void remove(int index) |
| { |
| synchronized (getTreeLock ()) |
| { |
| Component r = component[index]; |
| |
| r.removeNotify(); |
| |
| System.arraycopy(component, index + 1, component, index, |
| ncomponents - index - 1); |
| component[--ncomponents] = null; |
| |
| invalidate(); |
| |
| if (layoutMgr != null) |
| layoutMgr.removeLayoutComponent(r); |
| |
| r.parent = null; |
| |
| if (isShowing ()) |
| { |
| // Post event to notify of removing the component. |
| ContainerEvent ce = new ContainerEvent(this, |
| ContainerEvent.COMPONENT_REMOVED, |
| r); |
| getToolkit().getSystemEventQueue().postEvent(ce); |
| } |
| } |
| } |
| |
| /** |
| * Removes the specified component from this container. |
| * |
| * @return component The component to remove from this container. |
| */ |
| public void remove(Component comp) |
| { |
| synchronized (getTreeLock ()) |
| { |
| for (int i = 0; i < ncomponents; ++i) |
| { |
| if (component[i] == comp) |
| { |
| remove(i); |
| break; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Removes all components from this container. |
| */ |
| public void removeAll() |
| { |
| synchronized (getTreeLock ()) |
| { |
| while (ncomponents > 0) |
| remove(0); |
| } |
| } |
| |
| /** |
| * Returns the current layout manager for this container. |
| * |
| * @return The layout manager for this container. |
| */ |
| public LayoutManager getLayout() |
| { |
| return layoutMgr; |
| } |
| |
| /** |
| * Sets the layout manager for this container to the specified layout |
| * manager. |
| * |
| * @param mgr The new layout manager for this container. |
| */ |
| public void setLayout(LayoutManager mgr) |
| { |
| layoutMgr = mgr; |
| invalidate(); |
| } |
| |
| /** |
| * Layout the components in this container. |
| */ |
| public void doLayout() |
| { |
| layout (); |
| } |
| |
| /** |
| * Layout the components in this container. |
| * |
| * @deprecated use {@link #doLayout()} instead |
| */ |
| public void layout() |
| { |
| if (layoutMgr != null) |
| layoutMgr.layoutContainer (this); |
| } |
| |
| /** |
| * Invalidates this container to indicate that it (and all parent |
| * containers) need to be laid out. |
| */ |
| public void invalidate() |
| { |
| super.invalidate(); |
| } |
| |
| /** |
| * Re-lays out the components in this container. |
| */ |
| public void validate() |
| { |
| synchronized (getTreeLock ()) |
| { |
| if (! isValid() && peer != null) |
| { |
| validateTree(); |
| } |
| } |
| } |
| |
| /** |
| * Recursively invalidates the container tree. |
| */ |
| void invalidateTree() |
| { |
| for (int i = 0; i < ncomponents; i++) |
| { |
| Component comp = component[i]; |
| comp.invalidate(); |
| if (comp instanceof Container) |
| ((Container) comp).invalidateTree(); |
| } |
| } |
| |
| /** |
| * Recursively validates the container tree, recomputing any invalid |
| * layouts. |
| */ |
| protected void validateTree() |
| { |
| if (valid) |
| return; |
| |
| ContainerPeer cPeer = null; |
| if (peer != null && ! (peer instanceof LightweightPeer)) |
| { |
| cPeer = (ContainerPeer) peer; |
| cPeer.beginValidate(); |
| } |
| |
| for (int i = 0; i < ncomponents; ++i) |
| { |
| Component comp = component[i]; |
| |
| if (comp.getPeer () == null) |
| comp.addNotify(); |
| } |
| |
| doLayout (); |
| for (int i = 0; i < ncomponents; ++i) |
| { |
| Component comp = component[i]; |
| |
| if (! comp.isValid()) |
| { |
| if (comp instanceof Container) |
| { |
| ((Container) comp).validateTree(); |
| } |
| else |
| { |
| component[i].validate(); |
| } |
| } |
| } |
| |
| /* children will call invalidate() when they are layed out. It |
| is therefore important that valid is not set to true |
| until after the children have been layed out. */ |
| valid = true; |
| |
| if (cPeer != null) |
| cPeer.endValidate(); |
| } |
| |
| public void setFont(Font f) |
| { |
| super.setFont(f); |
| // FIXME: Although it might make more sense to invalidate only |
| // those children whose font == null, Sun invalidates all children. |
| // So we'll do the same. |
| invalidateTree(); |
| } |
| |
| /** |
| * Returns the preferred size of this container. |
| * |
| * @return The preferred size of this container. |
| */ |
| public Dimension getPreferredSize() |
| { |
| return preferredSize (); |
| } |
| |
| /** |
| * Returns the preferred size of this container. |
| * |
| * @return The preferred size of this container. |
| * |
| * @deprecated use {@link #getPreferredSize()} instead |
| */ |
| public Dimension preferredSize() |
| { |
| if (layoutMgr != null) |
| return layoutMgr.preferredLayoutSize (this); |
| else |
| return super.preferredSize (); |
| } |
| |
| /** |
| * Returns the minimum size of this container. |
| * |
| * @return The minimum size of this container. |
| */ |
| public Dimension getMinimumSize() |
| { |
| return minimumSize (); |
| } |
| |
| /** |
| * Returns the minimum size of this container. |
| * |
| * @return The minimum size of this container. |
| * |
| * @deprecated use {@link #getMinimumSize()} instead |
| */ |
| public Dimension minimumSize() |
| { |
| if (layoutMgr != null) |
| return layoutMgr.minimumLayoutSize (this); |
| else |
| return super.minimumSize (); |
| } |
| |
| /** |
| * Returns the maximum size of this container. |
| * |
| * @return The maximum size of this container. |
| */ |
| public Dimension getMaximumSize() |
| { |
| if (layoutMgr != null && layoutMgr instanceof LayoutManager2) |
| { |
| LayoutManager2 lm2 = (LayoutManager2) layoutMgr; |
| return lm2.maximumLayoutSize(this); |
| } |
| else |
| return super.getMaximumSize(); |
| } |
| |
| /** |
| * Returns the preferred alignment along the X axis. This is a value |
| * between 0 and 1 where 0 represents alignment flush left and |
| * 1 means alignment flush right, and 0.5 means centered. |
| * |
| * @return The preferred alignment along the X axis. |
| */ |
| public float getAlignmentX() |
| { |
| if (layoutMgr instanceof LayoutManager2) |
| { |
| LayoutManager2 lm2 = (LayoutManager2) layoutMgr; |
| return lm2.getLayoutAlignmentX(this); |
| } |
| else |
| return super.getAlignmentX(); |
| } |
| |
| /** |
| * Returns the preferred alignment along the Y axis. This is a value |
| * between 0 and 1 where 0 represents alignment flush top and |
| * 1 means alignment flush bottom, and 0.5 means centered. |
| * |
| * @return The preferred alignment along the Y axis. |
| */ |
| public float getAlignmentY() |
| { |
| if (layoutMgr instanceof LayoutManager2) |
| { |
| LayoutManager2 lm2 = (LayoutManager2) layoutMgr; |
| return lm2.getLayoutAlignmentY(this); |
| } |
| else |
| return super.getAlignmentY(); |
| } |
| |
| /** |
| * Paints this container. The implementation of this method in this |
| * class forwards to any lightweight components in this container. If |
| * this method is subclassed, this method should still be invoked as |
| * a superclass method so that lightweight components are properly |
| * drawn. |
| * |
| * @param g The graphics context for this paint job. |
| */ |
| public void paint(Graphics g) |
| { |
| if (!isShowing()) |
| return; |
| // Paint self first. |
| super.paint(g); |
| // Visit heavyweights as well, in case they were |
| // erased when we cleared the background for this container. |
| visitChildren(g, GfxPaintVisitor.INSTANCE, false); |
| } |
| |
| /** |
| * Updates this container. The implementation of this method in this |
| * class forwards to any lightweight components in this container. If |
| * this method is subclassed, this method should still be invoked as |
| * a superclass method so that lightweight components are properly |
| * drawn. |
| * |
| * @param g The graphics context for this update. |
| */ |
| public void update(Graphics g) |
| { |
| super.update(g); |
| } |
| |
| /** |
| * Prints this container. The implementation of this method in this |
| * class forwards to any lightweight components in this container. If |
| * this method is subclassed, this method should still be invoked as |
| * a superclass method so that lightweight components are properly |
| * drawn. |
| * |
| * @param g The graphics context for this print job. |
| */ |
| public void print(Graphics g) |
| { |
| super.print(g); |
| visitChildren(g, GfxPrintVisitor.INSTANCE, true); |
| } |
| |
| /** |
| * Paints all of the components in this container. |
| * |
| * @param g The graphics context for this paint job. |
| */ |
| public void paintComponents(Graphics g) |
| { |
| super.paint(g); |
| visitChildren(g, GfxPaintAllVisitor.INSTANCE, true); |
| } |
| |
| /** |
| * Prints all of the components in this container. |
| * |
| * @param g The graphics context for this print job. |
| */ |
| public void printComponents(Graphics g) |
| { |
| super.paint(g); |
| visitChildren(g, GfxPrintAllVisitor.INSTANCE, true); |
| } |
| |
| /** |
| * Adds the specified container listener to this object's list of |
| * container listeners. |
| * |
| * @param listener The listener to add. |
| */ |
| public synchronized void addContainerListener(ContainerListener listener) |
| { |
| containerListener = AWTEventMulticaster.add(containerListener, listener); |
| } |
| |
| /** |
| * Removes the specified container listener from this object's list of |
| * container listeners. |
| * |
| * @param listener The listener to remove. |
| */ |
| public synchronized void removeContainerListener(ContainerListener listener) |
| { |
| containerListener = AWTEventMulticaster.remove(containerListener, listener); |
| } |
| |
| /** |
| * @since 1.4 |
| */ |
| public synchronized ContainerListener[] getContainerListeners() |
| { |
| return (ContainerListener[]) |
| AWTEventMulticaster.getListeners(containerListener, |
| ContainerListener.class); |
| } |
| |
| /** |
| * Returns an array of all the objects currently registered as FooListeners |
| * upon this Container. FooListeners are registered using the addFooListener |
| * method. |
| * |
| * @exception ClassCastException If listenerType doesn't specify a class or |
| * interface that implements @see java.util.EventListener. |
| * |
| * @since 1.3 |
| */ |
| public EventListener[] getListeners(Class listenerType) |
| { |
| if (listenerType == ContainerListener.class) |
| return getContainerListeners(); |
| return super.getListeners(listenerType); |
| } |
| |
| /** |
| * Processes the specified event. This method calls |
| * <code>processContainerEvent()</code> if this method is a |
| * <code>ContainerEvent</code>, otherwise it calls the superclass |
| * method. |
| * |
| * @param e The event to be processed. |
| */ |
| protected void processEvent(AWTEvent e) |
| { |
| if (e instanceof ContainerEvent) |
| processContainerEvent((ContainerEvent) e); |
| else |
| super.processEvent(e); |
| } |
| |
| /** |
| * Called when a container event occurs if container events are enabled. |
| * This method calls any registered listeners. |
| * |
| * @param e The event that occurred. |
| */ |
| protected void processContainerEvent(ContainerEvent e) |
| { |
| if (containerListener == null) |
| return; |
| switch (e.id) |
| { |
| case ContainerEvent.COMPONENT_ADDED: |
| containerListener.componentAdded(e); |
| break; |
| |
| case ContainerEvent.COMPONENT_REMOVED: |
| containerListener.componentRemoved(e); |
| break; |
| } |
| } |
| |
| /** |
| * AWT 1.0 event processor. |
| * |
| * @param e The event that occurred. |
| * |
| * @deprecated use {@link #dispatchEvent(AWTEvent)} instead |
| */ |
| public void deliverEvent(Event e) |
| { |
| if (!handleEvent (e)) |
| { |
| synchronized (getTreeLock ()) |
| { |
| Component parent = getParent (); |
| |
| if (parent != null) |
| parent.deliverEvent (e); |
| } |
| } |
| } |
| |
| /** |
| * Returns the component located at the specified point. This is done |
| * by checking whether or not a child component claims to contain this |
| * point. The first child component that does is returned. If no |
| * child component claims the point, the container itself is returned, |
| * unless the point does not exist within this container, in which |
| * case <code>null</code> is returned. |
| * |
| * @param x The X coordinate of the point. |
| * @param y The Y coordinate of the point. |
| * |
| * @return The component containing the specified point, or |
| * <code>null</code> if there is no such point. |
| */ |
| public Component getComponentAt(int x, int y) |
| { |
| return locate (x, y); |
| } |
| |
| /** |
| * Returns the component located at the specified point. This is done |
| * by checking whether or not a child component claims to contain this |
| * point. The first child component that does is returned. If no |
| * child component claims the point, the container itself is returned, |
| * unless the point does not exist within this container, in which |
| * case <code>null</code> is returned. |
| * |
| * @param x The x position of the point to return the component at. |
| * @param y The y position of the point to return the component at. |
| * |
| * @return The component containing the specified point, or <code>null</code> |
| * if there is no such point. |
| * |
| * @deprecated use {@link #getComponentAt(int, int)} instead |
| */ |
| public Component locate(int x, int y) |
| { |
| synchronized (getTreeLock ()) |
| { |
| if (!contains (x, y)) |
| return null; |
| for (int i = 0; i < ncomponents; ++i) |
| { |
| // Ignore invisible children... |
| if (!component[i].isVisible ()) |
| continue; |
| |
| int x2 = x - component[i].x; |
| int y2 = y - component[i].y; |
| if (component[i].contains (x2, y2)) |
| return component[i]; |
| } |
| return this; |
| } |
| } |
| |
| /** |
| * Returns the component located at the specified point. This is done |
| * by checking whether or not a child component claims to contain this |
| * point. The first child component that does is returned. If no |
| * child component claims the point, the container itself is returned, |
| * unless the point does not exist within this container, in which |
| * case <code>null</code> is returned. |
| * |
| * @param p The point to return the component at. |
| * @return The component containing the specified point, or <code>null</code> |
| * if there is no such point. |
| */ |
| public Component getComponentAt(Point p) |
| { |
| return getComponentAt (p.x, p.y); |
| } |
| |
| public Component findComponentAt(int x, int y) |
| { |
| synchronized (getTreeLock ()) |
| { |
| if (! contains(x, y)) |
| return null; |
| |
| for (int i = 0; i < ncomponents; ++i) |
| { |
| // Ignore invisible children... |
| if (!component[i].isVisible()) |
| continue; |
| |
| int x2 = x - component[i].x; |
| int y2 = y - component[i].y; |
| // We don't do the contains() check right away because |
| // findComponentAt would redundantly do it first thing. |
| if (component[i] instanceof Container) |
| { |
| Container k = (Container) component[i]; |
| Component r = k.findComponentAt(x2, y2); |
| if (r != null) |
| return r; |
| } |
| else if (component[i].contains(x2, y2)) |
| return component[i]; |
| } |
| |
| return this; |
| } |
| } |
| |
| public Component findComponentAt(Point p) |
| { |
| return findComponentAt(p.x, p.y); |
| } |
| |
| /** |
| * Called when this container is added to another container to inform it |
| * to create its peer. Peers for any child components will also be |
| * created. |
| */ |
| public void addNotify() |
| { |
| super.addNotify(); |
| addNotifyContainerChildren(); |
| } |
| |
| /** |
| * Called when this container is removed from its parent container to |
| * inform it to destroy its peer. This causes the peers of all child |
| * component to be destroyed as well. |
| */ |
| public void removeNotify() |
| { |
| synchronized (getTreeLock ()) |
| { |
| for (int i = 0; i < ncomponents; ++i) |
| component[i].removeNotify(); |
| super.removeNotify(); |
| } |
| } |
| |
| /** |
| * Tests whether or not the specified component is contained within |
| * this components subtree. |
| * |
| * @param comp The component to test. |
| * |
| * @return <code>true</code> if this container is an ancestor of the |
| * specified component, <code>false</code> otherwise. |
| */ |
| public boolean isAncestorOf(Component comp) |
| { |
| synchronized (getTreeLock ()) |
| { |
| while (true) |
| { |
| if (comp == null) |
| return false; |
| if (comp == this) |
| return true; |
| comp = comp.getParent(); |
| } |
| } |
| } |
| |
| /** |
| * Returns a string representing the state of this container for |
| * debugging purposes. |
| * |
| * @return A string representing the state of this container. |
| */ |
| protected String paramString() |
| { |
| if (layoutMgr == null) |
| return super.paramString(); |
| |
| StringBuffer sb = new StringBuffer(); |
| sb.append(super.paramString()); |
| sb.append(",layout="); |
| sb.append(layoutMgr.getClass().getName()); |
| return sb.toString(); |
| } |
| |
| /** |
| * Writes a listing of this container to the specified stream starting |
| * at the specified indentation point. |
| * |
| * @param out The <code>PrintStream</code> to write to. |
| * @param indent The indentation point. |
| */ |
| public void list(PrintStream out, int indent) |
| { |
| synchronized (getTreeLock ()) |
| { |
| super.list(out, indent); |
| for (int i = 0; i < ncomponents; ++i) |
| component[i].list(out, indent + 2); |
| } |
| } |
| |
| /** |
| * Writes a listing of this container to the specified stream starting |
| * at the specified indentation point. |
| * |
| * @param out The <code>PrintWriter</code> to write to. |
| * @param indent The indentation point. |
| */ |
| public void list(PrintWriter out, int indent) |
| { |
| synchronized (getTreeLock ()) |
| { |
| super.list(out, indent); |
| for (int i = 0; i < ncomponents; ++i) |
| component[i].list(out, indent + 2); |
| } |
| } |
| |
| /** |
| * Sets the focus traversal keys for a given traversal operation for this |
| * Container. |
| * |
| * @exception IllegalArgumentException If id is not one of |
| * KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, |
| * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, |
| * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS, |
| * or KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS, |
| * or if keystrokes contains null, or if any Object in keystrokes is not an |
| * AWTKeyStroke, or if any keystroke represents a KEY_TYPED event, or if any |
| * keystroke already maps to another focus traversal operation for this |
| * Container. |
| * |
| * @since 1.4 |
| */ |
| public void setFocusTraversalKeys(int id, Set keystrokes) |
| { |
| if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && |
| id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && |
| id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS && |
| id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS) |
| throw new IllegalArgumentException (); |
| |
| if (keystrokes == null) |
| { |
| Container parent = getParent (); |
| |
| while (parent != null) |
| { |
| if (parent.areFocusTraversalKeysSet (id)) |
| { |
| keystrokes = parent.getFocusTraversalKeys (id); |
| break; |
| } |
| parent = parent.getParent (); |
| } |
| |
| if (keystrokes == null) |
| keystrokes = KeyboardFocusManager.getCurrentKeyboardFocusManager (). |
| getDefaultFocusTraversalKeys (id); |
| } |
| |
| Set sa; |
| Set sb; |
| Set sc; |
| String name; |
| switch (id) |
| { |
| case KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS: |
| sa = getFocusTraversalKeys |
| (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); |
| sb = getFocusTraversalKeys |
| (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS); |
| sc = getFocusTraversalKeys |
| (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS); |
| name = "forwardFocusTraversalKeys"; |
| break; |
| case KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS: |
| sa = getFocusTraversalKeys |
| (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); |
| sb = getFocusTraversalKeys |
| (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS); |
| sc = getFocusTraversalKeys |
| (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS); |
| name = "backwardFocusTraversalKeys"; |
| break; |
| case KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS: |
| sa = getFocusTraversalKeys |
| (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); |
| sb = getFocusTraversalKeys |
| (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); |
| sc = getFocusTraversalKeys |
| (KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS); |
| name = "upCycleFocusTraversalKeys"; |
| break; |
| case KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS: |
| sa = getFocusTraversalKeys |
| (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS); |
| sb = getFocusTraversalKeys |
| (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS); |
| sc = getFocusTraversalKeys |
| (KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS); |
| name = "downCycleFocusTraversalKeys"; |
| break; |
| default: |
| throw new IllegalArgumentException (); |
| } |
| |
| int i = keystrokes.size (); |
| Iterator iter = keystrokes.iterator (); |
| |
| while (--i >= 0) |
| { |
| Object o = iter.next (); |
| if (!(o instanceof AWTKeyStroke) |
| || sa.contains (o) || sb.contains (o) || sc.contains (o) |
| || ((AWTKeyStroke) o).keyCode == KeyEvent.VK_UNDEFINED) |
| throw new IllegalArgumentException (); |
| } |
| |
| if (focusTraversalKeys == null) |
| focusTraversalKeys = new Set[3]; |
| |
| keystrokes = Collections.unmodifiableSet (new HashSet (keystrokes)); |
| firePropertyChange (name, focusTraversalKeys[id], keystrokes); |
| |
| focusTraversalKeys[id] = keystrokes; |
| } |
| |
| /** |
| * Returns the Set of focus traversal keys for a given traversal operation for |
| * this Container. |
| * |
| * @exception IllegalArgumentException If id is not one of |
| * KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, |
| * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, |
| * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS, |
| * or KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS. |
| * |
| * @since 1.4 |
| */ |
| public Set getFocusTraversalKeys (int id) |
| { |
| if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && |
| id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && |
| id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS && |
| id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS) |
| throw new IllegalArgumentException (); |
| |
| Set s = null; |
| |
| if (focusTraversalKeys != null) |
| s = focusTraversalKeys[id]; |
| |
| if (s == null && parent != null) |
| s = parent.getFocusTraversalKeys (id); |
| |
| return s == null ? (KeyboardFocusManager.getCurrentKeyboardFocusManager() |
| .getDefaultFocusTraversalKeys(id)) : s; |
| } |
| |
| /** |
| * Returns whether the Set of focus traversal keys for the given focus |
| * traversal operation has been explicitly defined for this Container. |
| * If this method returns false, this Container is inheriting the Set from |
| * an ancestor, or from the current KeyboardFocusManager. |
| * |
| * @exception IllegalArgumentException If id is not one of |
| * KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, |
| * KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, |
| * KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS, |
| * or KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS. |
| * |
| * @since 1.4 |
| */ |
| public boolean areFocusTraversalKeysSet (int id) |
| { |
| if (id != KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS && |
| id != KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS && |
| id != KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS && |
| id != KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS) |
| throw new IllegalArgumentException (); |
| |
| return focusTraversalKeys != null && focusTraversalKeys[id] != null; |
| } |
| |
| /** |
| * Check whether the given Container is the focus cycle root of this |
| * Container's focus traversal cycle. If this Container is a focus |
| * cycle root itself, then it will be in two different focus cycles |
| * -- it's own, and that of its ancestor focus cycle root's. In |
| * that case, if <code>c</code> is either of those containers, this |
| * method will return true. |
| * |
| * @param c the candidate Container |
| * |
| * @return true if c is the focus cycle root of the focus traversal |
| * cycle to which this Container belongs, false otherwise |
| * |
| * @since 1.4 |
| */ |
| public boolean isFocusCycleRoot (Container c) |
| { |
| if (this == c |
| && isFocusCycleRoot ()) |
| return true; |
| |
| Container ancestor = getFocusCycleRootAncestor (); |
| |
| if (c == ancestor) |
| return true; |
| |
| return false; |
| } |
| |
| /** |
| * If this Container is a focus cycle root, set the focus traversal |
| * policy that determines the focus traversal order for its |
| * children. If non-null, this policy will be inherited by all |
| * inferior focus cycle roots. If <code>policy</code> is null, this |
| * Container will inherit its policy from the closest ancestor focus |
| * cycle root that's had its policy set. |
| * |
| * @param policy the new focus traversal policy for this Container or null |
| * |
| * @since 1.4 |
| */ |
| public void setFocusTraversalPolicy (FocusTraversalPolicy policy) |
| { |
| focusTraversalPolicy = policy; |
| } |
| |
| /** |
| * Return the focus traversal policy that determines the focus |
| * traversal order for this Container's children. This method |
| * returns null if this Container is not a focus cycle root. If the |
| * focus traversal policy has not been set explicitly, then this |
| * method will return an ancestor focus cycle root's policy instead. |
| * |
| * @return this Container's focus traversal policy or null |
| * |
| * @since 1.4 |
| */ |
| public FocusTraversalPolicy getFocusTraversalPolicy () |
| { |
| if (!isFocusCycleRoot ()) |
| return null; |
| |
| if (focusTraversalPolicy == null) |
| { |
| Container ancestor = getFocusCycleRootAncestor (); |
| |
| if (ancestor != this) |
| return ancestor.getFocusTraversalPolicy (); |
| else |
| { |
| KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); |
| |
| return manager.getDefaultFocusTraversalPolicy (); |
| } |
| } |
| else |
| return focusTraversalPolicy; |
| } |
| |
| /** |
| * Check whether this Container's focus traversal policy has been |
| * explicitly set. If it has not, then this Container will inherit |
| * its focus traversal policy from one of its ancestor focus cycle |
| * roots. |
| * |
| * @return true if focus traversal policy is set, false otherwise |
| */ |
| public boolean isFocusTraversalPolicySet () |
| { |
| return focusTraversalPolicy == null; |
| } |
| |
| /** |
| * Set whether or not this Container is the root of a focus |
| * traversal cycle. This Container's focus traversal policy |
| * determines the order of focus traversal. Some policies prevent |
| * the focus from being transferred between two traversal cycles |
| * until an up or down traversal operation is performed. In that |
| * case, normal traversal (not up or down) is limited to this |
| * Container and all of this Container's descendents that are not |
| * descendents of inferior focus cycle roots. In the default case |
| * however, ContainerOrderFocusTraversalPolicy is in effect, and it |
| * supports implicit down-cycle traversal operations. |
| * |
| * @return true if this is a focus cycle root, false otherwise |
| * |
| * @since 1.4 |
| */ |
| public void setFocusCycleRoot (boolean focusCycleRoot) |
| { |
| this.focusCycleRoot = focusCycleRoot; |
| } |
| |
| /** |
| * Check whether this Container is a focus cycle root. |
| * |
| * @return true if this is a focus cycle root, false otherwise |
| * |
| * @since 1.4 |
| */ |
| public boolean isFocusCycleRoot () |
| { |
| return focusCycleRoot; |
| } |
| |
| /** |
| * Transfer focus down one focus traversal cycle. If this Container |
| * is a focus cycle root, then its default component becomes the |
| * focus owner, and this Container becomes the current focus cycle |
| * root. No traversal will occur if this Container is not a focus |
| * cycle root. |
| * |
| * @since 1.4 |
| */ |
| public void transferFocusDownCycle () |
| { |
| KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); |
| |
| manager.downFocusCycle (this); |
| } |
| |
| /** |
| * Sets the ComponentOrientation property of this container and all components |
| * contained within it. |
| * |
| * @exception NullPointerException If orientation is null |
| * |
| * @since 1.4 |
| */ |
| public void applyComponentOrientation (ComponentOrientation orientation) |
| { |
| if (orientation == null) |
| throw new NullPointerException (); |
| } |
| |
| public void addPropertyChangeListener (PropertyChangeListener listener) |
| { |
| if (listener == null) |
| return; |
| |
| if (changeSupport == null) |
| changeSupport = new PropertyChangeSupport (this); |
| |
| changeSupport.addPropertyChangeListener (listener); |
| } |
| |
| public void addPropertyChangeListener (String name, |
| PropertyChangeListener listener) |
| { |
| if (listener == null) |
| return; |
| |
| if (changeSupport == null) |
| changeSupport = new PropertyChangeSupport (this); |
| |
| changeSupport.addPropertyChangeListener (name, listener); |
| } |
| |
| // Hidden helper methods. |
| |
| /** |
| * Perform a graphics operation on the children of this container. |
| * For each applicable child, the visitChild() method will be called |
| * to perform the graphics operation. |
| * |
| * @param gfx The graphics object that will be used to derive new |
| * graphics objects for the children. |
| * |
| * @param visitor Object encapsulating the graphics operation that |
| * should be performed. |
| * |
| * @param lightweightOnly If true, only lightweight components will |
| * be visited. |
| */ |
| private void visitChildren(Graphics gfx, GfxVisitor visitor, |
| boolean lightweightOnly) |
| { |
| synchronized (getTreeLock ()) |
| { |
| for (int i = ncomponents - 1; i >= 0; --i) |
| { |
| Component comp = component[i]; |
| // If we're visiting heavyweights as well, |
| // don't recurse into Containers here. This avoids |
| // painting the same nested child multiple times. |
| boolean applicable = comp.isVisible() |
| && (comp.isLightweight() |
| || !lightweightOnly && ! (comp instanceof Container)); |
| |
| if (applicable) |
| visitChild(gfx, visitor, comp); |
| } |
| } |
| } |
| |
| /** |
| * Perform a graphics operation on a child. A translated and clipped |
| * graphics object will be created, and the visit() method of the |
| * visitor will be called to perform the operation. |
| * |
| * @param gfx The graphics object that will be used to derive new |
| * graphics objects for the child. |
| * |
| * @param visitor Object encapsulating the graphics operation that |
| * should be performed. |
| * |
| * @param comp The child component that should be visited. |
| */ |
| private void visitChild(Graphics gfx, GfxVisitor visitor, |
| Component comp) |
| { |
| Rectangle bounds = comp.getBounds(); |
| Rectangle oldClip = gfx.getClipBounds(); |
| if (oldClip == null) |
| oldClip = bounds; |
| |
| Rectangle clip = oldClip.intersection(bounds); |
| |
| if (clip.isEmpty()) return; |
| |
| boolean clipped = false; |
| boolean translated = false; |
| try |
| { |
| gfx.setClip(clip.x, clip.y, clip.width, clip.height); |
| clipped = true; |
| gfx.translate(bounds.x, bounds.y); |
| translated = true; |
| visitor.visit(comp, gfx); |
| } |
| finally |
| { |
| if (translated) |
| gfx.translate (-bounds.x, -bounds.y); |
| if (clipped) |
| gfx.setClip (oldClip.x, oldClip.y, oldClip.width, oldClip.height); |
| } |
| } |
| |
| void dispatchEventImpl(AWTEvent e) |
| { |
| // Give lightweight dispatcher a chance to handle it. |
| if (eventTypeEnabled (e.id) |
| && dispatcher != null |
| && dispatcher.handleEvent (e)) |
| return; |
| |
| if ((e.id <= ContainerEvent.CONTAINER_LAST |
| && e.id >= ContainerEvent.CONTAINER_FIRST) |
| && (containerListener != null |
| || (eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0)) |
| processEvent(e); |
| else |
| super.dispatchEventImpl(e); |
| } |
| |
| // This is used to implement Component.transferFocus. |
| Component findNextFocusComponent(Component child) |
| { |
| synchronized (getTreeLock ()) |
| { |
| int start, end; |
| if (child != null) |
| { |
| for (start = 0; start < ncomponents; ++start) |
| { |
| if (component[start] == child) |
| break; |
| } |
| end = start; |
| // This special case lets us be sure to terminate. |
| if (end == 0) |
| end = ncomponents; |
| ++start; |
| } |
| else |
| { |
| start = 0; |
| end = ncomponents; |
| } |
| |
| for (int j = start; j != end; ++j) |
| { |
| if (j >= ncomponents) |
| { |
| // The JCL says that we should wrap here. However, that |
| // seems wrong. To me it seems that focus order should be |
| // global within in given window. So instead if we reach |
| // the end we try to look in our parent, if we have one. |
| if (parent != null) |
| return parent.findNextFocusComponent(this); |
| j -= ncomponents; |
| } |
| if (component[j] instanceof Container) |
| { |
| Component c = component[j]; |
| c = c.findNextFocusComponent(null); |
| if (c != null) |
| return c; |
| } |
| else if (component[j].isFocusTraversable()) |
| return component[j]; |
| } |
| |
| return null; |
| } |
| } |
| |
| private void addNotifyContainerChildren() |
| { |
| synchronized (getTreeLock ()) |
| { |
| for (int i = ncomponents; --i >= 0; ) |
| { |
| component[i].addNotify(); |
| if (component[i].isLightweight ()) |
| { |
| |
| // If we're not lightweight, and we just got a lightweight |
| // child, we need a lightweight dispatcher to feed it events. |
| if (! this.isLightweight()) |
| { |
| if (dispatcher == null) |
| dispatcher = new LightweightDispatcher (this); |
| } |
| |
| |
| enableEvents(component[i].eventMask); |
| if (peer != null && !isLightweight ()) |
| enableEvents (AWTEvent.PAINT_EVENT_MASK); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Deserialize this Container: |
| * <ol> |
| * <li>Read from the stream the default serializable fields.</li> |
| * <li>Read a list of serializable ContainerListeners as optional |
| * data. If the list is null, no listeners will be registered.</li> |
| * <li>Read this Container's FocusTraversalPolicy as optional data. |
| * If this is null, then this Container will use a |
| * DefaultFocusTraversalPolicy.</li> |
| * </ol> |
| * |
| * @param s the stream to read from |
| * @throws ClassNotFoundException if deserialization fails |
| * @throws IOException if the stream fails |
| */ |
| private void readObject (ObjectInputStream s) |
| throws ClassNotFoundException, IOException |
| { |
| s.defaultReadObject (); |
| String key = (String) s.readObject (); |
| while (key != null) |
| { |
| Object object = s.readObject (); |
| if ("containerL".equals (key)) |
| addContainerListener((ContainerListener) object); |
| // FIXME: under what key is the focus traversal policy stored? |
| else if ("focusTraversalPolicy".equals (key)) |
| setFocusTraversalPolicy ((FocusTraversalPolicy) object); |
| |
| key = (String) s.readObject(); |
| } |
| } |
| |
| /** |
| * Serialize this Container: |
| * <ol> |
| * <li>Write to the stream the default serializable fields.</li> |
| * <li>Write the list of serializable ContainerListeners as optional |
| * data.</li> |
| * <li>Write this Container's FocusTraversalPolicy as optional data.</li> |
| * </ol> |
| * |
| * @param s the stream to write to |
| * @throws IOException if the stream fails |
| */ |
| private void writeObject (ObjectOutputStream s) throws IOException |
| { |
| s.defaultWriteObject (); |
| AWTEventMulticaster.save (s, "containerL", containerListener); |
| if (focusTraversalPolicy instanceof Serializable) |
| s.writeObject (focusTraversalPolicy); |
| else |
| s.writeObject (null); |
| } |
| |
| // Nested classes. |
| |
| /* The following classes are used in concert with the |
| visitChildren() method to implement all the graphics operations |
| that requires traversal of the containment hierarchy. */ |
| |
| abstract static class GfxVisitor |
| { |
| public abstract void visit(Component c, Graphics gfx); |
| } |
| |
| static class GfxPaintVisitor extends GfxVisitor |
| { |
| public static final GfxVisitor INSTANCE = new GfxPaintVisitor(); |
| |
| public void visit(Component c, Graphics gfx) |
| { |
| c.paint(gfx); |
| } |
| } |
| |
| static class GfxPrintVisitor extends GfxVisitor |
| { |
| public static final GfxVisitor INSTANCE = new GfxPrintVisitor(); |
| |
| public void visit(Component c, Graphics gfx) |
| { |
| c.print(gfx); |
| } |
| } |
| |
| static class GfxPaintAllVisitor extends GfxVisitor |
| { |
| public static final GfxVisitor INSTANCE = new GfxPaintAllVisitor(); |
| |
| public void visit(Component c, Graphics gfx) |
| { |
| c.paintAll(gfx); |
| } |
| } |
| |
| static class GfxPrintAllVisitor extends GfxVisitor |
| { |
| public static final GfxVisitor INSTANCE = new GfxPrintAllVisitor(); |
| |
| public void visit(Component c, Graphics gfx) |
| { |
| c.printAll(gfx); |
| } |
| } |
| |
| /** |
| * This class provides accessibility support for subclasses of container. |
| * |
| * @author Eric Blake (ebb9@email.byu.edu) |
| * |
| * @since 1.3 |
| */ |
| protected class AccessibleAWTContainer extends AccessibleAWTComponent |
| { |
| /** |
| * Compatible with JDK 1.4+. |
| */ |
| private static final long serialVersionUID = 5081320404842566097L; |
| |
| /** |
| * The handler to fire PropertyChange when children are added or removed. |
| * |
| * @serial the handler for property changes |
| */ |
| protected ContainerListener accessibleContainerHandler |
| = new AccessibleContainerHandler(); |
| |
| /** |
| * The default constructor. |
| */ |
| protected AccessibleAWTContainer() |
| { |
| Container.this.addContainerListener(accessibleContainerHandler); |
| } |
| |
| /** |
| * Return the number of accessible children of the containing accessible |
| * object (at most the total number of its children). |
| * |
| * @return the number of accessible children |
| */ |
| public int getAccessibleChildrenCount() |
| { |
| synchronized (getTreeLock ()) |
| { |
| int count = 0; |
| int i = component == null ? 0 : component.length; |
| while (--i >= 0) |
| if (component[i] instanceof Accessible) |
| count++; |
| return count; |
| } |
| } |
| |
| /** |
| * Return the nth accessible child of the containing accessible object. |
| * |
| * @param i the child to grab, zero-based |
| * @return the accessible child, or null |
| */ |
| public Accessible getAccessibleChild(int i) |
| { |
| synchronized (getTreeLock ()) |
| { |
| if (component == null) |
| return null; |
| int index = -1; |
| while (i >= 0 && ++index < component.length) |
| if (component[index] instanceof Accessible) |
| i--; |
| if (i < 0) |
| return (Accessible) component[index]; |
| return null; |
| } |
| } |
| |
| /** |
| * Return the accessible child located at point (in the parent's |
| * coordinates), if one exists. |
| * |
| * @param p the point to look at |
| * |
| * @return an accessible object at that point, or null |
| * |
| * @throws NullPointerException if p is null |
| */ |
| public Accessible getAccessibleAt(Point p) |
| { |
| Component c = getComponentAt(p.x, p.y); |
| return c != Container.this && c instanceof Accessible ? (Accessible) c |
| : null; |
| } |
| |
| /** |
| * This class fires a <code>PropertyChange</code> listener, if registered, |
| * when children are added or removed from the enclosing accessible object. |
| * |
| * @author Eric Blake (ebb9@email.byu.edu) |
| * |
| * @since 1.3 |
| */ |
| protected class AccessibleContainerHandler implements ContainerListener |
| { |
| /** |
| * Default constructor. |
| */ |
| protected AccessibleContainerHandler() |
| { |
| } |
| |
| /** |
| * Fired when a component is added; forwards to the PropertyChange |
| * listener. |
| * |
| * @param e the container event for adding |
| */ |
| public void componentAdded(ContainerEvent e) |
| { |
| AccessibleAWTContainer.this.firePropertyChange |
| (ACCESSIBLE_CHILD_PROPERTY, null, e.getChild()); |
| } |
| |
| /** |
| * Fired when a component is removed; forwards to the PropertyChange |
| * listener. |
| * |
| * @param e the container event for removing |
| */ |
| public void componentRemoved(ContainerEvent e) |
| { |
| AccessibleAWTContainer.this.firePropertyChange |
| (ACCESSIBLE_CHILD_PROPERTY, e.getChild(), null); |
| } |
| } // class AccessibleContainerHandler |
| } // class AccessibleAWTContainer |
| } // class Container |
| |
| /** |
| * There is a helper class implied from stack traces called |
| * LightweightDispatcher, but since it is not part of the public API, |
| * rather than mimic it exactly we write something which does "roughly |
| * the same thing". |
| */ |
| |
| class LightweightDispatcher implements Serializable |
| { |
| private static final long serialVersionUID = 5184291520170872969L; |
| private Container nativeContainer; |
| private Cursor nativeCursor; |
| private long eventMask; |
| |
| private transient Component mouseEventTarget; |
| private transient Component pressedComponent; |
| private transient Component lastComponentEntered; |
| private transient int pressCount; |
| |
| LightweightDispatcher(Container c) |
| { |
| nativeContainer = c; |
| } |
| |
| void acquireComponentForMouseEvent(MouseEvent me) |
| { |
| int x = me.getX (); |
| int y = me.getY (); |
| |
| // Find the candidate which should receive this event. |
| Component parent = nativeContainer; |
| Component candidate = null; |
| Point p = me.getPoint(); |
| while (candidate == null && parent != null) |
| { |
| candidate = |
| SwingUtilities.getDeepestComponentAt(parent, p.x, p.y); |
| if (candidate == null || (candidate.eventMask & me.getID()) == 0) |
| { |
| candidate = null; |
| p = SwingUtilities.convertPoint(parent, p.x, p.y, parent.parent); |
| parent = parent.parent; |
| } |
| } |
| |
| // If the only candidate we found was the native container itself, |
| // don't dispatch any event at all. We only care about the lightweight |
| // children here. |
| if (candidate == nativeContainer) |
| candidate = null; |
| |
| // If our candidate is new, inform the old target we're leaving. |
| if (lastComponentEntered != null |
| && lastComponentEntered.isShowing() |
| && lastComponentEntered != candidate) |
| { |
| // Old candidate could have been removed from |
| // the nativeContainer so we check first. |
| if (SwingUtilities.isDescendingFrom(lastComponentEntered, nativeContainer)) |
| { |
| Point tp = |
| SwingUtilities.convertPoint(nativeContainer, |
| x, y, lastComponentEntered); |
| MouseEvent exited = new MouseEvent (lastComponentEntered, |
| MouseEvent.MOUSE_EXITED, |
| me.getWhen (), |
| me.getModifiersEx (), |
| tp.x, tp.y, |
| me.getClickCount (), |
| me.isPopupTrigger (), |
| me.getButton ()); |
| lastComponentEntered.dispatchEvent (exited); |
| } |
| lastComponentEntered = null; |
| } |
| // If we have a candidate, maybe enter it. |
| if (candidate != null) |
| { |
| mouseEventTarget = candidate; |
| if (candidate.isLightweight() |
| && candidate.isShowing() |
| && candidate != nativeContainer |
| && candidate != lastComponentEntered) |
| { |
| lastComponentEntered = mouseEventTarget; |
| Point cp = SwingUtilities.convertPoint(nativeContainer, |
| x, y, lastComponentEntered); |
| MouseEvent entered = new MouseEvent (lastComponentEntered, |
| MouseEvent.MOUSE_ENTERED, |
| me.getWhen (), |
| me.getModifiersEx (), |
| cp.x, cp.y, |
| me.getClickCount (), |
| me.isPopupTrigger (), |
| me.getButton ()); |
| lastComponentEntered.dispatchEvent (entered); |
| } |
| } |
| |
| if (me.getID() == MouseEvent.MOUSE_RELEASED |
| || me.getID() == MouseEvent.MOUSE_PRESSED && pressCount > 0 |
| || me.getID() == MouseEvent.MOUSE_DRAGGED) |
| // If any of the following events occur while a button is held down, |
| // they should be dispatched to the same component to which the |
| // original MOUSE_PRESSED event was dispatched: |
| // - MOUSE_RELEASED |
| // - MOUSE_PRESSED: another button pressed while the first is held down |
| // - MOUSE_DRAGGED |
| if (SwingUtilities.isDescendingFrom(pressedComponent, nativeContainer)) |
| mouseEventTarget = pressedComponent; |
| else if (me.getID() == MouseEvent.MOUSE_CLICKED) |
| { |
| // Don't dispatch CLICKED events whose target is not the same as the |
| // target for the original PRESSED event. |
| if (candidate != pressedComponent) |
| mouseEventTarget = null; |
| else if (pressCount == 0) |
| pressedComponent = null; |
| } |
| } |
| |
| boolean handleEvent(AWTEvent e) |
| { |
| if (e instanceof MouseEvent) |
| { |
| MouseEvent me = (MouseEvent) e; |
| |
| acquireComponentForMouseEvent(me); |
| |
| // Avoid dispatching ENTERED and EXITED events twice. |
| if (mouseEventTarget != null |
| && mouseEventTarget.isShowing() |
| && e.getID() != MouseEvent.MOUSE_ENTERED |
| && e.getID() != MouseEvent.MOUSE_EXITED) |
| { |
| MouseEvent newEvt = |
| SwingUtilities.convertMouseEvent(nativeContainer, me, |
| mouseEventTarget); |
| mouseEventTarget.dispatchEvent(newEvt); |
| |
| switch (e.getID()) |
| { |
| case MouseEvent.MOUSE_PRESSED: |
| if (pressCount++ == 0) |
| pressedComponent = mouseEventTarget; |
| break; |
| |
| case MouseEvent.MOUSE_RELEASED: |
| // Clear our memory of the original PRESSED event, only if |
| // we're not expecting a CLICKED event after this. If |
| // there is a CLICKED event after this, it will do clean up. |
| if (--pressCount == 0 |
| && mouseEventTarget != pressedComponent) |
| pressedComponent = null; |
| break; |
| } |
| if (newEvt.isConsumed()) |
| e.consume(); |
| } |
| } |
| |
| return e.isConsumed(); |
| } |
| |
| } // class LightweightDispatcher |