blob: fa83502946d315ab7b8a244132b564c10daf366e [file] [log] [blame]
/* JComponent.java -- Every component in swing inherits from this class.
Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 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 javax.swing;
import java.applet.Applet;
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FocusTraversalPolicy;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.dnd.DropTarget;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.peer.LightweightPeer;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;
import java.io.Serializable;
import java.util.EventListener;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Set;
import javax.accessibility.Accessible;
import javax.accessibility.AccessibleContext;
import javax.accessibility.AccessibleExtendedComponent;
import javax.accessibility.AccessibleKeyBinding;
import javax.accessibility.AccessibleRole;
import javax.accessibility.AccessibleState;
import javax.accessibility.AccessibleStateSet;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.event.EventListenerList;
import javax.swing.plaf.ComponentUI;
/**
* The base class of all Swing components.
* It contains generic methods to manage events, properties and sizes. Actual
* drawing of the component is channeled to a look-and-feel class that is
* implemented elsewhere.
*
* @author Ronald Veldema (rveldema&064;cs.vu.nl)
* @author Graydon Hoare (graydon&064;redhat.com)
*/
public abstract class JComponent extends Container implements Serializable
{
private static final long serialVersionUID = -7908749299918704233L;
/**
* The accessible context of this <code>JComponent</code>.
*/
protected AccessibleContext accessibleContext;
/**
* Basic accessibility support for <code>JComponent</code> derived
* widgets.
*/
public abstract class AccessibleJComponent
extends AccessibleAWTContainer
implements AccessibleExtendedComponent
{
/**
* Receives notification if the focus on the JComponent changes and
* fires appropriate PropertyChangeEvents to listeners registered with
* the AccessibleJComponent.
*/
protected class AccessibleFocusHandler
implements FocusListener
{
/**
* Creates a new AccessibleFocusHandler.
*/
protected AccessibleFocusHandler()
{
// Nothing to do here.
}
/**
* Receives notification when the JComponent gained focus and fires
* a PropertyChangeEvent to listeners registered on the
* AccessibleJComponent with a property name of
* {@link AccessibleContext#ACCESSIBLE_STATE_PROPERTY} and a new value
* of {@link AccessibleState#FOCUSED}.
*/
public void focusGained(FocusEvent event)
{
AccessibleJComponent.this.firePropertyChange
(AccessibleContext.ACCESSIBLE_STATE_PROPERTY, null,
AccessibleState.FOCUSED);
}
/**
* Receives notification when the JComponent lost focus and fires
* a PropertyChangeEvent to listeners registered on the
* AccessibleJComponent with a property name of
* {@link AccessibleContext#ACCESSIBLE_STATE_PROPERTY} and an old value
* of {@link AccessibleState#FOCUSED}.
*/
public void focusLost(FocusEvent valevent)
{
AccessibleJComponent.this.firePropertyChange
(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
AccessibleState.FOCUSED, null);
}
}
/**
* Receives notification if there are child components are added or removed
* from the JComponent and fires appropriate PropertyChangeEvents to
* interested listeners on the AccessibleJComponent.
*/
protected class AccessibleContainerHandler
implements ContainerListener
{
/**
* Creates a new AccessibleContainerHandler.
*/
protected AccessibleContainerHandler()
{
// Nothing to do here.
}
/**
* Receives notification when a child component is added to the
* JComponent and fires a PropertyChangeEvent on listeners registered
* with the AccessibleJComponent with a property name of
* {@link AccessibleContext#ACCESSIBLE_CHILD_PROPERTY}.
*
* @param event the container event
*/
public void componentAdded(ContainerEvent event)
{
Component c = event.getChild();
if (c != null && c instanceof Accessible)
{
AccessibleContext childCtx = c.getAccessibleContext();
AccessibleJComponent.this.firePropertyChange
(AccessibleContext.ACCESSIBLE_CHILD_PROPERTY, null, childCtx);
}
}
/**
* Receives notification when a child component is removed from the
* JComponent and fires a PropertyChangeEvent on listeners registered
* with the AccessibleJComponent with a property name of
* {@link AccessibleContext#ACCESSIBLE_CHILD_PROPERTY}.
*
* @param event the container event
*/
public void componentRemoved(ContainerEvent event)
{
Component c = event.getChild();
if (c != null && c instanceof Accessible)
{
AccessibleContext childCtx = c.getAccessibleContext();
AccessibleJComponent.this.firePropertyChange
(AccessibleContext.ACCESSIBLE_CHILD_PROPERTY, childCtx, null);
}
}
}
private static final long serialVersionUID = -7047089700479897799L;
/**
* Receives notification when a child component is added to the
* JComponent and fires a PropertyChangeEvent on listeners registered
* with the AccessibleJComponent.
*
* @specnote AccessibleAWTContainer has a protected field with the same
* name. Looks like a bug or nasty misdesign to me.
*/
protected ContainerListener accessibleContainerHandler;
/**
* Receives notification if the focus on the JComponent changes and
* fires appropriate PropertyChangeEvents to listeners registered with
* the AccessibleJComponent.
*
* @specnote AccessibleAWTComponent has a protected field
* accessibleAWTFocusHandler. Looks like a bug or nasty misdesign
* to me.
*/
protected FocusListener accessibleFocusHandler;
/**
* Creates a new AccessibleJComponent.
*/
protected AccessibleJComponent()
{
// Nothing to do here.
}
/**
* Adds a property change listener to the list of registered listeners.
*
* This sets up the {@link #accessibleContainerHandler} and
* {@link #accessibleFocusHandler} fields and calls
* <code>super.addPropertyChangeListener(listener)</code>.
*
* @param listener the listener to add
*/
public void addPropertyChangeListener(PropertyChangeListener listener)
{
// Tests seem to indicate that this method also sets up the other two
// handlers.
if (accessibleContainerHandler == null)
{
accessibleContainerHandler = new AccessibleContainerHandler();
addContainerListener(accessibleContainerHandler);
}
if (accessibleFocusHandler == null)
{
accessibleFocusHandler = new AccessibleFocusHandler();
addFocusListener(accessibleFocusHandler);
}
super.addPropertyChangeListener(listener);
}
/**
* Removes a property change listener from the list of registered listeners.
*
* This uninstalls the {@link #accessibleContainerHandler} and
* {@link #accessibleFocusHandler} fields and calls
* <code>super.removePropertyChangeListener(listener)</code>.
*
* @param listener the listener to remove
*/
public void removePropertyChangeListener(PropertyChangeListener listener)
{
// Tests seem to indicate that this method also resets the other two
// handlers.
if (accessibleContainerHandler != null)
{
removeContainerListener(accessibleContainerHandler);
accessibleContainerHandler = null;
}
if (accessibleFocusHandler != null)
{
removeFocusListener(accessibleFocusHandler);
accessibleFocusHandler = null;
}
super.removePropertyChangeListener(listener);
}
/**
* Returns the number of accessible children of this object.
*
* @return the number of accessible children of this object
*/
public int getAccessibleChildrenCount()
{
// TODO: The functionality should be performed in the superclass.
// Find out why this is overridden. However, it is very well possible
// that this is left over from times when there was no such superclass
// method.
return super.getAccessibleChildrenCount();
}
/**
* Returns the accessible child component at index <code>i</code>.
*
* @param i the index of the accessible child to return
*
* @return the accessible child component at index <code>i</code>
*/
public Accessible getAccessibleChild(int i)
{
// TODO: The functionality should be performed in the superclass.
// Find out why this is overridden. However, it is very well possible
// that this is left over from times when there was no such superclass
// method.
return super.getAccessibleChild(i);
}
/**
* Returns the accessible state set of this component.
*
* @return the accessible state set of this component
*/
public AccessibleStateSet getAccessibleStateSet()
{
// Note: While the java.awt.Component has an 'opaque' property, it
// seems that it is not added to the accessible state set there, even
// if this property is true. However, it is handled for JComponent, so
// we add it here.
AccessibleStateSet state = super.getAccessibleStateSet();
if (isOpaque())
state.add(AccessibleState.OPAQUE);
return state;
}
/**
* Returns the localized name for this object. Generally this should
* almost never return {@link Component#getName()} since that is not
* a localized name. If the object is some kind of text component (like
* a menu item), then the value of the object may be returned. Also, if
* the object has a tooltip, the value of the tooltip may also be
* appropriate.
*
* @return the localized name for this object or <code>null</code> if this
* object has no name
*/
public String getAccessibleName()
{
String name = super.getAccessibleName();
// There are two fallbacks provided by the JComponent in the case the
// superclass returns null:
// - If the component is inside a titled border, then it inherits the
// name from the border title.
// - If the component is not inside a titled border but has a label
// (via JLabel.setLabelFor()), then it gets the name from the label's
// accessible context.
if (name == null)
{
name = getTitledBorderText();
}
if (name == null)
{
Object l = getClientProperty(JLabel.LABEL_PROPERTY);
if (l instanceof Accessible)
{
AccessibleContext labelCtx =
((Accessible) l).getAccessibleContext();
name = labelCtx.getAccessibleName();
}
}
return name;
}
/**
* Returns the localized description of this object.
*
* @return the localized description of this object or <code>null</code>
* if this object has no description
*/
public String getAccessibleDescription()
{
// There are two fallbacks provided by the JComponent in the case the
// superclass returns null:
// - If the component has a tooltip, then inherit the description from
// the tooltip.
// - If the component is not inside a titled border but has a label
// (via JLabel.setLabelFor()), then it gets the name from the label's
// accessible context.
String descr = super.getAccessibleDescription();
if (descr == null)
{
descr = getToolTipText();
}
if (descr == null)
{
Object l = getClientProperty(JLabel.LABEL_PROPERTY);
if (l instanceof Accessible)
{
AccessibleContext labelCtx =
((Accessible) l).getAccessibleContext();
descr = labelCtx.getAccessibleName();
}
}
return descr;
}
/**
* Returns the accessible role of this component.
*
* @return the accessible role of this component
*
* @see AccessibleRole
*/
public AccessibleRole getAccessibleRole()
{
return AccessibleRole.SWING_COMPONENT;
}
/**
* Recursivly searches a border hierarchy (starting at <code>border) for
* a titled border and returns the title if one is found, <code>null</code>
* otherwise.
*
* @param border the border to start search from
*
* @return the border title of a possibly found titled border
*/
protected String getBorderTitle(Border border)
{
String title = null;
if (border instanceof CompoundBorder)
{
CompoundBorder compound = (CompoundBorder) border;
Border inner = compound.getInsideBorder();
title = getBorderTitle(inner);
if (title == null)
{
Border outer = compound.getOutsideBorder();
title = getBorderTitle(outer);
}
}
else if (border instanceof TitledBorder)
{
TitledBorder titled = (TitledBorder) border;
title = titled.getTitle();
}
return title;
}
/**
* Returns the tooltip text for this accessible component.
*
* @return the tooltip text for this accessible component
*/
public String getToolTipText()
{
return JComponent.this.getToolTipText();
}
/**
* Returns the title of the border of this accessible component if
* this component has a titled border, otherwise returns <code>null</code>.
*
* @return the title of the border of this accessible component if
* this component has a titled border, otherwise returns
* <code>null</code>
*/
public String getTitledBorderText()
{
return getBorderTitle(getBorder());
}
/**
* Returns the keybindings associated with this accessible component or
* <code>null</code> if the component does not support key bindings.
*
* @return the keybindings associated with this accessible component
*/
public AccessibleKeyBinding getAccessibleKeyBinding()
{
// The reference implementation seems to always return null here,
// independent of the key bindings of the JComponent. So do we.
return null;
}
}
/**
* An explicit value for the component's preferred size; if not set by a
* user, this is calculated on the fly by delegating to the {@link
* ComponentUI#getPreferredSize} method on the {@link #ui} property.
*/
Dimension preferredSize;
/**
* An explicit value for the component's minimum size; if not set by a
* user, this is calculated on the fly by delegating to the {@link
* ComponentUI#getMinimumSize} method on the {@link #ui} property.
*/
Dimension minimumSize;
/**
* An explicit value for the component's maximum size; if not set by a
* user, this is calculated on the fly by delegating to the {@link
* ComponentUI#getMaximumSize} method on the {@link #ui} property.
*/
Dimension maximumSize;
/**
* A value between 0.0 and 1.0 indicating the preferred horizontal
* alignment of the component, relative to its siblings. The values
* {@link #LEFT_ALIGNMENT}, {@link #CENTER_ALIGNMENT}, and {@link
* #RIGHT_ALIGNMENT} can also be used, as synonyms for <code>0.0</code>,
* <code>0.5</code>, and <code>1.0</code>, respectively. Not all layout
* managers use this property.
*
* @see #getAlignmentX
* @see #setAlignmentX
* @see javax.swing.OverlayLayout
* @see javax.swing.BoxLayout
*/
float alignmentX = -1.0F;
/**
* A value between 0.0 and 1.0 indicating the preferred vertical
* alignment of the component, relative to its siblings. The values
* {@link #TOP_ALIGNMENT}, {@link #CENTER_ALIGNMENT}, and {@link
* #BOTTOM_ALIGNMENT} can also be used, as synonyms for <code>0.0</code>,
* <code>0.5</code>, and <code>1.0</code>, respectively. Not all layout
* managers use this property.
*
* @see #getAlignmentY
* @see #setAlignmentY
* @see javax.swing.OverlayLayout
* @see javax.swing.BoxLayout
*/
float alignmentY = -1.0F;
/**
* The border painted around this component.
*
* @see #paintBorder
*/
Border border;
/**
* The text to show in the tooltip associated with this component.
*
* @see #setToolTipText
* @see #getToolTipText()
*/
String toolTipText;
/**
* The popup menu for the component.
*
* @see #getComponentPopupMenu()
* @see #setComponentPopupMenu(JPopupMenu)
*/
JPopupMenu componentPopupMenu;
/**
* A flag that controls whether the {@link #getComponentPopupMenu()} method
* looks to the component's parent when the <code>componentPopupMenu</code>
* field is <code>null</code>.
*/
boolean inheritsPopupMenu;
/**
* <p>Whether to double buffer this component when painting. This flag
* should generally be <code>true</code>, to ensure good painting
* performance.</p>
*
* <p>All children of a double buffered component are painted into the
* double buffer automatically, so only the top widget in a window needs
* to be double buffered.</p>
*
* @see #setDoubleBuffered
* @see #isDoubleBuffered
* @see #paint
*/
boolean doubleBuffered = true;
/**
* A set of flags indicating which debugging graphics facilities should
* be enabled on this component. The values should be a combination of
* {@link DebugGraphics#NONE_OPTION}, {@link DebugGraphics#LOG_OPTION},
* {@link DebugGraphics#FLASH_OPTION}, or {@link
* DebugGraphics#BUFFERED_OPTION}.
*
* @see #setDebugGraphicsOptions
* @see #getDebugGraphicsOptions
* @see DebugGraphics
* @see #getComponentGraphics
*/
int debugGraphicsOptions;
/**
* <p>This property controls two independent behaviors simultaneously.</p>
*
* <p>First, it controls whether to fill the background of this widget
* when painting its body. This affects calls to {@link
* JComponent#paintComponent}, which in turn calls {@link
* ComponentUI#update} on the component's {@link #ui} property. If the
* component is opaque during this call, the background will be filled
* before calling {@link ComponentUI#paint}. This happens merely as a
* convenience; you may fill the component's background yourself too,
* but there is no need to do so if you will be filling with the same
* color.</p>
*
* <p>Second, it the opaque property informs swing's repaint system
* whether it will be necessary to paint the components "underneath" this
* component, in Z-order. If the component is opaque, it is considered to
* completely occlude components "underneath" it, so they will not be
* repainted along with the opaque component.</p>
*
* <p>The default value for this property is <code>false</code>, but most
* components will want to set it to <code>true</code> when installing UI
* defaults in {@link ComponentUI#installUI}.</p>
*
* @see #setOpaque
* @see #isOpaque
* @see #paintComponent
*/
boolean opaque = false;
/**
* The user interface delegate for this component. Event delivery and
* repainting of the component are usually delegated to this object.
*
* @see #setUI
* @see #getUIClassID
* @see #updateUI
*/
protected ComponentUI ui;
/**
* A hint to the focus system that this component should or should not
* get focus. If this is <code>false</code>, swing will not try to
* request focus on this component; if <code>true</code>, swing might
* try to request focus, but the request might fail. Thus it is only
* a hint guiding swing's behavior.
*
* @see #requestFocus()
* @see #isRequestFocusEnabled
* @see #setRequestFocusEnabled
*/
boolean requestFocusEnabled;
/**
* Flag indicating behavior of this component when the mouse is dragged
* outside the component and the mouse <em>stops moving</em>. If
* <code>true</code>, synthetic mouse events will be delivered on regular
* timed intervals, continuing off in the direction the mouse exited the
* component, until the mouse is released or re-enters the component.
*
* @see #setAutoscrolls
* @see #getAutoscrolls
*/
boolean autoscrolls = false;
/**
* Indicates whether the current paint call is already double buffered or
* not.
*/
static boolean paintingDoubleBuffered = false;
/**
* Indicates whether we are calling paintDoubleBuffered() from
* paintImmadiately (RepaintManager) or from paint() (AWT refresh).
*/
static private boolean isRepainting = false;
/**
* Listeners for events other than {@link PropertyChangeEvent} are
* handled by this listener list. PropertyChangeEvents are handled in
* {@link #changeSupport}.
*/
protected EventListenerList listenerList = new EventListenerList();
/**
* Handles VetoableChangeEvents.
*/
private VetoableChangeSupport vetoableChangeSupport;
/**
* Storage for "client properties", which are key/value pairs associated
* with this component by a "client", such as a user application or a
* layout manager. This is lazily constructed when the component gets its
* first client property.
*/
private Hashtable clientProperties;
private InputMap inputMap_whenFocused;
private InputMap inputMap_whenAncestorOfFocused;
private ComponentInputMap inputMap_whenInFocusedWindow;
private ActionMap actionMap;
/** @since 1.3 */
private boolean verifyInputWhenFocusTarget = true;
private InputVerifier inputVerifier;
private TransferHandler transferHandler;
/**
* Indicates if this component is currently painting a tile or not.
*/
private boolean paintingTile;
/**
* A temporary buffer used for fast dragging of components.
*/
private Image dragBuffer;
/**
* Indicates if the dragBuffer is already initialized.
*/
private boolean dragBufferInitialized;
/**
* A cached Rectangle object to be reused. Be careful when you use that,
* so that it doesn't get modified in another context within the same
* method call chain.
*/
private static transient Rectangle rectCache;
/**
* The default locale of the component.
*
* @see #getDefaultLocale
* @see #setDefaultLocale
*/
private static Locale defaultLocale;
public static final String TOOL_TIP_TEXT_KEY = "ToolTipText";
/**
* Constant used to indicate that no condition has been assigned to a
* particular action.
*
* @see #registerKeyboardAction(ActionListener, KeyStroke, int)
*/
public static final int UNDEFINED_CONDITION = -1;
/**
* Constant used to indicate that an action should be performed only when
* the component has focus.
*
* @see #registerKeyboardAction(ActionListener, KeyStroke, int)
*/
public static final int WHEN_FOCUSED = 0;
/**
* Constant used to indicate that an action should be performed only when
* the component is an ancestor of the component which has focus.
*
* @see #registerKeyboardAction(ActionListener, KeyStroke, int)
*/
public static final int WHEN_ANCESTOR_OF_FOCUSED_COMPONENT = 1;
/**
* Constant used to indicate that an action should be performed only when
* the component is in the window which has focus.
*
* @see #registerKeyboardAction(ActionListener, KeyStroke, int)
*/
public static final int WHEN_IN_FOCUSED_WINDOW = 2;
/**
* Indicates if the opaque property has been set by a client program or by
* the UI.
*
* @see #setUIProperty(String, Object)
* @see LookAndFeel#installProperty(JComponent, String, Object)
*/
private boolean clientOpaqueSet = false;
/**
* Indicates if the autoscrolls property has been set by a client program or
* by the UI.
*
* @see #setUIProperty(String, Object)
* @see LookAndFeel#installProperty(JComponent, String, Object)
*/
private boolean clientAutoscrollsSet = false;
/**
* Creates a new <code>JComponent</code> instance.
*/
public JComponent()
{
super();
setDropTarget(new DropTarget());
setLocale(getDefaultLocale());
debugGraphicsOptions = DebugGraphics.NONE_OPTION;
setRequestFocusEnabled(true);
}
/**
* Helper to lazily construct and return the client properties table.
*
* @return The current client properties table
*
* @see #clientProperties
* @see #getClientProperty
* @see #putClientProperty
*/
private Hashtable getClientProperties()
{
if (clientProperties == null)
clientProperties = new Hashtable();
return clientProperties;
}
/**
* Get a client property associated with this component and a particular
* key.
*
* @param key The key with which to look up the client property
*
* @return A client property associated with this object and key
*
* @see #clientProperties
* @see #getClientProperties
* @see #putClientProperty
*/
public final Object getClientProperty(Object key)
{
return getClientProperties().get(key);
}
/**
* Add a client property <code>value</code> to this component, associated
* with <code>key</code>. If there is an existing client property
* associated with <code>key</code>, it will be replaced. A
* {@link PropertyChangeEvent} is sent to registered listeners (with the
* name of the property being <code>key.toString()</code>).
*
* @param key The key of the client property association to add
* @param value The value of the client property association to add
*
* @see #clientProperties
* @see #getClientProperties
* @see #getClientProperty
*/
public final void putClientProperty(Object key, Object value)
{
Hashtable t = getClientProperties();
Object old = t.get(key);
if (value != null)
t.put(key, value);
else
t.remove(key);
firePropertyChange(key.toString(), old, value);
}
/**
* Unregister an <code>AncestorListener</code>.
*
* @param listener The listener to unregister
*
* @see #addAncestorListener
*/
public void removeAncestorListener(AncestorListener listener)
{
listenerList.remove(AncestorListener.class, listener);
}
/**
* Unregister a <code>VetoableChangeChangeListener</code>.
*
* @param listener The listener to unregister
*
* @see #addVetoableChangeListener
*/
public void removeVetoableChangeListener(VetoableChangeListener listener)
{
if (vetoableChangeSupport != null)
vetoableChangeSupport.removeVetoableChangeListener(listener);
}
/**
* Register an <code>AncestorListener</code>.
*
* @param listener The listener to register
*
* @see #removeVetoableChangeListener
*/
public void addAncestorListener(AncestorListener listener)
{
listenerList.add(AncestorListener.class, listener);
}
/**
* Register a <code>VetoableChangeListener</code>.
*
* @param listener The listener to register
*
* @see #removeVetoableChangeListener
* @see #listenerList
*/
public void addVetoableChangeListener(VetoableChangeListener listener)
{
// Lazily instantiate this, it's rarely needed.
if (vetoableChangeSupport == null)
vetoableChangeSupport = new VetoableChangeSupport(this);
vetoableChangeSupport.addVetoableChangeListener(listener);
}
/**
* Returns all registered {@link EventListener}s of the given
* <code>listenerType</code>.
*
* @param listenerType the class of listeners to filter (<code>null</code>
* not permitted).
*
* @return An array of registered listeners.
*
* @throws ClassCastException if <code>listenerType</code> does not implement
* the {@link EventListener} interface.
* @throws NullPointerException if <code>listenerType</code> is
* <code>null</code>.
*
* @see #getAncestorListeners()
* @see #listenerList
*
* @since 1.3
*/
public EventListener[] getListeners(Class listenerType)
{
if (listenerType == PropertyChangeListener.class)
return getPropertyChangeListeners();
else if (listenerType == VetoableChangeListener.class)
return getVetoableChangeListeners();
else
return listenerList.getListeners(listenerType);
}
/**
* Return all registered <code>AncestorListener</code> objects.
*
* @return The set of <code>AncestorListener</code> objects in {@link
* #listenerList}
*/
public AncestorListener[] getAncestorListeners()
{
return (AncestorListener[]) getListeners(AncestorListener.class);
}
/**
* Return all registered <code>VetoableChangeListener</code> objects.
*
* @return An array of the <code>VetoableChangeListener</code> objects
* registered with this component (possibly empty but never
* <code>null</code>).
*
* @since 1.4
*/
public VetoableChangeListener[] getVetoableChangeListeners()
{
return vetoableChangeSupport == null ? new VetoableChangeListener[0]
: vetoableChangeSupport.getVetoableChangeListeners();
}
/**
* Call {@link VetoableChangeListener#vetoableChange} on all listeners
* registered to listen to a given property. Any method which changes
* the specified property of this component should call this method.
*
* @param propertyName The property which changed
* @param oldValue The old value of the property
* @param newValue The new value of the property
*
* @throws PropertyVetoException if the change was vetoed by a listener
*
* @see #addVetoableChangeListener
* @see #removeVetoableChangeListener
*/
protected void fireVetoableChange(String propertyName, Object oldValue,
Object newValue)
throws PropertyVetoException
{
if (vetoableChangeSupport != null)
vetoableChangeSupport.fireVetoableChange(propertyName, oldValue, newValue);
}
/**
* Fires a property change for a primitive integer property.
*
* @param property the name of the property
* @param oldValue the old value of the property
* @param newValue the new value of the property
*
* @specnote This method is implemented in
* {@link Component#firePropertyChange(String, int, int)}. It is
* only here because it is specified to be public, whereas the
* Component method is protected.
*/
public void firePropertyChange(String property, int oldValue, int newValue)
{
super.firePropertyChange(property, oldValue, newValue);
}
/**
* Fires a property change for a primitive boolean property.
*
* @param property the name of the property
* @param oldValue the old value of the property
* @param newValue the new value of the property
*
* @specnote This method is implemented in
* {@link Component#firePropertyChange(String, boolean, boolean)}.
* It is only here because it is specified to be public, whereas
* the Component method is protected.
*/
public void firePropertyChange(String property, boolean oldValue,
boolean newValue)
{
super.firePropertyChange(property, oldValue, newValue);
}
/**
* Fires a property change for a primitive character property.
*
* @param property the name of the property
* @param oldValue the old value of the property
* @param newValue the new value of the property
*/
public void firePropertyChange(String property, char oldValue,
char newValue)
{
// FIXME - This method is already public in awt Component, but
// is included here to work around a compilation bug in gcj 4.1.
super.firePropertyChange(property, oldValue, newValue);
}
/**
* Get the value of the accessibleContext property for this component.
*
* @return the current value of the property
*/
public AccessibleContext getAccessibleContext()
{
return null;
}
/**
* Get the value of the {@link #alignmentX} property.
*
* @return The current value of the property.
*
* @see #setAlignmentX
* @see #alignmentY
*/
public float getAlignmentX()
{
float ret = alignmentX;
if (alignmentX < 0)
// alignment has not been set explicitly.
ret = super.getAlignmentX();
return ret;
}
/**
* Get the value of the {@link #alignmentY} property.
*
* @return The current value of the property.
*
* @see #setAlignmentY
* @see #alignmentX
*/
public float getAlignmentY()
{
float ret = alignmentY;
if (alignmentY < 0)
// alignment has not been set explicitly.
ret = super.getAlignmentY();
return ret;
}
/**
* Get the current value of the {@link #autoscrolls} property.
*
* @return The current value of the property
*/
public boolean getAutoscrolls()
{
return autoscrolls;
}
/**
* Set the value of the {@link #border} property.
*
* @param newBorder The new value of the property
*
* @see #getBorder
*/
public void setBorder(Border newBorder)
{
Border oldBorder = getBorder();
if (oldBorder == newBorder)
return;
border = newBorder;
firePropertyChange("border", oldBorder, newBorder);
repaint();
}
/**
* Get the value of the {@link #border} property.
*
* @return The property's current value
*
* @see #setBorder
*/
public Border getBorder()
{
return border;
}
/**
* Get the component's current bounding box. If a rectangle is provided,
* use this as the return value (adjusting its fields in place);
* otherwise (of <code>null</code> is provided) return a new {@link
* Rectangle}.
*
* @param rv Optional return value to use
*
* @return A rectangle bounding the component
*/
public Rectangle getBounds(Rectangle rv)
{
if (rv == null)
return new Rectangle(getX(), getY(), getWidth(), getHeight());
else
{
rv.setBounds(getX(), getY(), getWidth(), getHeight());
return rv;
}
}
/**
* Prepares a graphics context for painting this object. If {@link
* #debugGraphicsOptions} is not equal to {@link
* DebugGraphics#NONE_OPTION}, produce a new {@link DebugGraphics} object
* wrapping the parameter. Otherwise configure the parameter with this
* component's foreground color and font.
*
* @param g The graphics context to wrap or configure
*
* @return A graphics context to paint this object with
*
* @see #debugGraphicsOptions
* @see #paint
*/
protected Graphics getComponentGraphics(Graphics g)
{
Graphics g2 = g;
int options = getDebugGraphicsOptions();
if (options != DebugGraphics.NONE_OPTION)
{
if (!(g2 instanceof DebugGraphics))
g2 = new DebugGraphics(g);
DebugGraphics dg = (DebugGraphics) g2;
dg.setDebugOptions(dg.getDebugOptions() | options);
}
g2.setFont(this.getFont());
g2.setColor(this.getForeground());
return g2;
}
/**
* Get the value of the {@link #debugGraphicsOptions} property.
*
* @return The current value of the property.
*
* @see #setDebugGraphicsOptions
* @see #debugGraphicsOptions
*/
public int getDebugGraphicsOptions()
{
String option = System.getProperty("gnu.javax.swing.DebugGraphics");
int options = debugGraphicsOptions;
if (option != null && option.length() != 0)
{
if (options < 0)
options = 0;
if (option.equals("LOG"))
options |= DebugGraphics.LOG_OPTION;
else if (option.equals("FLASH"))
options |= DebugGraphics.FLASH_OPTION;
}
return options;
}
/**
* Get the component's insets, which are calculated from
* the {@link #border} property. If the border is <code>null</code>,
* calls {@link Container#getInsets}.
*
* @return The component's current insets
*/
public Insets getInsets()
{
if (border == null)
return super.getInsets();
return getBorder().getBorderInsets(this);
}
/**
* Get the component's insets, which are calculated from the {@link
* #border} property. If the border is <code>null</code>, calls {@link
* Container#getInsets}. The passed-in {@link Insets} value will be
* used as the return value, if possible.
*
* @param insets Return value object to reuse, if possible
*
* @return The component's current insets
*/
public Insets getInsets(Insets insets)
{
Insets t = getInsets();
if (insets == null)
return t;
insets.left = t.left;
insets.right = t.right;
insets.top = t.top;
insets.bottom = t.bottom;
return insets;
}
/**
* Get the component's location. The passed-in {@link Point} value
* will be used as the return value, if possible.
*
* @param rv Return value object to reuse, if possible
*
* @return The component's current location
*/
public Point getLocation(Point rv)
{
if (rv == null)
return new Point(getX(), getY());
rv.setLocation(getX(), getY());
return rv;
}
/**
* Get the component's maximum size. If the {@link #maximumSize} property
* has been explicitly set, it is returned. If the {@link #maximumSize}
* property has not been set but the {@link #ui} property has been, the
* result of {@link ComponentUI#getMaximumSize} is returned. If neither
* property has been set, the result of {@link Container#getMaximumSize}
* is returned.
*
* @return The maximum size of the component
*
* @see #maximumSize
* @see #setMaximumSize
*/
public Dimension getMaximumSize()
{
if (maximumSize != null)
return maximumSize;
if (ui != null)
{
Dimension s = ui.getMaximumSize(this);
if (s != null)
return s;
}
Dimension p = super.getMaximumSize();
return p;
}
/**
* Get the component's minimum size. If the {@link #minimumSize} property
* has been explicitly set, it is returned. If the {@link #minimumSize}
* property has not been set but the {@link #ui} property has been, the
* result of {@link ComponentUI#getMinimumSize} is returned. If neither
* property has been set, the result of {@link Container#getMinimumSize}
* is returned.
*
* @return The minimum size of the component
*
* @see #minimumSize
* @see #setMinimumSize
*/
public Dimension getMinimumSize()
{
if (minimumSize != null)
return minimumSize;
if (ui != null)
{
Dimension s = ui.getMinimumSize(this);
if (s != null)
return s;
}
Dimension p = super.getMinimumSize();
return p;
}
/**
* Get the component's preferred size. If the {@link #preferredSize}
* property has been explicitly set, it is returned. If the {@link
* #preferredSize} property has not been set but the {@link #ui} property
* has been, the result of {@link ComponentUI#getPreferredSize} is
* returned. If neither property has been set, the result of {@link
* Container#getPreferredSize} is returned.
*
* @return The preferred size of the component
*
* @see #preferredSize
* @see #setPreferredSize
*/
public Dimension getPreferredSize()
{
Dimension prefSize = null;
if (preferredSize != null)
prefSize = new Dimension(preferredSize);
else if (ui != null)
{
Dimension s = ui.getPreferredSize(this);
if (s != null)
prefSize = s;
}
if (prefSize == null)
prefSize = super.getPreferredSize();
return prefSize;
}
/**
* Checks if a maximum size was explicitely set on the component.
*
* @return <code>true</code> if a maximum size was set,
* <code>false</code> otherwise
*
* @since 1.3
*/
public boolean isMaximumSizeSet()
{
return maximumSize != null;
}
/**
* Checks if a minimum size was explicitely set on the component.
*
* @return <code>true</code> if a minimum size was set,
* <code>false</code> otherwise
*
* @since 1.3
*/
public boolean isMinimumSizeSet()
{
return minimumSize != null;
}
/**
* Checks if a preferred size was explicitely set on the component.
*
* @return <code>true</code> if a preferred size was set,
* <code>false</code> otherwise
*
* @since 1.3
*/
public boolean isPreferredSizeSet()
{
return preferredSize != null;
}
/**
* Return the value of the <code>nextFocusableComponent</code> property.
*
* @return The current value of the property, or <code>null</code>
* if none has been set.
*
* @deprecated See {@link java.awt.FocusTraversalPolicy}
*/
public Component getNextFocusableComponent()
{
Container focusRoot = this;
if (! this.isFocusCycleRoot())
focusRoot = getFocusCycleRootAncestor();
FocusTraversalPolicy policy = focusRoot.getFocusTraversalPolicy();
return policy.getComponentAfter(focusRoot, this);
}
/**
* Return the set of {@link KeyStroke} objects which are registered
* to initiate actions on this component.
*
* @return An array of the registered keystrokes (possibly empty but never
* <code>null</code>).
*/
public KeyStroke[] getRegisteredKeyStrokes()
{
KeyStroke[] ks0;
KeyStroke[] ks1;
KeyStroke[] ks2;
if (inputMap_whenFocused != null)
ks0 = inputMap_whenFocused.keys();
else
ks0 = new KeyStroke[0];
if (inputMap_whenAncestorOfFocused != null)
ks1 = inputMap_whenAncestorOfFocused.keys();
else
ks1 = new KeyStroke[0];
if (inputMap_whenInFocusedWindow != null)
ks2 = inputMap_whenInFocusedWindow.keys();
else
ks2 = new KeyStroke[0];
int count = ks0.length + ks1.length + ks2.length;
KeyStroke[] result = new KeyStroke[count];
System.arraycopy(ks0, 0, result, 0, ks0.length);
System.arraycopy(ks1, 0, result, ks0.length, ks1.length);
System.arraycopy(ks2, 0, result, ks0.length + ks1.length, ks2.length);
return result;
}
/**
* Returns the first ancestor of this component which is a {@link JRootPane}.
* Equivalent to calling <code>SwingUtilities.getRootPane(this);</code>.
*
* @return An ancestral JRootPane, or <code>null</code> if none exists.
*/
public JRootPane getRootPane()
{
JRootPane p = SwingUtilities.getRootPane(this);
return p;
}
/**
* Get the component's size. The passed-in {@link Dimension} value
* will be used as the return value, if possible.
*
* @param rv Return value object to reuse, if possible
*
* @return The component's current size
*/
public Dimension getSize(Dimension rv)
{
if (rv == null)
return new Dimension(getWidth(), getHeight());
else
{
rv.setSize(getWidth(), getHeight());
return rv;
}
}
/**
* Return the <code>toolTip</code> property of this component, creating it and
* setting it if it is currently <code>null</code>. This method can be
* overridden in subclasses which wish to control the exact form of
* tooltip created.
*
* @return The current toolTip
*/
public JToolTip createToolTip()
{
JToolTip toolTip = new JToolTip();
toolTip.setComponent(this);
toolTip.setTipText(toolTipText);
return toolTip;
}
/**
* Return the location at which the {@link #toolTipText} property should be
* displayed, when triggered by a particular mouse event.
*
* @param event The event the tooltip is being presented in response to
*
* @return The point at which to display a tooltip, or <code>null</code>
* if swing is to choose a default location.
*/
public Point getToolTipLocation(MouseEvent event)
{
return null;
}
/**
* Set the value of the {@link #toolTipText} property.
*
* @param text The new property value
*
* @see #getToolTipText()
*/
public void setToolTipText(String text)
{
if (text == null)
{
ToolTipManager.sharedInstance().unregisterComponent(this);
toolTipText = null;
return;
}
// XXX: The tip text doesn't get updated unless you set it to null
// and then to something not-null. This is consistent with the behaviour
// of Sun's ToolTipManager.
String oldText = toolTipText;
toolTipText = text;
if (oldText == null)
ToolTipManager.sharedInstance().registerComponent(this);
}
/**
* Get the value of the {@link #toolTipText} property.
*
* @return The current property value
*
* @see #setToolTipText
*/
public String getToolTipText()
{
return toolTipText;
}
/**
* Get the value of the {@link #toolTipText} property, in response to a
* particular mouse event.
*
* @param event The mouse event which triggered the tooltip
*
* @return The current property value
*
* @see #setToolTipText
*/
public String getToolTipText(MouseEvent event)
{
return getToolTipText();
}
/**
* Returns the flag that controls whether or not the component inherits its
* parent's popup menu when no popup menu is specified for this component.
*
* @return A boolean.
*
* @since 1.5
*
* @see #setInheritsPopupMenu(boolean)
*/
public boolean getInheritsPopupMenu()
{
return inheritsPopupMenu;
}
/**
* Sets the flag that controls whether or not the component inherits its
* parent's popup menu when no popup menu is specified for this component.
* This is a bound property with the property name 'inheritsPopupMenu'.
*
* @param inherit the new flag value.
*
* @since 1.5
*
* @see #getInheritsPopupMenu()
*/
public void setInheritsPopupMenu(boolean inherit)
{
if (inheritsPopupMenu != inherit)
{
inheritsPopupMenu = inherit;
this.firePropertyChange("inheritsPopupMenu", ! inherit, inherit);
}
}
/**
* Returns the popup menu for this component. If the popup menu is
* <code>null</code> AND the {@link #getInheritsPopupMenu()} method returns
* <code>true</code>, this method will return the parent's popup menu (if it
* has one).
*
* @return The popup menu (possibly <code>null</code>.
*
* @since 1.5
*
* @see #setComponentPopupMenu(JPopupMenu)
* @see #getInheritsPopupMenu()
*/
public JPopupMenu getComponentPopupMenu()
{
if (componentPopupMenu == null && getInheritsPopupMenu())
{
Container parent = getParent();
if (parent instanceof JComponent)
return ((JComponent) parent).getComponentPopupMenu();
else
return null;
}
else
return componentPopupMenu;
}
/**
* Sets the popup menu for this component (this is a bound property with
* the property name 'componentPopupMenu').
*
* @param popup the popup menu (<code>null</code> permitted).
*
* @since 1.5
*
* @see #getComponentPopupMenu()
*/
public void setComponentPopupMenu(JPopupMenu popup)
{
if (componentPopupMenu != popup)
{
JPopupMenu old = componentPopupMenu;
componentPopupMenu = popup;
firePropertyChange("componentPopupMenu", old, popup);
}
}
/**
* Return the top level ancestral container (usually a {@link
* java.awt.Window} or {@link java.applet.Applet}) which this component is
* contained within, or <code>null</code> if no ancestors exist.
*
* @return The top level container, if it exists
*/
public Container getTopLevelAncestor()
{
Container c = getParent();
for (Container peek = c; peek != null; peek = peek.getParent())
c = peek;
return c;
}
/**
* Compute the component's visible rectangle, which is defined
* recursively as either the component's bounds, if it has no parent, or
* the intersection of the component's bounds with the visible rectangle
* of its parent.
*
* @param rect The return value slot to place the visible rectangle in
*/
public void computeVisibleRect(Rectangle rect)
{
Component c = getParent();
if (c != null && c instanceof JComponent)
{
((JComponent) c).computeVisibleRect(rect);
rect.translate(-getX(), -getY());
rect = SwingUtilities.computeIntersection(0, 0, getWidth(),
getHeight(), rect);
}
else
rect.setRect(0, 0, getWidth(), getHeight());
}
/**
* Return the component's visible rectangle in a new {@link Rectangle},
* rather than via a return slot.
*
* @return the component's visible rectangle
*
* @see #computeVisibleRect(Rectangle)
*/
public Rectangle getVisibleRect()
{
Rectangle r = new Rectangle();
computeVisibleRect(r);
return r;
}
/**
* <p>Requests that this component receive input focus, giving window
* focus to the top level ancestor of this component. Only works on
* displayable, focusable, visible components.</p>
*
* <p>This method should not be called by clients; it is intended for
* focus implementations. Use {@link Component#requestFocus()} instead.</p>
*
* @see Component#requestFocus()
*/
public void grabFocus()
{
requestFocus();
}
/**
* Get the value of the {@link #doubleBuffered} property.
*
* @return The property's current value
*/
public boolean isDoubleBuffered()
{
return doubleBuffered;
}
/**
* Return <code>true</code> if the provided component has no native peer;
* in other words, if it is a "lightweight component".
*
* @param c The component to test for lightweight-ness
*
* @return Whether or not the component is lightweight
*/
public static boolean isLightweightComponent(Component c)
{
return c.getPeer() instanceof LightweightPeer;
}
/**
* Return <code>true</code> if you wish this component to manage its own
* focus. In particular: if you want this component to be sent
* <code>TAB</code> and <code>SHIFT+TAB</code> key events, and to not
* have its children considered as focus transfer targets. If
* <code>true</code>, focus traversal around this component changes to
* <code>CTRL+TAB</code> and <code>CTRL+SHIFT+TAB</code>.
*
* @return <code>true</code> if you want this component to manage its own
* focus, otherwise (by default) <code>false</code>
*
* @deprecated 1.4 Use {@link Component#setFocusTraversalKeys(int, Set)} and
* {@link Container#setFocusCycleRoot(boolean)} instead
*/
public boolean isManagingFocus()
{
return false;
}
/**
* Return the current value of the {@link #opaque} property.
*
* @return The current property value
*/
public boolean isOpaque()
{
return opaque;
}
/**
* Return <code>true</code> if the component can guarantee that none of its
* children will overlap in Z-order. This is a hint to the painting system.
* The default is to return <code>true</code>, but some components such as
* {@link JLayeredPane} should override this to return <code>false</code>.
*
* @return Whether the component tiles its children
*/
public boolean isOptimizedDrawingEnabled()
{
return true;
}
/**
* Return <code>true</code> if this component is currently painting a tile,
* this means that paint() is called again on another child component. This
* method returns <code>false</code> if this component does not paint a tile
* or if the last tile is currently painted.
*
* @return whether the component is painting a tile
*/
public boolean isPaintingTile()
{
return paintingTile;
}
/**
* Get the value of the {@link #requestFocusEnabled} property.
*
* @return The current value of the property
*/
public boolean isRequestFocusEnabled()
{
return requestFocusEnabled;
}
/**
* Return <code>true</code> if this component is a validation root; this
* will cause calls to {@link #invalidate()} in this component's children
* to be "captured" at this component, and not propagate to its parents.
* For most components this should return <code>false</code>, but some
* components such as {@link JViewport} will want to return
* <code>true</code>.
*
* @return Whether this component is a validation root
*/
public boolean isValidateRoot()
{
return false;
}
/**
* <p>Paint the component. This is a delicate process, and should only be
* called from the repaint thread, under control of the {@link
* RepaintManager}. Client code should usually call {@link #repaint()} to
* trigger painting.</p>
*
* <p>The body of the <code>paint</code> call involves calling {@link
* #paintComponent}, {@link #paintBorder}, and {@link #paintChildren} in
* order. If you want to customize painting behavior, you should override
* one of these methods rather than <code>paint</code>.</p>
*
* <p>For more details on the painting sequence, see <a
* href="http://java.sun.com/products/jfc/tsc/articles/painting/index.html">
* this article</a>.</p>
*
* @param g The graphics context to paint with
*
* @see #paintImmediately(Rectangle)
*/
public void paint(Graphics g)
{
RepaintManager rm = RepaintManager.currentManager(this);
// We do a little stunt act here to switch on double buffering if it's
// not already on. If we are not already doublebuffered, then we jump
// into the method paintDoubleBuffered, which turns on the double buffer
// and then calls paint(g) again. In the second call we go into the else
// branch of this if statement and actually paint things to the double
// buffer. When this method completes, the call stack unwinds back to
// paintDoubleBuffered, where the buffer contents is finally drawn to the
// screen.
if (!paintingDoubleBuffered && isDoubleBuffered()
&& rm.isDoubleBufferingEnabled())
{
Rectangle clip = g.getClipBounds();
paintDoubleBuffered(clip);
}
else
{
if (getClientProperty("bufferedDragging") != null
&& dragBuffer == null)
{
initializeDragBuffer();
}
else if (getClientProperty("bufferedDragging") == null
&& dragBuffer != null)
{
dragBuffer = null;
}
if (g.getClip() == null)
g.setClip(0, 0, getWidth(), getHeight());
if (dragBuffer != null && dragBufferInitialized)
{
g.drawImage(dragBuffer, 0, 0, this);
}
else
{
Graphics g2 = getComponentGraphics(g);
paintComponent(g2);
paintBorder(g2);
paintChildren(g2);
}
}
}
/**
* Initializes the drag buffer by creating a new image and painting this
* component into it.
*/
private void initializeDragBuffer()
{
dragBufferInitialized = false;
// Allocate new dragBuffer if the current one is too small.
if (dragBuffer == null || dragBuffer.getWidth(this) < getWidth()
|| dragBuffer.getHeight(this) < getHeight())
{
dragBuffer = createImage(getWidth(), getHeight());
}
Graphics g = dragBuffer.getGraphics();
paint(g);
g.dispose();
dragBufferInitialized = true;
}
/**
* Paint the component's border. This usually means calling {@link
* Border#paintBorder} on the {@link #border} property, if it is
* non-<code>null</code>. You may override this if you wish to customize
* border painting behavior. The border is painted after the component's
* body, but before the component's children.
*
* @param g The graphics context with which to paint the border
*
* @see #paint
* @see #paintChildren
* @see #paintComponent
*/
protected void paintBorder(Graphics g)
{
if (getBorder() != null)
getBorder().paintBorder(this, g, 0, 0, getWidth(), getHeight());
}
/**
* Paint the component's children. This usually means calling {@link
* Container#paint}, which recursively calls {@link #paint} on any of the
* component's children, with appropriate changes to coordinate space and
* clipping region. You may override this if you wish to customize
* children painting behavior. The children are painted after the
* component's body and border.
*
* @param g The graphics context with which to paint the children
*
* @see #paint
* @see #paintBorder
* @see #paintComponent
*/
protected void paintChildren(Graphics g)
{
if (getComponentCount() > 0)
{
// Need to lock the tree to avoid problems with AWT and concurrency.
synchronized (getTreeLock())
{
for (int i = getComponentCount() - 1; i >= 0; i--)
{
Component child = getComponent(i);
if (child != null && child.isLightweight()
&& child.isVisible())
{
int cx = child.getX();
int cy = child.getY();
int cw = child.getWidth();
int ch = child.getHeight();
if (g.hitClip(cx, cy, cw, ch))
{
if ((! isOptimizedDrawingEnabled()) && i > 0)
{
// Check if the child is completely obscured.
Rectangle clip = g.getClipBounds(); // A copy.
SwingUtilities.computeIntersection(cx, cy, cw, ch,
clip);
if (isCompletelyObscured(i, clip))
continue; // Continues the for-loop.
}
Graphics cg = g.create(cx, cy, cw, ch);
cg.setColor(child.getForeground());
cg.setFont(child.getFont());
try
{
child.paint(cg);
}
finally
{
cg.dispose();
}
}
}
}
}
}
}
/**
* Determines if a region of a child component is completely obscured by one
* of its siblings.
*
* @param index the index of the child component
* @param rect the region to check
*
* @return <code>true</code> if the region is completely obscured by a
* sibling, <code>false</code> otherwise
*/
private boolean isCompletelyObscured(int index, Rectangle rect)
{
boolean obscured = false;
for (int i = index - 1; i >= 0 && obscured == false; i--)
{
Component sib = getComponent(i);
if (sib.isVisible())
{
Rectangle sibRect = sib.getBounds(rectCache);
if (sib.isOpaque() && rect.x >= sibRect.x
&& (rect.x + rect.width) <= (sibRect.x + sibRect.width)
&& rect.y >= sibRect.y
&& (rect.y + rect.height) <= (sibRect.y + sibRect.height))
{
obscured = true;
}
}
}
return obscured;
}
/**
* Paint the component's body. This usually means calling {@link
* ComponentUI#update} on the {@link #ui} property of the component, if
* it is non-<code>null</code>. You may override this if you wish to
* customize the component's body-painting behavior. The component's body
* is painted first, before the border and children.
*
* @param g The graphics context with which to paint the body
*
* @see #paint
* @see #paintBorder
* @see #paintChildren
*/
protected void paintComponent(Graphics g)
{
if (ui != null)
{
Graphics g2 = g.create();
try
{
ui.update(g2, this);
}
finally
{
g2.dispose();
}
}
}
/**
* A variant of {@link #paintImmediately(Rectangle)} which takes
* integer parameters.
*
* @param x The left x coordinate of the dirty region
* @param y The top y coordinate of the dirty region
* @param w The width of the dirty region
* @param h The height of the dirty region
*/
public void paintImmediately(int x, int y, int w, int h)
{
paintImmediately(new Rectangle(x, y, w, h));
}
/**
* Transform the provided dirty rectangle for this component into the
* appropriate ancestral {@link JRootPane} and call {@link #paint} on
* that root pane. This method is called from the {@link RepaintManager}
* and should always be called within the painting thread.
*
* <p>This method will acquire a double buffer from the {@link
* RepaintManager} if the component's {@link #doubleBuffered} property is
* <code>true</code> and the <code>paint</code> call is the
* <em>first</em> recursive <code>paint</code> call inside swing.</p>
*
* <p>The method will also modify the provided {@link Graphics} context
* via the {@link #getComponentGraphics} method. If you want to customize
* the graphics object used for painting, you should override that method
* rather than <code>paint</code>.</p>
*
* @param r The dirty rectangle to paint
*/
public void paintImmediately(Rectangle r)
{
// Try to find a root pane for this component.
//Component root = findPaintRoot(r);
Component root = findPaintRoot(r);
// If no paint root is found, then this component is completely overlapped
// by another component and we don't need repainting.
if (root == null|| !root.isShowing())
return;
SwingUtilities.convertRectangleToAncestor(this, r, root);
if (root instanceof JComponent)
((JComponent) root).paintImmediately2(r);
else
root.repaint(r.x, r.y, r.width, r.height);
}
/**
* Performs the actual work of paintImmediatly on the repaint root.
*
* @param r the area to be repainted
*/
void paintImmediately2(Rectangle r)
{
isRepainting = true;
RepaintManager rm = RepaintManager.currentManager(this);
if (rm.isDoubleBufferingEnabled() && isPaintingDoubleBuffered())
paintDoubleBuffered(r);
else
paintSimple(r);
isRepainting = false;
}
/**
* Returns true if we must paint double buffered, that is, when this
* component or any of it's ancestors are double buffered.
*
* @return true if we must paint double buffered, that is, when this
* component or any of it's ancestors are double buffered
*/
private boolean isPaintingDoubleBuffered()
{
boolean doubleBuffered = isDoubleBuffered();
Component parent = getParent();
while (! doubleBuffered && parent != null)
{
doubleBuffered = parent instanceof JComponent
&& ((JComponent) parent).isDoubleBuffered();
parent = parent.getParent();
}
return doubleBuffered;
}
/**
* Performs double buffered repainting.
*/
private void paintDoubleBuffered(Rectangle r)
{
RepaintManager rm = RepaintManager.currentManager(this);
// Paint on the offscreen buffer.
Component root = SwingUtilities.getRoot(this);
Image buffer = rm.getVolatileOffscreenBuffer(this, root.getWidth(),
root.getHeight());
// The volatile offscreen buffer may be null when that's not supported
// by the AWT backend. Fall back to normal backbuffer in this case.
if (buffer == null)
buffer = rm.getOffscreenBuffer(this, root.getWidth(), root.getHeight());
//Rectangle targetClip = SwingUtilities.convertRectangle(this, r, root);
Graphics g2 = buffer.getGraphics();
clipAndTranslateGraphics(root, this, g2);
g2.clipRect(r.x, r.y, r.width, r.height);
g2 = getComponentGraphics(g2);
paintingDoubleBuffered = true;
try
{
if (isRepainting) // Called from paintImmediately, go through paint().
paint(g2);
else // Called from paint() (AWT refresh), don't call it again.
{
paintComponent(g2);
paintBorder(g2);
paintChildren(g2);
}
}
finally
{
paintingDoubleBuffered = false;
g2.dispose();
}
// Paint the buffer contents on screen.
rm.commitBuffer(this, r);
}
/**
* Clips and translates the Graphics instance for painting on the double
* buffer. This has to be done, so that it reflects the component clip of the
* target component.
*
* @param root the root component (top-level container usually)
* @param target the component to be painted
* @param g the Graphics instance
*/
private void clipAndTranslateGraphics(Component root, Component target,
Graphics g)
{
Component parent = target;
int deltaX = 0;
int deltaY = 0;
while (parent != root)
{
deltaX += parent.getX();
deltaY += parent.getY();
parent = parent.getParent();
}
g.translate(deltaX, deltaY);
g.clipRect(0, 0, target.getWidth(), target.getHeight());
}
/**
* Performs normal painting without double buffering.
*
* @param r the area that should be repainted
*/
void paintSimple(Rectangle r)
{
Graphics g = getGraphics();
Graphics g2 = getComponentGraphics(g);
g2.setClip(r);
paint(g2);
g2.dispose();
if (g != g2)
g.dispose();
}
/**
* Return a string representation for this component, for use in
* debugging.
*
* @return A string describing this component.
*/
protected String paramString()
{
StringBuffer sb = new StringBuffer();
sb.append(super.paramString());
sb.append(",alignmentX=").append(getAlignmentX());
sb.append(",alignmentY=").append(getAlignmentY());
sb.append(",border=");
if (getBorder() != null)
sb.append(getBorder());
sb.append(",maximumSize=");
if (getMaximumSize() != null)
sb.append(getMaximumSize());
sb.append(",minimumSize=");
if (getMinimumSize() != null)
sb.append(getMinimumSize());
sb.append(",preferredSize=");
if (getPreferredSize() != null)
sb.append(getPreferredSize());
return sb.toString();
}
/**
* A variant of {@link
* #registerKeyboardAction(ActionListener,String,KeyStroke,int)} which
* provides <code>null</code> for the command name.
*
* @param act the action listener to notify when the keystroke occurs.
* @param stroke the key stroke.
* @param cond the condition (one of {@link #WHEN_FOCUSED},
* {@link #WHEN_IN_FOCUSED_WINDOW} and
* {@link #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}).
*/
public void registerKeyboardAction(ActionListener act,
KeyStroke stroke,
int cond)
{
registerKeyboardAction(act, null, stroke, cond);
}
/*
* There is some charmingly undocumented behavior sun seems to be using
* to simulate the old register/unregister keyboard binding API. It's not
* clear to me why this matters, but we shall endeavour to follow suit.
*
* Two main thing seem to be happening when you do registerKeyboardAction():
*
* - no actionMap() entry gets created, just an entry in inputMap()
*
* - the inputMap() entry is a proxy class which invokes the the
* binding's actionListener as a target, and which clobbers the command
* name sent in the ActionEvent, providing the binding command name
* instead.
*
* This much you can work out just by asking the input and action maps
* what they contain after making bindings, and watching the event which
* gets delivered to the recipient. Beyond that, it seems to be a
* sun-private solution so I will only immitate it as much as it matters
* to external observers.
*/
private static class ActionListenerProxy
extends AbstractAction
{
ActionListener target;
String bindingCommandName;
public ActionListenerProxy(ActionListener li,
String cmd)
{
target = li;
bindingCommandName = cmd;
}
public void actionPerformed(ActionEvent e)
{
ActionEvent derivedEvent = new ActionEvent(e.getSource(),
e.getID(),
bindingCommandName,
e.getModifiers());
target.actionPerformed(derivedEvent);
}
}
/**
* An obsolete method to register a keyboard action on this component.
* You should use <code>getInputMap</code> and <code>getActionMap</code>
* to fetch mapping tables from keystrokes to commands, and commands to
* actions, respectively, and modify those mappings directly.
*
* @param act The action to be registered
* @param cmd The command to deliver in the delivered {@link
* java.awt.event.ActionEvent}
* @param stroke The keystroke to register on
* @param cond One of the values {@link #UNDEFINED_CONDITION},
* {@link #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}, {@link #WHEN_FOCUSED}, or
* {@link #WHEN_IN_FOCUSED_WINDOW}, indicating the condition which must
* be met for the action to be fired
*
* @see #unregisterKeyboardAction
* @see #getConditionForKeyStroke
* @see #resetKeyboardActions
*/
public void registerKeyboardAction(ActionListener act,
String cmd,
KeyStroke stroke,
int cond)
{
ActionListenerProxy proxy = new ActionListenerProxy(act, cmd);
getInputMap(cond).put(stroke, proxy);
getActionMap().put(proxy, proxy);
}
/**
* Sets the input map for the given condition.
*
* @param condition the condition (one of {@link #WHEN_FOCUSED},
* {@link #WHEN_IN_FOCUSED_WINDOW} and
* {@link #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}).
* @param map the map.
*
* @throws IllegalArgumentException if <code>condition</code> is not one of
* the specified values.
*/
public final void setInputMap(int condition, InputMap map)
{
enableEvents(AWTEvent.KEY_EVENT_MASK);
switch (condition)
{
case WHEN_FOCUSED:
inputMap_whenFocused = map;
break;
case WHEN_ANCESTOR_OF_FOCUSED_COMPONENT:
inputMap_whenAncestorOfFocused = map;
break;
case WHEN_IN_FOCUSED_WINDOW:
if (map != null && !(map instanceof ComponentInputMap))
throw new
IllegalArgumentException("WHEN_IN_FOCUSED_WINDOW " +
"InputMap must be a ComponentInputMap");
inputMap_whenInFocusedWindow = (ComponentInputMap)map;
break;
case UNDEFINED_CONDITION:
default:
throw new IllegalArgumentException();
}
}
/**
* Returns the input map associated with this component for the given
* state/condition.
*
* @param condition the state (one of {@link #WHEN_FOCUSED},
* {@link #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT} and
* {@link #WHEN_IN_FOCUSED_WINDOW}).
*
* @return The input map.
* @throws IllegalArgumentException if <code>condition</code> is not one of
* the specified values.
* @since 1.3
*/
public final InputMap getInputMap(int condition)
{
enableEvents(AWTEvent.KEY_EVENT_MASK);
switch (condition)
{
case WHEN_FOCUSED:
if (inputMap_whenFocused == null)
inputMap_whenFocused = new InputMap();
return inputMap_whenFocused;
case WHEN_ANCESTOR_OF_FOCUSED_COMPONENT:
if (inputMap_whenAncestorOfFocused == null)
inputMap_whenAncestorOfFocused = new InputMap();
return inputMap_whenAncestorOfFocused;
case WHEN_IN_FOCUSED_WINDOW:
if (inputMap_whenInFocusedWindow == null)
inputMap_whenInFocusedWindow = new ComponentInputMap(this);
return inputMap_whenInFocusedWindow;
case UNDEFINED_CONDITION:
default:
throw new IllegalArgumentException("Invalid 'condition' argument: "
+ condition);
}
}
/**
* Returns the input map associated with this component for the
* {@link #WHEN_FOCUSED} state.
*
* @return The input map.
*
* @since 1.3
* @see #getInputMap(int)
*/
public final InputMap getInputMap()
{
return getInputMap(WHEN_FOCUSED);
}
public final ActionMap getActionMap()
{
if (actionMap == null)
actionMap = new ActionMap();
return actionMap;
}
public final void setActionMap(ActionMap map)
{
actionMap = map;
}
/**
* Return the condition that determines whether a registered action
* occurs in response to the specified keystroke.
*
* As of 1.3 KeyStrokes can be registered with multiple simultaneous
* conditions.
*
* @param ks The keystroke to return the condition of
*
* @return One of the values {@link #UNDEFINED_CONDITION}, {@link
* #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}, {@link #WHEN_FOCUSED}, or {@link
* #WHEN_IN_FOCUSED_WINDOW}
*
* @see #registerKeyboardAction(ActionListener, KeyStroke, int)
* @see #unregisterKeyboardAction
* @see #resetKeyboardActions
*/
public int getConditionForKeyStroke(KeyStroke ks)
{
if (inputMap_whenFocused != null
&& inputMap_whenFocused.get(ks) != null)
return WHEN_FOCUSED;
else if (inputMap_whenAncestorOfFocused != null
&& inputMap_whenAncestorOfFocused.get(ks) != null)
return WHEN_ANCESTOR_OF_FOCUSED_COMPONENT;
else if (inputMap_whenInFocusedWindow != null
&& inputMap_whenInFocusedWindow.get(ks) != null)
return WHEN_IN_FOCUSED_WINDOW;
else
return UNDEFINED_CONDITION;
}
/**
* Get the ActionListener (typically an {@link Action} object) which is
* associated with a particular keystroke.
*
* @param ks The keystroke to retrieve the action of
*
* @return The action associated with the specified keystroke
*/
public ActionListener getActionForKeyStroke(KeyStroke ks)
{
Object key = getInputMap(JComponent.WHEN_FOCUSED).get(ks);
if (key == null)
key = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).get(ks);
if (key == null)
key = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).get(ks);
if (key != null)
{
if (key instanceof ActionListenerProxy)
return ((ActionListenerProxy) key).target;
else
return getActionMap().get(key);
}
return null;
}
/**
* A hook for subclasses which want to customize event processing.
*/
protected void processComponentKeyEvent(KeyEvent e)
{
// This method does nothing, it is meant to be overridden by subclasses.
}
/**
* Override the default key dispatch system from Component to hook into
* the swing {@link InputMap} / {@link ActionMap} system.
*
* See <a
* href="http://java.sun.com/products/jfc/tsc/special_report/kestrel/keybindings.html">
* this report</a> for more details, it's somewhat complex.
*/
protected void processKeyEvent(KeyEvent e)
{
// let the AWT event processing send KeyEvents to registered listeners
super.processKeyEvent(e);
processComponentKeyEvent(e);
if (e.isConsumed())
return;
// Input maps are checked in this order:
// 1. The focused component's WHEN_FOCUSED map is checked.
// 2. The focused component's WHEN_ANCESTOR_OF_FOCUSED_COMPONENT map.
// 3. The WHEN_ANCESTOR_OF_FOCUSED_COMPONENT maps of the focused
// component's parent, then its parent's parent, and so on.
// Note: Input maps for disabled components are skipped.
// 4. The WHEN_IN_FOCUSED_WINDOW maps of all the enabled components in
// the focused window are searched.
KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(e);
boolean pressed = e.getID() == KeyEvent.KEY_PRESSED;
if (processKeyBinding(keyStroke, e, WHEN_FOCUSED, pressed))
{
// This is step 1 from above comment.
e.consume();
return;
}
else if (processKeyBinding
(keyStroke, e, WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, pressed))
{
// This is step 2 from above comment.
e.consume();
return;
}
// This is step 3 from above comment.
Container current = getParent();
while (current != null)
{
// If current is a JComponent, see if it handles the event in its
// WHEN_ANCESTOR_OF_FOCUSED_COMPONENT maps.
if ((current instanceof JComponent) &&
((JComponent)current).processKeyBinding
(keyStroke, e,WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, pressed))
{
e.consume();
return;
}
// Stop when we've tried a top-level container and it didn't handle it
if (current instanceof Window || current instanceof Applet)
break;
// Move up the hierarchy
current = current.getParent();
}
// Current being null means the JComponent does not currently have a
// top-level ancestor, in which case we don't need to check
// WHEN_IN_FOCUSED_WINDOW bindings.
if (current == null || e.isConsumed())
return;
// This is step 4 from above comment. KeyboardManager maintains mappings
// related to WHEN_IN_FOCUSED_WINDOW bindings so that we don't have to
// traverse the containment hierarchy each time.
if (KeyboardManager.getManager().processKeyStroke(current, keyStroke, e))
e.consume();
}
protected boolean processKeyBinding(KeyStroke ks,
KeyEvent e,
int condition,
boolean pressed)
{
if (isEnabled())
{
Action act = null;
Object cmd = null;
InputMap map = getInputMap(condition);
if (map != null)
{
cmd = map.get(ks);
if (cmd != null)
{
if (cmd instanceof ActionListenerProxy)
act = (Action) cmd;
else
act = (Action) getActionMap().get(cmd);
}
}
if (act != null && act.isEnabled())
{
// Need to synchronize here so we don't get in trouble with
// our __command__ hack.
synchronized (act)
{
// We add the command as value to the action, so that
// the action can later determine the command with which it
// was called. This is undocumented, but shouldn't affect
// compatibility. It allows us to use only one Action instance
// to do the work for all components of one type, instead of
// having loads of small Actions. This effectivly saves startup
// time of Swing.
act.putValue("__command__", cmd);
return SwingUtilities.notifyAction(act, ks, e, this,
e.getModifiers());
}
}
}
return false;
}
/**
* Remove a keyboard action registry.
*
* @param aKeyStroke The keystroke to unregister
*
* @see #registerKeyboardAction(ActionListener, KeyStroke, int)
* @see #getConditionForKeyStroke
* @see #resetKeyboardActions
*/
public void unregisterKeyboardAction(KeyStroke aKeyStroke)
{
ActionMap am = getActionMap();
// This loops through the conditions WHEN_FOCUSED,
// WHEN_ANCESTOR_OF_FOCUSED_COMPONENT and WHEN_IN_FOCUSED_WINDOW.
for (int cond = 0; cond < 3; cond++)
{
InputMap im = getInputMap(cond);
if (im != null)
{
Object action = im.get(aKeyStroke);
if (action != null && am != null)
am.remove(action);
im.remove(aKeyStroke);
}
}
}
/**
* Reset all keyboard action registries.
*
* @see #registerKeyboardAction(ActionListener, KeyStroke, int)
* @see #unregisterKeyboardAction
* @see #getConditionForKeyStroke
*/
public void resetKeyboardActions()
{
if (inputMap_whenFocused != null)
inputMap_whenFocused.clear();
if (inputMap_whenAncestorOfFocused != null)
inputMap_whenAncestorOfFocused.clear();
if (inputMap_whenInFocusedWindow != null)
inputMap_whenInFocusedWindow.clear();
if (actionMap != null)
actionMap.clear();
}
/**
* Mark the described region of this component as dirty in the current
* {@link RepaintManager}. This will queue an asynchronous repaint using
* the system painting thread in the near future.
*
* @param tm ignored
* @param x coordinate of the region to mark as dirty
* @param y coordinate of the region to mark as dirty
* @param width dimension of the region to mark as dirty
* @param height dimension of the region to mark as dirty
*/
public void repaint(long tm, int x, int y, int width, int height)
{
RepaintManager.currentManager(this).addDirtyRegion(this, x, y, width,
height);
}
/**
* Mark the described region of this component as dirty in the current
* {@link RepaintManager}. This will queue an asynchronous repaint using
* the system painting thread in the near future.
*
* @param r The rectangle to mark as dirty
*/
public void repaint(Rectangle r)
{
RepaintManager.currentManager(this).addDirtyRegion(this, r.x, r.y, r.width,
r.height);
}
/**
* Request focus on the default component of this component's {@link
* FocusTraversalPolicy}.
*
* @return The result of {@link #requestFocus()}
*
* @deprecated Use {@link #requestFocus()} on the default component provided
* from the {@link FocusTraversalPolicy} instead.
*/
public boolean requestDefaultFocus()
{
return false;
}
/**
* Queue a an invalidation and revalidation of this component, using
* {@link RepaintManager#addInvalidComponent}.
*/
public void revalidate()
{
// As long as we don't have a parent we don't need to do any layout, since
// this is done anyway as soon as we get connected to a parent.
if (getParent() == null)
return;
if (! EventQueue.isDispatchThread())
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
revalidate();
}
});
else
{
invalidate();
RepaintManager.currentManager(this).addInvalidComponent(this);
}
}
/**
* Calls <code>scrollRectToVisible</code> on the component's parent.
* Components which can service this call should override.
*
* @param r The rectangle to make visible
*/
public void scrollRectToVisible(Rectangle r)
{
Component p = getParent();
if (p instanceof JComponent)
((JComponent) p).scrollRectToVisible(r);
}
/**
* Set the value of the {@link #alignmentX} property.
*
* @param a The new value of the property
*/
public void setAlignmentX(float a)
{
if (a < 0.0F)
alignmentX = 0.0F;
else if (a > 1.0)
alignmentX = 1.0F;
else
alignmentX = a;
}
/**
* Set the value of the {@link #alignmentY} property.
*
* @param a The new value of the property
*/
public void setAlignmentY(float a)
{
if (a < 0.0F)
alignmentY = 0.0F;
else if (a > 1.0)
alignmentY = 1.0F;
else
alignmentY = a;
}
/**
* Set the value of the {@link #autoscrolls} property.
*
* @param a The new value of the property
*/
public void setAutoscrolls(boolean a)
{
autoscrolls = a;
clientAutoscrollsSet = true;
}
/**
* Set the value of the {@link #debugGraphicsOptions} property.
*
* @param debugOptions The new value of the property
*/
public void setDebugGraphicsOptions(int debugOptions)
{
debugGraphicsOptions = debugOptions;
}
/**
* Set the value of the {@link #doubleBuffered} property.
*
* @param db The new value of the property
*/
public void setDoubleBuffered(boolean db)
{
doubleBuffered = db;
}
/**
* Set the value of the <code>enabled</code> property.
*
* @param enable The new value of the property
*/
public void setEnabled(boolean enable)
{
if (enable == isEnabled())
return;
super.setEnabled(enable);
firePropertyChange("enabled", !enable, enable);
repaint();
}
/**
* Set the value of the <code>font</code> property.
*
* @param f The new value of the property
*/
public void setFont(Font f)
{
if (f == getFont())
return;
super.setFont(f);
revalidate();
repaint();
}
/**
* Set the value of the <code>background</code> property.
*
* @param bg The new value of the property
*/
public void setBackground(Color bg)
{
if (bg == getBackground())
return;
super.setBackground(bg);
repaint();
}
/**
* Set the value of the <code>foreground</code> property.
*
* @param fg The new value of the property
*/
public void setForeground(Color fg)
{
if (fg == getForeground())
return;
super.setForeground(fg);
repaint();
}
/**
* Set the value of the {@link #maximumSize} property. The passed value is
* copied, the later direct changes on the argument have no effect on the
* property value.
*
* @param max The new value of the property
*/
public void setMaximumSize(Dimension max)
{
Dimension oldMaximumSize = maximumSize;
if (max != null)
maximumSize = new Dimension(max);
else
maximumSize = null;
firePropertyChange("maximumSize", oldMaximumSize, maximumSize);
}
/**
* Set the value of the {@link #minimumSize} property. The passed value is
* copied, the later direct changes on the argument have no effect on the
* property value.
*
* @param min The new value of the property
*/
public void setMinimumSize(Dimension min)
{
Dimension oldMinimumSize = minimumSize;
if (min != null)
minimumSize = new Dimension(min);
else
minimumSize = null;
firePropertyChange("minimumSize", oldMinimumSize, minimumSize);
}
/**
* Set the value of the {@link #preferredSize} property. The passed value is
* copied, the later direct changes on the argument have no effect on the
* property value.
*
* @param pref The new value of the property
*/
public void setPreferredSize(Dimension pref)
{
Dimension oldPreferredSize = preferredSize;
if (pref != null)
preferredSize = new Dimension(pref);
else
preferredSize = null;
firePropertyChange("preferredSize", oldPreferredSize, preferredSize);
}
/**
* Set the specified component to be the next component in the
* focus cycle, overriding the {@link FocusTraversalPolicy} for
* this component.
*
* @param aComponent The component to set as the next focusable
*
* @deprecated Use FocusTraversalPolicy instead
*/
public void setNextFocusableComponent(Component aComponent)
{
Container focusRoot = this;
if (! this.isFocusCycleRoot())
focusRoot = getFocusCycleRootAncestor();
FocusTraversalPolicy policy = focusRoot.getFocusTraversalPolicy();
if (policy instanceof CompatibilityFocusTraversalPolicy)
{
policy = new CompatibilityFocusTraversalPolicy(policy);
focusRoot.setFocusTraversalPolicy(policy);
}
CompatibilityFocusTraversalPolicy p =
(CompatibilityFocusTraversalPolicy) policy;
Component old = getNextFocusableComponent();
if (old != null)
{
p.removeNextFocusableComponent(this, old);
}
if (aComponent != null)
{
p.addNextFocusableComponent(this, aComponent);
}
}
/**
* Set the value of the {@link #requestFocusEnabled} property.
*
* @param e The new value of the property
*/
public void setRequestFocusEnabled(boolean e)
{
requestFocusEnabled = e;
}
/**
* Get the value of the {@link #transferHandler} property.
*
* @return The current value of the property
*
* @see #setTransferHandler
*/
public TransferHandler getTransferHandler()
{
return transferHandler;
}
/**
* Set the value of the {@link #transferHandler} property.
*
* @param newHandler The new value of the property
*
* @see #getTransferHandler
*/
public void setTransferHandler(TransferHandler newHandler)
{
if (transferHandler == newHandler)
return;
TransferHandler oldHandler = transferHandler;
transferHandler = newHandler;
firePropertyChange("transferHandler", oldHandler, newHandler);
}
/**
* Set if the component should paint all pixels withing its bounds.
* If this property is set to false, the component expects the cleared
* background.
*
* @param isOpaque if true, paint all pixels. If false, expect the clean
* background.
*
* @see ComponentUI#update
*/
public void setOpaque(boolean isOpaque)
{
boolean oldOpaque = opaque;
opaque = isOpaque;
clientOpaqueSet = true;
firePropertyChange("opaque", oldOpaque, opaque);
}
/**
* Set the value of the visible property.
*
* If the value is changed, then the AncestorListeners of this component
* and all its children (recursivly) are notified.
*
* @param v The new value of the property
*/
public void setVisible(boolean v)
{
// No need to do anything if the actual value doesn't change.
if (isVisible() == v)
return;
super.setVisible(v);
// Notify AncestorListeners.
if (v == true)
fireAncestorEvent(this, AncestorEvent.ANCESTOR_ADDED);
else
fireAncestorEvent(this, AncestorEvent.ANCESTOR_REMOVED);
Container parent = getParent();
if (parent != null)
parent.repaint(getX(), getY(), getWidth(), getHeight());
revalidate();
}
/**
* Call {@link #paint}.
*
* @param g The graphics context to paint into
*/
public void update(Graphics g)
{
paint(g);
}
/**
* Get the value of the UIClassID property. This property should be a key
* in the {@link UIDefaults} table managed by {@link UIManager}, the
* value of which is the name of a class to load for the component's
* {@link #ui} property.
*
* @return A "symbolic" name which will map to a class to use for the
* component's UI, such as <code>"ComponentUI"</code>
*
* @see #setUI
* @see #updateUI
*/
public String getUIClassID()
{
return "ComponentUI";
}
/**
* Install a new UI delegate as the component's {@link #ui} property. In
* the process, this will call {@link ComponentUI#uninstallUI} on any
* existing value for the {@link #ui} property, and {@link
* ComponentUI#installUI} on the new UI delegate.
*
* @param newUI The new UI delegate to install
*
* @see #updateUI
* @see #getUIClassID
*/
protected void setUI(ComponentUI newUI)
{
if (ui != null)
ui.uninstallUI(this);
ComponentUI oldUI = ui;
ui = newUI;
if (ui != null)
ui.installUI(this);
firePropertyChange("UI", oldUI, newUI);
revalidate();
repaint();
}
/**
* This method should be overridden in subclasses. In JComponent, the
* method does nothing. In subclasses, it should a UI delegate
* (corresponding to the symbolic name returned from {@link
* #getUIClassID}) from the {@link UIManager}, and calls {@link #setUI}
* with the new delegate.
*/
public void updateUI()
{
// Nothing to do here.
}
/**
* Returns the locale used as the default for all new components. The
* default value is {@link Locale#getDefault()} (that is, the platform
* default locale).
*
* @return The locale (never <code>null</code>).
*
* @see #setDefaultLocale(Locale)
*/
public static Locale getDefaultLocale()
{
if (defaultLocale == null)
defaultLocale = Locale.getDefault();
return defaultLocale;
}
/**
* Sets the locale to be used as the default for all new components. If this
* is set to <code>null</code>, the {@link #getDefaultLocale()} method will
* return the platform default locale.
*
* @param l the locale (<code>null</code> permitted).
*/
public static void setDefaultLocale(Locale l)
{
defaultLocale = l;
}
/**
* Returns the currently set input verifier for this component.
*
* @return the input verifier, or <code>null</code> if none
*/
public InputVerifier getInputVerifier()
{
return inputVerifier;
}
/**
* Sets the input verifier to use by this component.
*
* @param verifier the input verifier, or <code>null</code>
*/
public void setInputVerifier(InputVerifier verifier)
{
InputVerifier oldVerifier = inputVerifier;
inputVerifier = verifier;
firePropertyChange("inputVerifier", oldVerifier, verifier);
}
/**
* @since 1.3
*/
public boolean getVerifyInputWhenFocusTarget()
{
return verifyInputWhenFocusTarget;
}
/**
* @since 1.3
*/
public void setVerifyInputWhenFocusTarget(boolean verifyInputWhenFocusTarget)
{
if (this.verifyInputWhenFocusTarget == verifyInputWhenFocusTarget)
return;
this.verifyInputWhenFocusTarget = verifyInputWhenFocusTarget;
firePropertyChange("verifyInputWhenFocusTarget",
! verifyInputWhenFocusTarget,
verifyInputWhenFocusTarget);
}
/**
* Requests that this component gets the input focus if the
* requestFocusEnabled property is set to <code>true</code>.
* This also means that this component's top-level window becomes
* the focused window, if that is not already the case.
*
* The preconditions that have to be met to become a focus owner is that
* the component must be displayable, visible and focusable.
*
* Note that this signals only a request for becoming focused. There are
* situations in which it is not possible to get the focus. So developers
* should not assume that the component has the focus until it receives
* a {@link java.awt.event.FocusEvent} with a value of
* {@link java.awt.event.FocusEvent#FOCUS_GAINED}.
*
* @see Component#requestFocus()
*/
public void requestFocus()
{
if (isRequestFocusEnabled())
super.requestFocus();
}
/**
* This method is overridden to make it public so that it can be used
* by look and feel implementations.
*
* You should not use this method directly. Instead you are strongly
* encouraged to call {@link #requestFocus()} or
* {@link #requestFocusInWindow()} instead.
*
* @param temporary if the focus change is temporary
*
* @return <code>false</code> if the focus change request will definitly
* fail, <code>true</code> if it will likely succeed
*
* @see Component#requestFocus(boolean)
*
* @since 1.4
*/
public boolean requestFocus(boolean temporary)
{
return super.requestFocus(temporary);
}
/**
* Requests that this component gets the input focus if the top level
* window that contains this component has the focus and the
* requestFocusEnabled property is set to <code>true</code>.
*
* The preconditions that have to be met to become a focus owner is that
* the component must be displayable, visible and focusable.
*
* Note that this signals only a request for becoming focused. There are
* situations in which it is not possible to get the focus. So developers
* should not assume that the component has the focus until it receives
* a {@link java.awt.event.FocusEvent} with a value of
* {@link java.awt.event.FocusEvent#FOCUS_GAINED}.
*
* @return <code>false</code> if the focus change request will definitly
* fail, <code>true</code> if it will likely succeed
*
* @see Component#requestFocusInWindow()
*/
public boolean requestFocusInWindow()
{
if (isRequestFocusEnabled())
return super.requestFocusInWindow();
else
return false;
}
/**
* This method is overridden to make it public so that it can be used
* by look and feel implementations.
*
* You should not use this method directly. Instead you are strongly
* encouraged to call {@link #requestFocus()} or
* {@link #requestFocusInWindow()} instead.
*
* @param temporary if the focus change is temporary
*
* @return <code>false</code> if the focus change request will definitly
* fail, <code>true</code> if it will likely succeed
*
* @see Component#requestFocus(boolean)
*
* @since 1.4
*/
protected boolean requestFocusInWindow(boolean temporary)
{
return super.requestFocusInWindow(temporary);
}
/**
* Receives notification if this component is added to a parent component.
*
* Notification is sent to all registered AncestorListeners about the
* new parent.
*
* This method sets up ActionListeners for all registered KeyStrokes of
* this component in the chain of parent components.
*
* A PropertyChange event is fired to indicate that the ancestor property
* has changed.
*
* This method is used internally and should not be used in applications.
*/
public void addNotify()
{
// Register the WHEN_IN_FOCUSED_WINDOW keyboard bindings
// Note that here we unregister all bindings associated with
// this component and then re-register them. This may be more than
// necessary if the top-level ancestor hasn't changed. Should
// maybe improve this.
KeyboardManager km = KeyboardManager.getManager();
km.clearBindingsForComp(this);
km.registerEntireMap((ComponentInputMap)
this.getInputMap(WHEN_IN_FOCUSED_WINDOW));
super.addNotify();
// Notify AncestorListeners.
fireAncestorEvent(this, AncestorEvent.ANCESTOR_ADDED);
// fire property change event for 'ancestor'
firePropertyChange("ancestor", null, getParent());
}
/**
* Receives notification that this component no longer has a parent.
*
* This method sends an AncestorEvent to all registered AncestorListeners,
* notifying them that the parent is gone.
*
* The keybord actions of this component are removed from the parent and
* its ancestors.
*
* A PropertyChangeEvent is fired to indicate that the 'ancestor' property
* has changed.
*
* This method is called before the component is actually removed from
* its parent, so the parent is still visible through
* {@link Component#getParent}.
*/
public void removeNotify()
{
super.removeNotify();
KeyboardManager.getManager().clearBindingsForComp(this);
// Notify ancestor listeners.
fireAncestorEvent(this, AncestorEvent.ANCESTOR_REMOVED);
// fire property change event for 'ancestor'
firePropertyChange("ancestor", getParent(), null);
}
/**
* Returns <code>true</code> if the coordinates (x, y) lie within
* the bounds of this component and <code>false</code> otherwise.
* x and y are relative to the coordinate space of the component.
*
* @param x the X coordinate of the point to check
* @param y the Y coordinate of the point to check
*
* @return <code>true</code> if the specified point lies within the bounds
* of this component, <code>false</code> otherwise
*/
public boolean contains(int x, int y)
{
if (ui == null)
return super.contains(x, y);
else
return ui.contains(this, x, y);
}
/**
* Disables this component.
*
* @deprecated replaced by {@link #setEnabled(boolean)}
*/
public void disable()
{
super.disable();
}
/**
* Enables this component.
*
* @deprecated replaced by {@link #setEnabled(boolean)}
*/
public void enable()
{
super.enable();
}
/**
* Returns the Graphics context for this component. This can be used
* to draw on a component.
*
* @return the Graphics context for this component
*/
public Graphics getGraphics()
{
return super.getGraphics();
}
/**
* Returns the X coordinate of the upper left corner of this component.
* Prefer this method over {@link #getBounds} or {@link #getLocation}
* because it does not cause any heap allocation.
*
* @return the X coordinate of the upper left corner of the component
*/
public int getX()
{
return super.getX();
}
/**
* Returns the Y coordinate of the upper left corner of this component.
* Prefer this method over {@link #getBounds} or {@link #getLocation}
* because it does not cause any heap allocation.
*
* @return the Y coordinate of the upper left corner of the component
*/
public int getY()
{
return super.getY();
}
/**
* Returns the height of this component. Prefer this method over
* {@link #getBounds} or {@link #getSize} because it does not cause
* any heap allocation.
*
* @return the height of the component
*/
public int getHeight()
{
return super.getHeight();
}
/**
* Returns the width of this component. Prefer this method over
* {@link #getBounds} or {@link #getSize} because it does not cause
* any heap allocation.
*
* @return the width of the component
*/
public int getWidth()
{
return super.getWidth();
}
/**
* Prints this component to the given Graphics context. A call to this
* method results in calls to the methods {@link #printComponent},
* {@link #printBorder} and {@link #printChildren} in this order.
*
* Double buffering is temporarily turned off so the painting goes directly
* to the supplied Graphics context.
*
* @param g the Graphics context to print onto
*/
public void print(Graphics g)
{
boolean doubleBufferState = isDoubleBuffered();
setDoubleBuffered(false);
printComponent(g);
printBorder(g);
printChildren(g);
setDoubleBuffered(doubleBufferState);
}
/**
* Prints this component to the given Graphics context. This invokes
* {@link #print}.
*
* @param g the Graphics context to print onto
*/
public void printAll(Graphics g)
{
print(g);
}
/**
* Prints this component to the specified Graphics context. The default
* behaviour is to invoke {@link #paintComponent}. Override this
* if you want special behaviour for printing.
*
* @param g the Graphics context to print onto
*
* @since 1.3
*/
protected void printComponent(Graphics g)
{
paintComponent(g);
}
/**
* Print this component's children to the specified Graphics context.
* The default behaviour is to invoke {@link #paintChildren}. Override this
* if you want special behaviour for printing.
*
* @param g the Graphics context to print onto
*
* @since 1.3
*/
protected void printChildren(Graphics g)
{
paintChildren(g);
}
/**
* Print this component's border to the specified Graphics context.
* The default behaviour is to invoke {@link #paintBorder}. Override this
* if you want special behaviour for printing.
*
* @param g the Graphics context to print onto
*
* @since 1.3
*/
protected void printBorder(Graphics g)
{
paintBorder(g);
}
/**
* Processes mouse motion event, like dragging and moving.
*
* @param ev the MouseEvent describing the mouse motion
*/
protected void processMouseMotionEvent(MouseEvent ev)
{
super.processMouseMotionEvent(ev);
}
/**
* Moves and resizes the component.
*
* @param x the new horizontal location
* @param y the new vertial location
* @param w the new width
* @param h the new height
*/
public void reshape(int x, int y, int w, int h)
{
int oldX = getX();
int oldY = getY();
super.reshape(x, y, w, h);
// Notify AncestorListeners.
if (oldX != getX() || oldY != getY())
fireAncestorEvent(this, AncestorEvent.ANCESTOR_MOVED);
}
/**
* Fires an AncestorEvent to this component's and all of its child
* component's AncestorListeners.
*
* @param ancestor the component that triggered the event
* @param id the kind of ancestor event that should be fired
*/
void fireAncestorEvent(JComponent ancestor, int id)
{
// Fire event for registered ancestor listeners of this component.
AncestorListener[] listeners = getAncestorListeners();
if (listeners.length > 0)
{
AncestorEvent ev = new AncestorEvent(this, id,
ancestor, ancestor.getParent());
for (int i = 0; i < listeners.length; i++)
{
switch (id)
{
case AncestorEvent.ANCESTOR_MOVED:
listeners[i].ancestorMoved(ev);
break;
case AncestorEvent.ANCESTOR_ADDED:
listeners[i].ancestorAdded(ev);
break;
case AncestorEvent.ANCESTOR_REMOVED:
listeners[i].ancestorRemoved(ev);
break;
}
}
}
// Dispatch event to all children.
int numChildren = getComponentCount();
for (int i = 0; i < numChildren; i++)
{
Component child = getComponent(i);
if (! (child instanceof JComponent))
continue;
JComponent jc = (JComponent) child;
jc.fireAncestorEvent(ancestor, id);
}
}
/**
* Finds a suitable paint root for painting this component. This method first
* checks if this component is overlapped using
* {@link #findOverlapFreeParent(Rectangle)}. The returned paint root is then
* feeded to {@link #findOpaqueParent(Component)} to find the nearest opaque
* component for this paint root. If no paint is necessary, then we return
* <code>null</code>.
*
* @param c the clip of this component
*
* @return the paint root or <code>null</code> if no painting is necessary
*/
private Component findPaintRoot(Rectangle c)
{
Component p = findOverlapFreeParent(c);
if (p == null)
return null;
Component root = findOpaqueParent(p);
return root;
}
/**
* Scans the containment hierarchy upwards for components that overlap the
* this component in the specified clip. This method returns
* <code>this</code>, if no component overlaps this component. It returns
* <code>null</code> if another component completely covers this component
* in the specified clip (no repaint necessary). If another component partly
* overlaps this component in the specified clip, then the parent of this
* component is returned (this is the component that must be used as repaint
* root). For efficient lookup, the method
* {@link #isOptimizedDrawingEnabled()} is used.
*
* @param clip the clip of this component
*
* @return the paint root, or <code>null</code> if no paint is necessary
*/
private Component findOverlapFreeParent(Rectangle clip)
{
Rectangle currentClip = clip;
Component found = this;
Container parent = this;
while (parent != null && !(parent instanceof Window))
{
Container newParent = parent.getParent();
if (newParent == null || newParent instanceof Window)
break;
// If the parent is optimizedDrawingEnabled, then its children are
// tiled and cannot have an overlapping child. Go directly to next
// parent.
if ((newParent instanceof JComponent
&& ((JComponent) newParent).isOptimizedDrawingEnabled()))
{
parent = newParent;
continue;
}
// If the parent is not optimizedDrawingEnabled, we must check if the
// parent or some neighbor overlaps the current clip.
// This is the current clip converted to the parent's coordinate
// system. TODO: We can do this more efficiently by succesively
// cumulating the parent-child translations.
Rectangle target = SwingUtilities.convertRectangle(found,
currentClip,
newParent);
// We have an overlap if either:
// - The new parent itself doesn't completely cover the clip
// (this can be the case with viewports).
// - If some higher-level (than the current) children of the new parent
// intersect the target rectangle.
Rectangle parentRect = SwingUtilities.getLocalBounds(newParent);
boolean haveOverlap =
! SwingUtilities.isRectangleContainingRectangle(parentRect, target);
if (! haveOverlap)
{
Component child;
for (int i = 0; (child = newParent.getComponent(i)) != parent && !haveOverlap; i++)
{
Rectangle childRect = child.getBounds();
haveOverlap = target.intersects(childRect);
}
}
if (haveOverlap)
{
found = newParent;
currentClip = target;
}
parent = newParent;
}
//System.err.println("overlapfree parent: " + found);
return found;
}
/**
* Finds the nearest component to <code>c</code> (upwards in the containment
* hierarchy), that is opaque. If <code>c</code> itself is opaque,
* this returns <code>c</code> itself.
*
* @param c the start component for the search
* @return the nearest component to <code>c</code> (upwards in the containment
* hierarchy), that is opaque; If <code>c</code> itself is opaque,
* this returns <code>c</code> itself
*/
private Component findOpaqueParent(Component c)
{
Component found = c;
while (true)
{
if ((found instanceof JComponent) && ((JComponent) found).isOpaque())
break;
else if (!(found instanceof JComponent) && !found.isLightweight())
break;
Container p = found.getParent();
if (p == null)
break;
else
found = p;
}
return found;
}
/**
* This is the method that gets called when the WHEN_IN_FOCUSED_WINDOW map
* is changed.
*
* @param changed the JComponent associated with the WHEN_IN_FOCUSED_WINDOW
* map
*/
void updateComponentInputMap(ComponentInputMap changed)
{
// Since you can change a component's input map via
// setInputMap, we have to check if <code>changed</code>
// is still in our WHEN_IN_FOCUSED_WINDOW map hierarchy
InputMap curr = getInputMap(WHEN_IN_FOCUSED_WINDOW);
while (curr != null && curr != changed)
curr = curr.getParent();
// If curr is null then changed is not in the hierarchy
if (curr == null)
return;
// Now we have to update the keyboard manager's hashtable
KeyboardManager km = KeyboardManager.getManager();
// This is a poor strategy, should be improved. We currently
// delete all the old bindings for the component and then register
// the current bindings.
km.clearBindingsForComp(changed.getComponent());
km.registerEntireMap((ComponentInputMap)
getInputMap(WHEN_IN_FOCUSED_WINDOW));
}
/**
* Helper method for
* {@link LookAndFeel#installProperty(JComponent, String, Object)}.
*
* @param propertyName the name of the property
* @param value the value of the property
*
* @throws IllegalArgumentException if the specified property cannot be set
* by this method
* @throws ClassCastException if the property value does not match the
* property type
* @throws NullPointerException if <code>c</code> or
* <code>propertyValue</code> is <code>null</code>
*/
void setUIProperty(String propertyName, Object value)
{
if (propertyName.equals("opaque"))
{
if (! clientOpaqueSet)
{
setOpaque(((Boolean) value).booleanValue());
clientOpaqueSet = false;
}
}
else if (propertyName.equals("autoscrolls"))
{
if (! clientAutoscrollsSet)
{
setAutoscrolls(((Boolean) value).booleanValue());
clientAutoscrollsSet = false;
}
}
else
{
throw new IllegalArgumentException
("Unsupported property for LookAndFeel.installProperty(): "
+ propertyName);
}
}
}