diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/classpath/javax/swing/JComponent.java | |
download | cbb-gcc-4.6.4-upstream.tar.bz2 cbb-gcc-4.6.4-upstream.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'libjava/classpath/javax/swing/JComponent.java')
-rw-r--r-- | libjava/classpath/javax/swing/JComponent.java | 3801 |
1 files changed, 3801 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/JComponent.java b/libjava/classpath/javax/swing/JComponent.java new file mode 100644 index 000000000..365f62aa9 --- /dev/null +++ b/libjava/classpath/javax/swing/JComponent.java @@ -0,0 +1,3801 @@ +/* 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 gnu.java.lang.CPStringBuilder; + +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.ArrayList; +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; + } + } + + /** + * 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 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 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; + + + /** + * Used to optimize painting. This is set in paintImmediately2() to specify + * the exact component path to be painted by paintChildren. + */ + Component paintChild; + + /** + * 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); + + // When both old and new value are null, no event is fired. This is + // different from what firePropertyChange() normally does, so we add this + // check here. + if (old != null || value != null) + 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 <T extends EventListener> T[] getListeners(Class<T> listenerType) + { + if (listenerType == PropertyChangeListener.class) + return (T[]) getPropertyChangeListeners(); + else if (listenerType == VetoableChangeListener.class) + return (T[]) 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); + } + + /** + * 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 <code>maximumSize</code> property + * has been explicitly set, it is returned. If the <code>maximumSize</code> + * 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 Component#setMaximumSize + * @see Component#getMaximumSize() + * @see Component#isMaximumSizeSet() + * @see ComponentUI#getMaximumSize(JComponent) + */ + public Dimension getMaximumSize() + { + Dimension size = null; + if (isMaximumSizeSet()) + size = super.getMaximumSize(); + else + { + if (ui != null) + size = ui.getMaximumSize(this); + if (size == null) + size = super.getMaximumSize(); + } + return size; + } + + /** + * Get the component's minimum size. If the <code>minimumSize</code> property + * has been explicitly set, it is returned. If the <code>minimumSize</code> + * 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 Component#setMinimumSize + * @see Component#getMinimumSize() + * @see Component#isMinimumSizeSet() + * @see ComponentUI#getMinimumSize(JComponent) + */ + public Dimension getMinimumSize() + { + Dimension size = null; + if (isMinimumSizeSet()) + size = super.getMinimumSize(); + else + { + if (ui != null) + size = ui.getMinimumSize(this); + if (size == null) + size = super.getMinimumSize(); + } + return size; + } + + /** + * Get the component's preferred size. If the <code>preferredSize</code> + * property has been explicitly set, it is returned. If the + * <code>preferredSize</code> 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 Component#setPreferredSize + * @see Component#getPreferredSize() + * @see Component#isPreferredSizeSet() + * @see ComponentUI#getPreferredSize(JComponent) + */ + public Dimension getPreferredSize() + { + Dimension size = null; + if (isPreferredSizeSet()) + size = super.getPreferredSize(); + else + { + if (ui != null) + size = ui.getPreferredSize(this); + if (size == null) + size = super.getPreferredSize(); + } + return size; + } + + /** + * 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); + return toolTip; + } + + /** + * Return the location at which the <code>toolTipText</code> 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 tooltip text for this component. If a non-<code>null</code> + * value is set, this component is registered in the + * <code>ToolTipManager</code> in order to turn on tooltips for this + * component. If a <code>null</code> value is set, tooltips are turne off + * for this component. + * + * @param text the tooltip text for this component + * + * @see #getToolTipText() + * @see #getToolTipText(MouseEvent) + */ + public void setToolTipText(String text) + { + String old = getToolTipText(); + putClientProperty(TOOL_TIP_TEXT_KEY, text); + ToolTipManager ttm = ToolTipManager.sharedInstance(); + if (text == null) + ttm.unregisterComponent(this); + else if (old == null) + ttm.registerComponent(this); + } + + /** + * Returns the current tooltip text for this component, or <code>null</code> + * if none has been set. + * + * @return the current tooltip text for this component, or <code>null</code> + * if none has been set + * + * @see #setToolTipText + * @see #getToolTipText(MouseEvent) + */ + public String getToolTipText() + { + return (String) getClientProperty(TOOL_TIP_TEXT_KEY); + } + + /** + * Returns the tooltip text for this component for a particular mouse + * event. This can be used to support context sensitive tooltips that can + * change with the mouse location. By default this returns the static + * tooltip text returned by {@link #getToolTipText()}. + * + * @param event the mouse event which triggered the tooltip + * + * @return the tooltip text for this component for a particular mouse + * event + * + * @see #setToolTipText + * @see #getToolTipText() + */ + 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.x, clip.y, clip.width, clip.height); + } + else + { + if (getClientProperty("bufferedDragging") != null + && dragBuffer == null) + { + initializeDragBuffer(); + } + else if (getClientProperty("bufferedDragging") == null + && dragBuffer != null) + { + dragBuffer = null; + } + + Rectangle clip = g.getClipBounds(); + int clipX, clipY, clipW, clipH; + if (clip == null) + { + clipX = 0; + clipY = 0; + clipW = getWidth(); + clipH = getHeight(); + } + else + { + clipX = clip.x; + clipY = clip.y; + clipW = clip.width; + clipH = clip.height; + } + if (dragBuffer != null && dragBufferInitialized) + { + g.drawImage(dragBuffer, 0, 0, this); + } + else + { + Graphics g2 = getComponentGraphics(g); + if (! isOccupiedByChild(clipX, clipY, clipW, clipH)) + { + paintComponent(g2); + paintBorder(g2); + } + paintChildren(g2); + } + } + } + + /** + * Determines if a region of this component is completely occupied by + * an opaque child component, in which case we don't need to bother + * painting this component at all. + * + * @param x the area, x coordinate + * @param y the area, y coordinate + * @param w the area, width + * @param h the area, height + * + * @return <code>true</code> if the specified area is completely covered + * by a child component, <code>false</code> otherwise + */ + private boolean isOccupiedByChild(int x, int y, int w, int h) + { + boolean occupied = false; + int count = getComponentCount(); + for (int i = 0; i < count; i++) + { + Component child = getComponent(i); + int cx = child.getX(); + int cy = child.getY(); + int cw = child.getWidth(); + int ch = child.getHeight(); + if (child.isVisible() && x >= cx && x + w <= cx + cw && y >= cy + && y + h <= cy + ch) + { + occupied = child.isOpaque(); + break; + } + } + return occupied; + } + + /** + * 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()) + { + // Fast forward to the child to paint, if set by + // paintImmediately2() + int i = getComponentCount() - 1; + if (paintChild != null && paintChild.isOpaque()) + { + for (; i >= 0 && getComponent(i) != paintChild; i--) + ; + } + for (; 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.x, clip.y, + clip.width, clip.height)) + 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 x the region to check, x coordinate + * @param y the region to check, y coordinate + * @param w the region to check, width + * @param h the region to check, height + * + * @return <code>true</code> if the region is completely obscured by a + * sibling, <code>false</code> otherwise + */ + private boolean isCompletelyObscured(int index, int x, int y, int w, int h) + { + 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() && x >= sibRect.x + && (x + w) <= (sibRect.x + sibRect.width) + && y >= sibRect.y + && (y + h) <= (sibRect.y + sibRect.height)) + { + obscured = true; + } + } + } + return obscured; + } + + /** + * Checks if a component/rectangle is partially obscured by one of its + * siblings. + * Note that this doesn't check for completely obscured, this is + * done by isCompletelyObscured() and should probably also be checked. + * + * @param i the component index from which to start searching + * @param x the x coordinate of the rectangle to check + * @param y the y coordinate of the rectangle to check + * @param w the width of the rectangle to check + * @param h the height of the rectangle to check + * + * @return <code>true</code> if the rectangle is partially obscured + */ + private boolean isPartiallyObscured(int i, int x, int y, int w, int h) + { + boolean obscured = false; + for (int j = i - 1; j >= 0 && ! obscured; j--) + { + Component sibl = getComponent(j); + if (sibl.isVisible()) + { + Rectangle rect = sibl.getBounds(rectCache); + if (!(x + w <= rect.x) + || (y + h <= rect.y) + || (x >= rect.x + rect.width) + || (y >= rect.y + rect.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) + { + // Find opaque parent and call paintImmediately2() on it. + if (isShowing()) + { + Component c = this; + Component p; + while (c != null && ! c.isOpaque()) + { + p = c.getParent(); + if (p != null) + { + x += c.getX(); + y += c.getY(); + c = p; + } + } + if (c instanceof JComponent) + ((JComponent) c).paintImmediately2(x, y, w, h); + else + c.repaint(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) + { + paintImmediately(r.x, r.y, r.width, r.height); + } + + /** + * Performs the actual work of paintImmediatly on the repaint root. + * + * @param x the area to be repainted, X coordinate + * @param y the area to be repainted, Y coordinate + */ + void paintImmediately2(int x, int y, int w, int h) + { + // Optimization for components that are always painted on top. + boolean onTop = onTop() && isOpaque(); + + // Fetch the RepaintManager. + RepaintManager rm = RepaintManager.currentManager(this); + + // The painting clip; + int paintX = x; + int paintY = y; + int paintW = w; + int paintH = h; + + // If we should paint buffered or not. + boolean haveBuffer = false; + + // The component that is finally triggered for painting. + JComponent paintRoot = this; + + // Stores the component and all its parents. This will be used to limit + // the actually painted components in paintChildren by setting + // the field paintChild. + int pIndex = -1; + int pCount = 0; + ArrayList components = new ArrayList(); + + // Offset to subtract from the paintRoot rectangle when painting. + int offsX = 0; + int offsY = 0; + + // The current component and its child. + Component child; + Container c; + + // Find appropriate paint root. + for (c = this, child = null; + c != null && ! (c instanceof Window) && ! (c instanceof Applet); + child = c, c = c.getParent()) + { + JComponent jc = c instanceof JComponent ? (JComponent) c : null; + components.add(c); + if (! onTop && jc != null && ! jc.isOptimizedDrawingEnabled()) + { + // Indicates whether we reset the paint root to be the current + // component. + boolean updatePaintRoot = false; + + // Check obscured state of the child. + // Generally, we have 3 cases here: + // 1. Not obscured. No need to paint from the parent. + // 2. Partially obscured. Paint from the parent. + // 3. Completely obscured. No need to paint anything. + if (c != this) + { + if (jc.isPaintRoot()) + updatePaintRoot = true; + else + { + int count = c.getComponentCount(); + int i = 0; + for (; i < count && c.getComponent(i) != child; i++) + ; + + if (jc.isCompletelyObscured(i, paintX, paintY, paintW, + paintH)) + return; // No need to paint anything. + else if (jc.isPartiallyObscured(i, paintX, paintY, paintW, + paintH)) + updatePaintRoot = true; + + } + } + if (updatePaintRoot) + { + // Paint from parent. + paintRoot = jc; + pIndex = pCount; + offsX = 0; + offsY = 0; + haveBuffer = false; + } + } + pCount++; + // Check if component is double buffered. + if (rm.isDoubleBufferingEnabled() && jc != null + && jc.isDoubleBuffered()) + { + haveBuffer = true; + } + + // Clip the paint region with the parent. + if (! onTop) + { + paintX = Math.max(0, paintX); + paintY = Math.max(0, paintY); + paintW = Math.min(c.getWidth(), paintW + paintX) - paintX; + paintH = Math.min(c.getHeight(), paintH + paintY) - paintY; + int dx = c.getX(); + int dy = c.getY(); + paintX += dx; + paintY += dy; + offsX += dx; + offsY += dy; + } + } + if (c != null && c.getPeer() != null && paintW > 0 && paintH > 0) + { + isRepainting = true; + paintX -= offsX; + paintY -= offsY; + + // Set the painting path so that paintChildren paints only what we + // want. + if (paintRoot != this) + { + for (int i = pIndex; i > 0; i--) + { + Component paintParent = (Component) components.get(i); + if (paintParent instanceof JComponent) + ((JComponent) paintParent).paintChild = + (Component) components.get(i - 1); + } + } + + // Actually trigger painting. + if (haveBuffer) + paintRoot.paintDoubleBuffered(paintX, paintY, paintW, paintH); + else + { + Graphics g = paintRoot.getGraphics(); + try + { + g.setClip(paintX, paintY, paintW, paintH); + paintRoot.paint(g); + } + finally + { + g.dispose(); + } + } + + // Reset the painting path. + if (paintRoot != this) + { + for (int i = pIndex; i > 0; i--) + { + Component paintParent = (Component) components.get(i); + if (paintParent instanceof JComponent) + ((JComponent) paintParent).paintChild = null; + } + } + + isRepainting = false; + } + } + + /** + * Returns <code>true</code> if the component is guaranteed to be painted + * on top of others. This returns false by default and is overridden by + * components like JMenuItem, JPopupMenu and JToolTip to return true for + * added efficiency. + * + * @return <code>true</code> if the component is guaranteed to be painted + * on top of others + */ + boolean onTop() + { + return false; + } + + /** + * This returns true when a component needs to force itself as a paint + * origin. This is used for example in JViewport to make sure that it + * gets to update its backbuffer. + * + * @return true when a component needs to force itself as a paint + * origin + */ + boolean isPaintRoot() + { + return false; + } + + /** + * Performs double buffered repainting. + */ + private void paintDoubleBuffered(int x, int y, int w, int h) + { + 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(x, y, w, h); + 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, x, y, w, h); + } + + /** + * 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() + { + CPStringBuilder sb = new CPStringBuilder(); + 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 = 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) + { + // Search nearest JComponent. + int xOffs = getX(); + int yOffs = getY(); + Component p; + for (p = getParent(); p != null && ! (p instanceof JComponent); + p = p.getParent()) + { + xOffs += p.getX(); + yOffs += p.getY(); + } + if (p != null) + { + r.x += xOffs; + r.y += yOffs; + JComponent jParent = (JComponent) p; + jParent.scrollRectToVisible(r); + r.x -= xOffs; + r.y -= yOffs; + } + } + + /** + * 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 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); + } + } + + /** + * 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); + } + } +} |