/*
* @(#)Container.java 1.187 06/10/10
*
* Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program 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 version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*
*/
package java.awt;
import java.io.PrintStream;
import java.io.PrintWriter;
import sun.awt.peer.ComponentPeer;
import sun.awt.peer.ContainerPeer;
import java.awt.event.*;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.IOException;
import sun.awt.ConstrainableGraphics;
import java.util.Set;
import java.util.EventListener;
import java.awt.AWTEventMulticaster;
/**
* A generic Abstract Window Toolkit(AWT) container object is a component
* that can contain other AWT components.
* <p>
* Components added to a container are tracked in a list. The order
* of the list will define the components' front-to-back stacking order
* within the container. If no index is specified when adding a
* component to a container, it will be added to the end of the list
* (and hence to the bottom of the stacking order).
* @version 1.173, 08/19/02
* @author Arthur van Hoff
* @author Sami Shaio
* @see java.awt.Container#add(java.awt.Component, int)
* @see java.awt.Container#getComponent(int)
* @see java.awt.LayoutManager
* @since JDK1.0
*/
public class Container extends Component {
/**
* The number of components in this container.
*/
int ncomponents;
/**
* The components in this container.
*/
Component component[] = new Component[4];
/**
* Layout manager for this container.
*/
LayoutManager layoutMgr;
/**
* Event router for lightweight components. If this container
* is native, this dispatcher takes care of forwarding and
* retargeting the events to lightweight components contained
* (if any).
*/
private transient LightweightDispatcher dispatcher;
/** Internal, cached size information */
private Dimension maxSize;
transient ContainerListener containerListener;
/*
* JDK 1.1 serialVersionUID
*/
private static final long serialVersionUID = 4613797578919906343L;
/**
* Constructs a new Container. Containers can be extended directly,
* but are lightweight in this case and must be contained by a parent
* somewhere higher up in the component tree that is native.
* (such as Frame for example).
*/
public Container() {}
/**
* Gets the number of components in this panel.
* @return the number of components in this panel.
* @see java.awt.Container#getComponent
* @since JDK1.1
*/
public int getComponentCount() {
return countComponents();
}
/**
* @deprecated As of JDK version 1.1,
* replaced by getComponentCount().
*/
public int countComponents() {
return ncomponents;
}
/**
* Gets the nth component in this container.
* @param n the index of the component to get.
* @return the n<sup>th</sup> component in this container.
* @exception ArrayIndexOutOfBoundsException
* if the n<sup>th</sup> value does not exist.
* @since JDK1.0
*/
public Component getComponent(int n) {
synchronized (getTreeLock()) {
if ((n < 0) || (n >= ncomponents)) {
throw new ArrayIndexOutOfBoundsException("No such child: " + n);
}
return component[n];
}
}
/**
* Gets all the components in this container.
* @return an array of all the components in this container.
* @since JDK1.0
*/
public Component[] getComponents() {
synchronized (getTreeLock()) {
Component list[] = new Component[ncomponents];
System.arraycopy(component, 0, list, 0, ncomponents);
return list;
}
}
/**
* Determines the insets of this container, which indicate the size
* of the container's border.
* <p>
* A <code>Frame</code> object, for example, has a top inset that
* corresponds to the height of the frame's title bar.
* @return the insets of this container.
* @see java.awt.Insets
* @see java.awt.LayoutManager
* @since JDK1.1
*/
public Insets getInsets() {
return insets();
}
/**
* @deprecated As of JDK version 1.1,
* replaced by <code>getInsets()</code>.
*/
public Insets insets() {
if (this.peer != null && this.peer instanceof ContainerPeer) {
ContainerPeer peer = (ContainerPeer) this.peer;
return (Insets) peer.getInsets().clone();
}
return new Insets(0, 0, 0, 0);
}
/**
* Adds the specified component to the end of this container.
* @param comp the component to be added.
* @return the component argument.
* @since JDK1.0
*/
public Component add(Component comp) {
addImpl(comp, null, -1);
return comp;
}
/**
* Adds the specified component to this container.
* It is strongly advised to use the 1.1 method, add(Component, Object),
* in place of this method.
*/
public Component add(String name, Component comp) {
addImpl(comp, name, -1);
return comp;
}
/**
* Adds the specified component to this container at the given
* position.
* @param comp the component to be added.
* @param index the position at which to insert the component,
* or <code>-1</code> to insert the component at the end.
* @return the component <code>comp</code>
* @see #remove
* @since JDK1.0
*/
public Component add(Component comp, int index) {
addImpl(comp, null, index);
return comp;
}
/**
* Adds the specified component to the end of this container.
* Also notifies the layout manager to add the component to
* this container's layout using the specified constraints object.
* @param comp the component to be added
* @param constraints an object expressing
* layout contraints for this component
* @see java.awt.LayoutManager
* @since JDK1.1
*/
public void add(Component comp, Object constraints) {
addImpl(comp, constraints, -1);
}
/**
* Adds the specified component to this container with the specified
* constraints at the specified index. Also notifies the layout
* manager to add the component to the this container's layout using
* the specified constraints object.
* @param comp the component to be added
* @param constraints an object expressing layout contraints for this
* @param index the position in the container's list at which to insert
* the component. -1 means insert at the end.
* component
* @see #remove
* @see LayoutManager
*/
public void add(Component comp, Object constraints, int index) {
addImpl(comp, constraints, index);
}
/**
* Adds the specified component to this container at the specified
* index. This method also notifies the layout manager to add
* the component to this container's layout using the specified
* constraints object.
* <p>
* This is the method to override if a program needs to track
* every add request to a container. An overriding method should
* usually include a call to the superclass's version of the method:
* <p>
* <blockquote>
* <code>super.addImpl(comp, constraints, index)</code>
* </blockquote>
* <p>
* @param comp the component to be added.
* @param constraints an object expressing layout contraints
* for this component.
* @param index the position in the container's list at which to
* insert the component, where <code>-1</code>
* means insert at the end.
* @see java.awt.Container#add(java.awt.Component)
* @see java.awt.Container#add(java.awt.Component, int)
* @see java.awt.Container#add(java.awt.Component, java.lang.Object)
* @see java.awt.LayoutManager
* @since JDK1.1
*/
protected void addImpl(Component comp, Object constraints, int index) {
synchronized (getTreeLock()) {
/* Check for correct arguments: index in bounds,
* comp cannot be one of this container's parents,
* and comp cannot be a window.
*/
if (index > ncomponents || (index < 0 && index != -1)) {
throw new IllegalArgumentException(
"illegal component position");
}
if (comp instanceof Container) {
for (Container cn = this; cn != null; cn = cn.parent) {
if (cn == comp) {
throw new IllegalArgumentException(
"adding container's parent to itself");
}
}
}
if (comp instanceof Window) {
throw new IllegalArgumentException(
"adding a window to a container");
}
/* Reparent the component and tidy up the tree's state. */
if (comp.parent != null) {
comp.parent.remove(comp);
// 6262334
if (index > ncomponents) {
throw new
IllegalArgumentException("illegal component position");
}
// 6262334
}
/* Add component to list; allocate new array if necessary. */
if (ncomponents == component.length) {
Component newcomponents[] = new Component[ncomponents * 2];
System.arraycopy(component, 0, newcomponents, 0, ncomponents);
component = newcomponents;
}
if (index == -1 || index == ncomponents) {
component[ncomponents++] = comp;
} else {
System.arraycopy(component, index, component,
index + 1, ncomponents - index);
component[index] = comp;
ncomponents++;
}
comp.parent = this;
if (valid) {
invalidate();
}
if (peer != null) {
comp.addNotify();
}
/* Notify the layout manager of the added component. */
if (layoutMgr != null) {
if (layoutMgr instanceof LayoutManager2) {
((LayoutManager2) layoutMgr).addLayoutComponent(comp, constraints);
} else if (constraints instanceof String) {
layoutMgr.addLayoutComponent((String) constraints, comp);
}
}
if (containerListener != null ||
(eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0) {
ContainerEvent e = new ContainerEvent(this,
ContainerEvent.COMPONENT_ADDED,
comp);
processEvent(e);
}
}
}
/**
* Removes the component, specified by <code>index</code>,
* from this container.
* @param index the index of the component to be removed.
* @see #add
* @since JDK1.1
*/
public void remove(int index) {
synchronized (getTreeLock()) {
/* 4629242 - Check if Container contains any components or if
* index references a null array element.
*/
if (ncomponents == 0 || component[index] == null) {
throw new ArrayIndexOutOfBoundsException("Invalid index : " + index + ".");
}
Component comp = component[index];
if (peer != null) {
comp.removeNotify();
}
if (layoutMgr != null) {
layoutMgr.removeLayoutComponent(comp);
}
comp.parent = null;
System.arraycopy(component, index + 1,
component, index,
ncomponents - index - 1);
component[--ncomponents] = null;
if (valid) {
invalidate();
}
if (containerListener != null ||
(eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0) {
ContainerEvent e = new ContainerEvent(this,
ContainerEvent.COMPONENT_REMOVED,
comp);
processEvent(e);
}
return;
}
}
/**
* Removes the specified component from this container.
* @param comp the component to be removed
* @see #add
* @since JDK1.0
*/
public void remove(Component comp) {
synchronized (getTreeLock()) {
if (comp.parent == this) {
/* Search backwards, expect that more recent additions
* are more likely to be removed.
*/
Component component[] = this.component;
for (int i = ncomponents; --i >= 0;) {
if (component[i] == comp) {
remove(i);
}
}
}
}
}
/**
* Removes all the components from this container.
* @see #add
* @see #remove
* @since JDK1.0
*/
public void removeAll() {
synchronized (getTreeLock()) {
while (ncomponents > 0) {
Component comp = component[--ncomponents];
component[ncomponents] = null;
if (peer != null) {
comp.removeNotify();
}
if (layoutMgr != null) {
layoutMgr.removeLayoutComponent(comp);
}
comp.parent = null;
if (containerListener != null ||
(eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0) {
ContainerEvent e = new ContainerEvent(this,
ContainerEvent.COMPONENT_REMOVED,
comp);
processEvent(e);
}
}
if (valid) {
invalidate();
}
}
}
/**
* Gets the layout manager for this container.
* @see #doLayout
* @see #setLayout
* @since JDK1.0
*/
public LayoutManager getLayout() {
return layoutMgr;
}
/**
* Sets the layout manager for this container.
* @param mgr the specified layout manager
* @see #doLayout
* @see #getLayout
* @since JDK1.0
*/
public void setLayout(LayoutManager mgr) {
layoutMgr = mgr;
if (valid) {
invalidate();
}
}
/**
* Causes this container to lay out its components. Most programs
* should not call this method directly, but should invoke
* the <code>validate</code> method instead.
* @see java.awt.LayoutManager#layoutContainer
* @see #setLayout
* @see #validate
* @since JDK1.1
*/
public void doLayout() {
layout();
}
/**
* @deprecated As of JDK version 1.1,
* replaced by <code>doLayout()</code>.
*/
public void layout() {
LayoutManager layoutMgr = this.layoutMgr;
if (layoutMgr != null) {
layoutMgr.layoutContainer(this);
}
}
/**
* Invalidates the container. The container and all parents
* above it are marked as needing to be laid out. This method can
* be called often, so it needs to execute quickly.
* @see #validate
* @see #layout
* @see LayoutManager
*/
public void invalidate() {
if (layoutMgr instanceof LayoutManager2) {
LayoutManager2 lm = (LayoutManager2) layoutMgr;
lm.invalidateLayout(this);
}
super.invalidate();
}
/**
* Validates this container and all of its subcomponents.
* <p>
* AWT uses <code>validate</code> to cause a container to lay out
* its subcomponents again after the components it contains
* have been added to or modified.
* @see #validate
* @see Component#invalidate
* @since JDK1.0
*/
public void validate() {
/* Avoid grabbing lock unless really necessary. */
if (!valid) {
synchronized (getTreeLock()) {
if (!valid && peer != null) {
Cursor oldCursor = getCursor();
ContainerPeer p = null;
if (peer instanceof ContainerPeer) {
p = (ContainerPeer) peer;
}
if (p != null) {
p.beginValidate();
}
validateTree();
valid = true;
if (p != null) {
p.endValidate();
}
}
}
}
}
/**
* Recursively descends the container tree and recomputes the
* layout for any subtrees marked as needing it (those marked as
* invalid). Synchronization should be provided by the method
* that calls this one: <code>validate</code>.
*/
protected void validateTree() {
if (!valid) {
doLayout();
Component component[] = this.component;
for (int i = 0; i < ncomponents; ++i) {
Component comp = component[i];
if ((comp instanceof Container)
&& !(comp instanceof Window)
&& !comp.valid) {
((Container) comp).validateTree();
} else {
comp.validate();
}
}
}
valid = true;
}
/**
* Returns the preferred size of this container.
* @return an instance of <code>Dimension</code> that represents
* the preferred size of this container.
* @see java.awt.Container#getMinimumSize
* @see java.awt.Container#getLayout
* @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container)
* @see java.awt.Component#getPreferredSize
* @since JDK1.0
*/
public Dimension getPreferredSize() {
return preferredSize();
}
/**
* @deprecated As of JDK version 1.1,
* replaced by <code>getPreferredSize()</code>.
*/
public Dimension preferredSize() {
/* Avoid grabbing the lock if a reasonable cached size value
* is available.
*/
Dimension dim = prefSize;
if (dim != null && isValid()) {
return dim;
}
synchronized (getTreeLock()) {
prefSize = (layoutMgr != null) ?
layoutMgr.preferredLayoutSize(this) :
super.preferredSize();
return prefSize;
}
}
/**
* Returns the minimum size of this container.
* @return an instance of <code>Dimension</code> that represents
* the minimum size of this container.
* @see java.awt.Container#getPreferredSize
* @see java.awt.Container#getLayout
* @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container)
* @see java.awt.Component#getMinimumSize
* @since JDK1.1
*/
public Dimension getMinimumSize() {
return minimumSize();
}
/**
* @deprecated As of JDK version 1.1,
* replaced by <code>getMinimumSize()</code>.
*/
public Dimension minimumSize() {
/* Avoid grabbing the lock if a reasonable cached size value
* is available.
*/
Dimension dim = minSize;
if (dim != null && isValid()) {
return dim;
}
synchronized (getTreeLock()) {
minSize = (layoutMgr != null) ?
layoutMgr.minimumLayoutSize(this) :
super.minimumSize();
return minSize;
}
}
/**
* Returns the maximum size of this container.
* @see #getPreferredSize
*/
public Dimension getMaximumSize() {
/* Avoid grabbing the lock if a reasonable cached size value
* is available.
*/
Dimension dim = maxSize;
if (dim != null && isValid()) {
return dim;
}
if (layoutMgr instanceof LayoutManager2) {
synchronized (getTreeLock()) {
LayoutManager2 lm = (LayoutManager2) layoutMgr;
maxSize = lm.maximumLayoutSize(this);
}
} else {
maxSize = super.getMaximumSize();
}
return maxSize;
}
/**
* Returns the alignment along the x axis. This specifies how
* the component would like to be aligned relative to other
* components. The value should be a number between 0 and 1
* where 0 represents alignment along the origin, 1 is aligned
* the furthest away from the origin, 0.5 is centered, etc.
*/
public float getAlignmentX() {
float xAlign;
if (layoutMgr instanceof LayoutManager2) {
synchronized (getTreeLock()) {
LayoutManager2 lm = (LayoutManager2) layoutMgr;
xAlign = lm.getLayoutAlignmentX(this);
}
} else {
xAlign = super.getAlignmentX();
}
return xAlign;
}
/**
* Returns the alignment along the y axis. This specifies how
* the component would like to be aligned relative to other
* components. The value should be a number between 0 and 1
* where 0 represents alignment along the origin, 1 is aligned
* the furthest away from the origin, 0.5 is centered, etc.
*/
public float getAlignmentY() {
float yAlign;
if (layoutMgr instanceof LayoutManager2) {
synchronized (getTreeLock()) {
LayoutManager2 lm = (LayoutManager2) layoutMgr;
yAlign = lm.getLayoutAlignmentY(this);
}
} else {
yAlign = super.getAlignmentY();
}
return yAlign;
}
/**
* Paints the container. This forwards the paint to any lightweight components
* that are children of this container. If this method is reimplemented,
* super.paint(g) should be called so that lightweight components are properly
* rendered. If a child component is entirely clipped by the current clipping
* setting in g, paint() will not be forwarded to that child.
*
* @param g the specified Graphics window
* @see java.awt.Component#update(java.awt.Graphics)
*/
public void paint(Graphics g) {
// Fixed 6643917: calling common method for paint and paintComponents
paintContents(g, true);
}
/**
* Updates the container. This forwards the update to any lightweight components
* that are children of this container. If this method is reimplemented,
* super.update(g) should be called so that lightweight components are properly
* rendered. If a child component is entirely clipped by the current clipping
* setting in g, update() will not be forwarded to that child.
*
* @param g the specified Graphics window
* @see java.awt.Component#update(java.awt.Graphics)
*/
public void update(Graphics g) {
if (isShowing()) {
// Fixed 6178052: double clearing of the background program.
//
// The legacy code for Container.update() checked whether the update
// is for a non-lightweight and if so would try to clear the rect
// before calling super.update(). This resulted in a double clear
// because the Component.update() legacy code also cleared the rect
// for instances of Panel, Frame, Dialog, and Window, besides Canvas.
//
// This double clearing is undesirable.
//
// The fix is to call paint(g) instead of super.update(g). See also
// the fix in Component.update().
if (! (peer instanceof sun.awt.peer.LightweightPeer)) {
peer.clearBackground(g);
}
paint(g);
}
}
/**
* Prints the container. This forwards the print to any lightweight components
* that are children of this container. If this method is reimplemented,
* super.print(g) should be called so that lightweight components are properly
* rendered. If a child component is entirely clipped by the current clipping
* setting in g, print() will not be forwarded to that child.
*
* @param g the specified Graphics window
* @see java.awt.Component#update(java.awt.Graphics)
*/
public void print(Graphics g) {
super.print(g); // By default, Component.print() calls paint()
int ncomponents = this.ncomponents;
Component component[] = this.component;
Rectangle clip = g.getClipRect();
for (int i = ncomponents - 1; i >= 0; i--) {
Component comp = component[i];
if (comp != null &&
comp.peer instanceof sun.awt.peer.LightweightPeer) {
Rectangle cr = comp.getBounds();
if ((clip == null) || cr.intersects(clip)) {
Graphics cg = g.create(cr.x, cr.y, cr.width, cr.height);
cg.setFont(comp.getFont());
try {
comp.print(cg);
} finally {
cg.dispose();
}
}
}
}
}
/**
* Paints each of the components in this container.
* @param g the graphics context.
* @see java.awt.Component#paint
* @see java.awt.Component#paintAll
* @since JDK1.0
*/
public void paintComponents(Graphics g) {
// Fixed 6643917: calling common method for paint and paintComponents
paintContents(g, false);
}
/**
* 6643917
* Paints only lightweights or all components, depending on the
* onlyLightweights boolean argument.
*/
private void paintContents(Graphics g, boolean onlyLightweights) {
if (isShowing()) {
int ncomponents = this.ncomponents;
Component component[] = this.component;
Rectangle clip = g.getClipRect();
for (int i = ncomponents - 1; i >= 0; i--) {
Component comp = component[i];
if (comp != null &&
(!onlyLightweights || comp.peer instanceof sun.awt.peer.LightweightPeer) &&
comp.visible == true) {
Rectangle cr = comp.getBounds();
if ((clip == null) || cr.intersects(clip)) {
Graphics cg = g.create();
if (cg instanceof ConstrainableGraphics) {
((ConstrainableGraphics) cg).constrain(cr.x, cr.y, cr.width, cr.height);
} else {
cg.translate(cr.x, cr.y);
// Fixed 6178102.
// Moved the clipRect() call here, same as in Component.getGraphics().
cg.clipRect(0, 0, cr.width, cr.height);
}
// Fixed 6178102.
// This call does not belong here.
//cg.clipRect(0, 0, cr.width, cr.height);
cg.setFont(comp.getFont());
try {
if (onlyLightweights)
comp.paint(cg);
else
comp.paintAll(cg);
} finally {
cg.dispose();
}
}
}
}
}
}
/**
* Prints each of the components in this container.
* @param g the graphics context.
* @see java.awt.Component#print
* @see java.awt.Component#printAll
* @since JDK1.0
*/
public void printComponents(Graphics g) {
int ncomponents = this.ncomponents;
Component component[] = this.component;
// Lightweight components always paint behind peered components,
// even if they are at the top of the Z order. We emulate this
// behavior by making two printing passes: the first for lightweights;
// the second for heavyweights.
for (int i = ncomponents - 1; i >= 0; i--) {
Component comp = component[i];
if (comp != null &&
comp.peer instanceof sun.awt.peer.LightweightPeer) {
printOneComponent(g, comp);
}
}
for (int i = ncomponents - 1; i >= 0; i--) {
Component comp = component[i];
if (comp != null &&
!(comp.peer instanceof sun.awt.peer.LightweightPeer)) {
printOneComponent(g, comp);
}
}
}
private void printOneComponent(Graphics g, Component comp) {
Graphics cg = g.create(comp.x, comp.y, comp.width,
comp.height);
cg.setFont(comp.getFont());
try {
comp.printAll(cg);
} finally {
cg.dispose();
}
}
/**
* Simulates the peer callbacks into java.awt for printing of
* lightweight Containers.
* @param g the graphics context to use for printing.
* @see Component#printAll
* @see #printComponents
*/
void lightweightPrint(Graphics g) {
super.lightweightPrint(g);
printComponents(g);
}
/**
* Adds the specified container listener to receive container events
* from this container.
* @param l the container listener
*/
public synchronized void addContainerListener(ContainerListener l) {
containerListener = AWTEventMulticaster.add(containerListener, l);
newEventsOnly = true;
}
/**
* Removes the specified container listener so it no longer receives
* container events from this container.
* @param l the container listener
*/
public synchronized void removeContainerListener(ContainerListener l) {
containerListener = AWTEventMulticaster.remove(containerListener, l);
}
/**
* Returns an array of all the container listeners
* registered on this container.
*
* @return all of this container's <code>ContainerListener</code>s
* or an empty array if no container
* listeners are currently registered
*
* @see #addContainerListener
* @see #removeContainerListener
* @since 1.4
*/
public synchronized ContainerListener[] getContainerListeners() {
return (ContainerListener[])AWTEventMulticaster.getListeners(
(EventListener)containerListener,
ContainerListener.class);
}
// NOTE: remove when filtering is done at lower level
boolean eventEnabled(AWTEvent e) {
int id = e.getID();
if (id == ContainerEvent.COMPONENT_ADDED ||
id == ContainerEvent.COMPONENT_REMOVED) {
if ((eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0 ||
containerListener != null) {
return true;
}
return false;
}
return super.eventEnabled(e);
}
/**
* Processes events on this container. If the event is a ContainerEvent,
* it invokes the processContainerEvent method, else it invokes its
* superclass's processEvent.
* @param e the event
*/
protected void processEvent(AWTEvent e) {
if (e instanceof ContainerEvent) {
processContainerEvent((ContainerEvent) e);
return;
}
super.processEvent(e);
}
/**
* Processes container events occurring on this container by
* dispatching them to any registered ContainerListener objects.
* NOTE: This method will not be called unless container events
* are enabled for this component; this happens when one of the
* following occurs:
* a) A ContainerListener object is registered via addContainerListener()
* b) Container events are enabled via enableEvents()
* @see Component#enableEvents
* @param e the container event
*/
protected void processContainerEvent(ContainerEvent e) {
if (containerListener != null) {
switch (e.getID()) {
case ContainerEvent.COMPONENT_ADDED:
containerListener.componentAdded(e);
break;
case ContainerEvent.COMPONENT_REMOVED:
containerListener.componentRemoved(e);
break;
}
}
}
/*
* Dispatches an event to this component or one of its sub components.
* @param e the event
*/
void dispatchEventImpl(AWTEvent e) {
if ((dispatcher != null) && dispatcher.dispatchEvent(e)) {
// event was sent to a lightweight component. The
// native-produced event sent to the native container
// must be properly disposed of by the peer, so it
// gets forwarded. If the native host has been removed
// as a result of the sending the lightweight event,
// the peer reference will be null.
e.consume();
if (peer != null) {
peer.handleEvent(e);
}
return;
}
super.dispatchEventImpl(e);
}
/*
* Dispatches an event to this component, without trying to forward
* it to any sub components
* @param e the event
*/
void dispatchEventToSelf(AWTEvent e) {
super.dispatchEventImpl(e);
}
/**
* Fetchs the top-most (deepest) lightweight component that is interested
* in receiving mouse events.
*/
Component getMouseEventTarget(int x, int y, boolean includeSelf) {
int ncomponents = this.ncomponents;
Component component[] = this.component;
for (int i = 0; i < ncomponents; i++) {
Component comp = component[i];
if ((comp != null) && (comp.contains(x - comp.x, y - comp.y)) &&
(comp.peer instanceof sun.awt.peer.LightweightPeer) &&
(comp.visible == true)) {
// found a component that intersects the point, see if there is
// a deeper possibility.
if (comp instanceof Container) {
Container child = (Container) comp;
Component deeper = child.getMouseEventTarget(x - child.x, y - child.y, includeSelf);
if (deeper != null) {
return deeper;
}
} else {
if ((comp.mouseListener != null) ||
((comp.eventMask & AWTEvent.MOUSE_EVENT_MASK) != 0) ||
(comp.mouseMotionListener != null) ||
((comp.eventMask & AWTEvent.MOUSE_MOTION_EVENT_MASK) != 0)) {
// there isn't a deeper target, but this component is a target
return comp;
}
}
}
}
boolean isPeerOK;
boolean isMouseOverMe;
boolean isMouseListener;
boolean isMotionListener;
isPeerOK = (peer instanceof sun.awt.peer.LightweightPeer) || includeSelf;
isMouseOverMe = contains(x, y);
isMouseListener = (mouseListener != null) ||
((eventMask & AWTEvent.MOUSE_EVENT_MASK) != 0);
isMotionListener = (mouseMotionListener != null) ||
((eventMask & AWTEvent.MOUSE_MOTION_EVENT_MASK) != 0);
// didn't find a child target, return this component if it's a possible target
if (isMouseOverMe && isPeerOK && (isMouseListener || isMotionListener)) {
return this;
}
// no possible target
return null;
}
/**
* Locates the visible child component that contains the specified
* point. The top-most child component is returned in the case
* where there is overlap in the components. If the containing child
* component is a Container, this method will continue searching for
* the deepest nested child component. Components which are not
* visible are ignored during the search.<p>
*
* The findComponentAt method is different from getComponentAt in
* that getComponentAt only searches the Container's immediate
* children; if the containing component is a Container,
* findComponentAt will search that child to find a nested component.
*
* @param p the point.
* @return null if the component does not contain the position.
* If there is no child component at the requested point and the
* point is within the bounds of the container the container itself
* is returned.
* @see Component#contains
* @see getComponentAt
* @since 1.2
*/
public Component findComponentAt(Point p) {
return findComponentAt(p.x, p.y);
}
/**
* Locates the visible child component that contains the specified
* position. The top-most child component is returned in the case
* where there is overlap in the components. If the containing child
* component is a Container, this method will continue searching for
* the deepest nested child component. Components which are not
* visible are ignored during the search.<p>
*
* The findComponentAt method is different from getComponentAt in
* that getComponentAt only searches the Container's immediate
* children; if the containing component is a Container,
* findComponentAt will search that child to find a nested component.
*
* @param x the <i>x</i> coordinate
* @param y the <i>y</i> coordinate
* @return null if the component does not contain the position.
* If there is no child component at the requested point and the
* point is within the bounds of the container the container itself
* is returned.
* @see Component#contains
* @see getComponentAt
* @since 1.2
*/
public Component findComponentAt(int x, int y) {
synchronized (getTreeLock()) {
return findComponentAt(x, y, true);
}
}
/**
* Private version of findComponentAt which has two controllable
* behaviors. Setting 'ignoreEnabled' to 'false' bypasses disabled
* Components during the serach. This behavior is used by the
* lightweight cursor support in sun.awt.GlobalCursorManager.
* Setting 'ignoreGlassPane' to 'true' bypasses the glass panes owned
* by any JRootPanes found during the search. This behavior is
* used by the DnD event targeting code. The cursor and DnD code
* both call this function directly via native code.
*
* The addition of both of these features is temporary, pending the
* adoption of new, public APIs which export these features (probably
* in merlin).
*/
final Component findComponentAt(int x, int y, boolean ignoreEnabled) {
if (!(contains(x, y) && visible && (ignoreEnabled || enabled))) {
return null;
}
int ncomponents = this.ncomponents;
Component component[] = this.component;
// Two passes: see comment in sun.awt.SunGraphicsCallback
for (int i = 0; i < ncomponents; i++) {
Component comp = component[i];
if (comp != null &&
!(comp.peer instanceof sun.awt.peer.LightweightPeer)) {
if (comp instanceof Container) {
comp = ((Container) comp).findComponentAt(x - comp.x,
y - comp.y,
ignoreEnabled);
} else {
comp = comp.getComponentAt(x - comp.x, y - comp.y);
}
if (comp != null && comp.visible &&
(ignoreEnabled || comp.enabled)) {
return comp;
}
}
}
for (int i = 0; i < ncomponents; i++) {
Component comp = component[i];
if (comp != null &&
comp.peer instanceof sun.awt.peer.LightweightPeer) {
if (comp instanceof Container) {
comp = ((Container) comp).findComponentAt(x - comp.x,
y - comp.y,
ignoreEnabled);
} else {
comp = comp.getComponentAt(x - comp.x, y - comp.y);
}
if (comp != null && comp.visible &&
(ignoreEnabled || comp.enabled)) {
return comp;
}
}
}
return this;
}
/**
* Set the cursor image to a predefined cursor.
* @param <code>cursor</code> One of the constants defined
* by the <code>Cursor</code> class.
* @see java.awt.Component#getCursor
* @see java.awt.Cursor
* @since JDK1.1
*/
public synchronized void setCursor(Cursor cursor) {
if (dispatcher != null) {
Component cursorOn = dispatcher.getCursorOn();
// if mouse is on a lightweight component, we should not
// call setCursor on the nativeContainer
if (cursorOn != null) {
this.cursor = cursor;
// it could be that cursorOn will inherit the new cursor
dispatcher.updateCursor(cursorOn);
return;
}
}
super.setCursor(cursor);
}
/**
* Update the cursor is decided by the dispatcher
* This method is used when setCursor on a lightweight
*/
void updateCursor(Component comp) {
if (dispatcher != null) {
dispatcher.updateCursor(comp);
}
}
/**
* Fetchs the top-most (deepest) lightweight component whose cursor
* should be displayed.
*/
Component getCursorTarget(int x, int y) {
int ncomponents = this.ncomponents;
Component component[] = this.component;
for (int i = 0; i < ncomponents; i++) {
Component comp = component[i];
if ((comp != null) &&
// 6201639
// Only examine lightweight components
// (See related fix in LightweightDispatcher.dispatchEvent())
(comp.peer instanceof sun.awt.peer.LightweightPeer) &&
// 6201639
(comp.contains(x - comp.x, y - comp.y)) &&
(comp.visible == true)) {
// found a component that intersects the point, see if there is
// a deeper possibility.
if (comp instanceof Container) {
Container child = (Container) comp;
Component deeper = child.getCursorTarget(x - child.x, y - child.y);
if (deeper != null) {
return deeper;
}
} else {
return comp;
}
}
}
if (contains(x, y) && (peer instanceof sun.awt.peer.LightweightPeer)) {
return this;
}
// no possible target
return null;
}
/**
* This is called by lightweight components that want the containing
* windowed parent to enable some kind of events on their behalf.
* This is needed for events that are normally only dispatched to
* windows to be accepted so that they can be forwarded downward to
* the lightweight component that has enabled them.
*/
void proxyEnableEvents(long events) {
if (peer instanceof sun.awt.peer.LightweightPeer) {
// this container is lightweight.... continue sending it
// upward.
parent.proxyEnableEvents(events);
} else {
// This is a native container, so it needs to host
// one of it's children. If this function is called before
// a peer has been created we don't yet have a dispatcher
// because it has not yet been determined if this instance
// is lightweight.
if (dispatcher != null) {
dispatcher.enableEvents(events);
}
}
}
Window getWindow() {
Container w = this;
while (!(w instanceof Window)) {
w = w.getParent();
}
return (Window) w;
}
/*
* Determines if the top-level window containing
* this component is activated
*/
private boolean isContainingWindowActivated() {
Component comp = this;
while (!(comp instanceof Window)) {
comp = comp.getParent();
}
return ((Window) comp).isActive();
}
/**
* @deprecated As of JDK version 1.1,
* replaced by <code>dispatchEvent(AWTEvent e)</code>
*/
public void deliverEvent(Event e) {
Component comp = getComponentAt(e.x, e.y);
if ((comp != null) && (comp != this)) {
e.translate(-comp.x, -comp.y);
comp.deliverEvent(e);
} else {
postEvent(e);
}
}
/**
* Locates the component that contains the x,y position. The
* top-most child component is returned in the case where there
* is overlap in the components. This is determined by finding
* the component closest to the index 0 that claims to contain
* the given point via Component.contains().
* @param x the <i>x</i> coordinate
* @param y the <i>y</i> coordinate
* @return null if the component does not contain the position.
* If there is no child component at the requested point and the
* point is within the bounds of the container the container itself
* is returned; otherwise the top-most child is returned.
* @see Component#contains
* @since JDK1.1
*/
public Component getComponentAt(int x, int y) {
return locate(x, y);
}
/**
* @deprecated As of JDK version 1.1,
* replaced by <code>getComponentAt(int, int)</code>.
*/
public Component locate(int x, int y) {
if (!contains(x, y)) {
return null;
}
int ncomponents = this.ncomponents;
Component component[] = this.component;
for (int i = 0; i < ncomponents; i++) {
Component comp = component[i];
if (comp != null) {
if (comp.contains(x - comp.x, y - comp.y)) {
return comp;
}
}
}
return this;
}
/**
* Gets the component that contains the specified point.
* @param p the point.
* @return returns the component that contains the point,
* or <code>null</code> if the component does
* not contain the point.
* @see java.awt.Component#contains
* @since JDK1.1
*/
public Component getComponentAt(Point p) {
return getComponentAt(p.x, p.y);
}
/**
* Notifies the container to create a peer. It will also
* notify the components contained in this container.
* This method should be called by <code>Container.add</code>,
* and not by user code directly.
* @see #removeNotify
* @since JDK1.0
*/
public void addNotify() {
synchronized (getTreeLock()) {
// addNotify() on the children may cause proxy event enabling
// on this instance, so we first call super.addNotify() and
// possibly create an lightweight event dispatcher before calling
// addNotify() on the children which may be lightweight.
super.addNotify();
if (!(peer instanceof sun.awt.peer.LightweightPeer)) {
dispatcher = new LightweightDispatcher(this);
}
int ncomponents = this.ncomponents;
Component component[] = this.component;
for (int i = 0; i < ncomponents; i++) {
component[i].addNotify();
}
}
}
/**
* Notifies this container and all of its subcomponents to remove
* their peers.
* This method should be invoked by the container's
* <code>remove</code> method, and not directly by user code.
* @see java.awt.Container#remove(int)
* @see java.awt.Container#remove(java.awt.Component)
* @since JDK1.0
*/
public void removeNotify() {
synchronized (getTreeLock()) {
int ncomponents = this.ncomponents;
Component component[] = this.component;
for (int i = 0; i < ncomponents; i++) {
component[i].removeNotify();
}
if (dispatcher != null) {
dispatcher.dispose();
}
super.removeNotify();
}
}
/**
* Checks if the component is contained in the component hierarchy of
* this container.
* @param c the component
* @return <code>true</code> if it is an ancestor;
* <code>true</code> otherwise.
* @since JDK1.1
*/
public boolean isAncestorOf(Component c) {
Container p;
if (c == null || ((p = c.getParent()) == null)) {
return false;
}
while (p != null) {
if (p == this) {
return true;
}
p = p.getParent();
}
return false;
}
/**
* Returns the parameter string representing the state of this
* container. This string is useful for debugging.
* @return the parameter string of this container.
* @since JDK1.0
*/
protected String paramString() {
String str = super.paramString();
LayoutManager layoutMgr = this.layoutMgr;
if (layoutMgr != null) {
str += ",layout=" + layoutMgr.getClass().getName();
}
return str;
}
public void show() {
boolean wasShowing = isShowing();
super.show();
if (!wasShowing && peer != null) {
peer.clearBackground(getGraphics());
}
}
/**
* Prints a listing of this container to the specified output
* stream. The listing starts at the specified indentation.
* @param out a print stream.
* @param indent the number of spaces to indent.
* @see java.awt.Component#list(java.io.PrintStream, int)
* @since JDK
*/
public void list(PrintStream out, int indent) {
super.list(out, indent);
int ncomponents = this.ncomponents;
Component component[] = this.component;
for (int i = 0; i < ncomponents; i++) {
Component comp = component[i];
if (comp != null) {
comp.list(out, indent + 1);
}
}
}
/**
* Prints out a list, starting at the specified indention, to the specified
* print writer.
*/
public void list(PrintWriter out, int indent) {
super.list(out, indent);
int ncomponents = this.ncomponents;
Component component[] = this.component;
for (int i = 0; i < ncomponents; i++) {
Component comp = component[i];
if (comp != null) {
comp.list(out, indent + 1);
}
}
}
void setFocusOwner(Component c) {
Container parent = this.parent;
if (parent != null) {
parent.setFocusOwner(c);
}
}
void preProcessKeyEvent(KeyEvent e) {
Container parent = this.parent;
if (parent != null) {
parent.preProcessKeyEvent(e);
}
}
void postProcessKeyEvent(KeyEvent e) {
Container parent = this.parent;
if (parent != null) {
parent.postProcessKeyEvent(e);
}
}
void transferFocus(Component base) {
nextFocus(base);
}
boolean postsOldMouseEvents() {
return true;
}
/**
* @deprecated As of JDK version 1.1,
* replaced by transferFocus(Component).
*/
void nextFocus(Component base) {
Container parent = this.parent;
if (parent != null) {
parent.transferFocus(base);
}
}
/* Serialization support. A Container is responsible for
* restoring the parent fields of its component children.
*/
private int containerSerializedDataVersion = 1;
private void writeObject(ObjectOutputStream s)
throws IOException {
s.defaultWriteObject();
AWTEventMulticaster.save(s, containerListenerK, containerListener);
s.writeObject(null);
}
private void readObject(ObjectInputStream s)
throws ClassNotFoundException, IOException {
s.defaultReadObject();
Component component[] = this.component;
for (int i = 0; i < ncomponents; i++)
component[i].parent = this;
Object keyOrNull;
while (null != (keyOrNull = s.readObject())) {
String key = ((String) keyOrNull).intern();
if (containerListenerK == key)
addContainerListener((ContainerListener) (s.readObject()));
else // skip value for unrecognized key
s.readObject();
}
}
// Focus-related functionality added
//-------------------------------------------------------------------------
private transient FocusTraversalPolicy focusTraversalPolicy;
private boolean focusCycleRoot = false;
void initializeFocusTraversalKeys() {
focusTraversalKeys = new Set[4];
}
public void setFocusTraversalKeys(int id, Set keystrokes) {
if (id < 0 || id >= KeyboardFocusManager.TRAVERSAL_KEY_LENGTH) {
throw new IllegalArgumentException("invalid focus traversal key identifier");
}
// Don't call super.setFocusTraversalKey. The Component parameter check
// does not allow DOWN_CYCLE_TRAVERSAL_KEYS, but we do.
setFocusTraversalKeys_NoIDCheck(id, keystrokes);
}
public Set getFocusTraversalKeys(int id) {
if (id < 0 || id >= KeyboardFocusManager.TRAVERSAL_KEY_LENGTH) {
throw new IllegalArgumentException("invalid focus traversal key identifier");
}
// Don't call super.getFocusTraversalKey. The Component parameter check
// does not allow DOWN_CYCLE_TRAVERSAL_KEY, but we do.
return getFocusTraversalKeys_NoIDCheck(id);
}
public boolean areFocusTraversalKeysSet(int id) {
if (id < 0 || id >= KeyboardFocusManager.TRAVERSAL_KEY_LENGTH) {
throw new IllegalArgumentException("invalid focus traversal key identifier");
}
return (focusTraversalKeys != null && focusTraversalKeys[id] != null);
}
public boolean isFocusCycleRoot(Container container) {
if (isFocusCycleRoot() && container == this) {
return true;
} else {
return super.isFocusCycleRoot(container);
}
}
private Container findTraversalRoot() {
// I potentially have two roots, myself and my root parent
// If I am the current root, then use me
// If none of my parents are roots, then use me
// If my root parent is the current root, then use my root parent
// If neither I nor my root parent is the current root, then
// use my root parent (a guess)
Container currentFocusCycleRoot = KeyboardFocusManager.
getCurrentKeyboardFocusManager().getCurrentFocusCycleRoot();
Container root;
if (currentFocusCycleRoot == this) {
root = this;
} else {
root = getFocusCycleRootAncestor();
if (root == null) {
root = this;
}
}
if (root != currentFocusCycleRoot) {
KeyboardFocusManager.getCurrentKeyboardFocusManager().
setGlobalCurrentFocusCycleRoot(root);
}
return root;
}
final boolean containsFocus() {
synchronized (getTreeLock()) {
Component comp = KeyboardFocusManager.
getCurrentKeyboardFocusManager().getFocusOwner();
while (comp != null && !(comp instanceof Window) && comp != this) {
comp = (Component) comp.getParent();
}
return (comp == this);
}
}
/**
* Check if this component is the child of this container or its children.
* Note: this function acquires treeLock
* Note: this function traverses children tree only in one Window.
* @param comp a component in test, must not be null
*/
boolean isParentOf(Component comp) {
synchronized(getTreeLock()) {
while (comp != null && comp != this && !(comp instanceof Window)) {
comp = comp.getParent();
}
return (comp == this);
}
}
void clearMostRecentFocusOwnerOnHide() {
Component comp = null;
Container window = this;
synchronized (getTreeLock()) {
while (window != null && !(window instanceof Window)) {
window = window.getParent();
}
if (window != null) {
comp = KeyboardFocusManager.
getMostRecentFocusOwner((Window)window);
while ((comp != null) && (comp != this) && !(comp instanceof Window)) {
comp = comp.getParent();
}
}
}
if (comp == this) {
KeyboardFocusManager.setMostRecentFocusOwner((Window)window, null);
}
if (window != null) {
Window myWindow = (Window)window;
synchronized(getTreeLock()) {
// This synchronized should always be the second in a pair (tree lock, KeyboardFocusManager.class)
synchronized(KeyboardFocusManager.class) {
Component storedComp = myWindow.getTemporaryLostComponent();
if (isParentOf(storedComp) || storedComp == this) {
myWindow.setTemporaryLostComponent(null);
}
}
}
}
}
void clearCurrentFocusCycleRootOnHide() {
KeyboardFocusManager kfm =
KeyboardFocusManager.getCurrentKeyboardFocusManager();
Container cont = kfm.getCurrentFocusCycleRoot();
synchronized (getTreeLock()) {
while (this != cont && !(cont instanceof Window) && (cont != null)) {
cont = cont.getParent();
}
}
if (cont == this) {
kfm.setGlobalCurrentFocusCycleRoot(null);
}
}
boolean nextFocusHelper() {
if (isFocusCycleRoot()) {
Container root = findTraversalRoot();
Component comp = this;
Container anc;
while (root != null &&
(anc = root.getFocusCycleRootAncestor()) != null &&
!(root.isShowing() &&
root.isFocusable() &&
root.isEnabled())) {
comp = root;
root = anc;
}
if (root != null) {
FocusTraversalPolicy policy = root.getFocusTraversalPolicy();
Component toFocus = policy.getComponentAfter(root, comp);
if (toFocus == null) {
toFocus = policy.getDefaultComponent(root);
}
if (toFocus != null) {
return toFocus.requestFocus(false);
}
}
return false;
} else {
// I only have one root, so the general case will suffice
return super.nextFocusHelper();
}
}
public void transferFocusBackward() {
if (isFocusCycleRoot()) {
Container root = findTraversalRoot();
Component comp = this;
while (root != null &&
!(root.isShowing() &&
root.isFocusable() &&
root.isEnabled())) {
comp = root;
root = comp.getFocusCycleRootAncestor();
}
if (root != null) {
FocusTraversalPolicy policy = root.getFocusTraversalPolicy();
Component toFocus = policy.getComponentBefore(root, comp);
if (toFocus == null) {
toFocus = policy.getDefaultComponent(root);
}
if (toFocus != null) {
toFocus.requestFocus();
}
}
} else {
// I only have one root, so the general case will suffice
super.transferFocusBackward();
}
}
public void setFocusTraversalPolicy(FocusTraversalPolicy policy) {
FocusTraversalPolicy oldPolicy;
synchronized (this) {
oldPolicy = this.focusTraversalPolicy;
this.focusTraversalPolicy = policy;
}
firePropertyChange("focusTraversalPolicy", oldPolicy,
policy);
}
public FocusTraversalPolicy getFocusTraversalPolicy() {
if (!isFocusCycleRoot()) {
return null;
}
FocusTraversalPolicy policy = this.focusTraversalPolicy;
if (policy != null) {
return policy;
}
Container rootAncestor = getFocusCycleRootAncestor();
if (rootAncestor != null) {
return rootAncestor.getFocusTraversalPolicy();
} else {
return KeyboardFocusManager.getCurrentKeyboardFocusManager().
getDefaultFocusTraversalPolicy();
}
}
public boolean isFocusTraversalPolicySet() {
return (focusTraversalPolicy != null);
}
public void setFocusCycleRoot(boolean focusCycleRoot) {
boolean oldFocusCycleRoot;
synchronized (this) {
oldFocusCycleRoot = this.focusCycleRoot;
this.focusCycleRoot = focusCycleRoot;
}
firePropertyChange("focusCycleRoot", new Boolean(oldFocusCycleRoot),
new Boolean(focusCycleRoot));
}
public boolean isFocusCycleRoot() {
return focusCycleRoot;
}
public void transferFocusDownCycle() {
if (isFocusCycleRoot()) {
KeyboardFocusManager.getCurrentKeyboardFocusManager().
setGlobalCurrentFocusCycleRoot(this);
Component toFocus = getFocusTraversalPolicy().
getDefaultComponent(this);
if (toFocus != null) {
toFocus.requestFocus();
}
}
}
}