diff options
Diffstat (limited to 'libjava/classpath/javax/swing/JComponent.java')
-rw-r--r-- | libjava/classpath/javax/swing/JComponent.java | 1053 |
1 files changed, 832 insertions, 221 deletions
diff --git a/libjava/classpath/javax/swing/JComponent.java b/libjava/classpath/javax/swing/JComponent.java index dc7689b0930..021a2a34080 100644 --- a/libjava/classpath/javax/swing/JComponent.java +++ b/libjava/classpath/javax/swing/JComponent.java @@ -44,6 +44,7 @@ import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; +import java.awt.EventQueue; import java.awt.FlowLayout; import java.awt.FocusTraversalPolicy; import java.awt.Font; @@ -53,6 +54,7 @@ import java.awt.Image; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; +import java.awt.Shape; import java.awt.Window; import java.awt.dnd.DropTarget; import java.awt.event.ActionEvent; @@ -64,7 +66,6 @@ import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.geom.Rectangle2D; -import java.awt.image.ImageObserver; import java.awt.peer.LightweightPeer; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -83,6 +84,8 @@ import javax.accessibility.AccessibleKeyBinding; import javax.accessibility.AccessibleRole; 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; @@ -121,9 +124,18 @@ public abstract class JComponent extends Container implements Serializable protected class AccessibleFocusHandler implements FocusListener { - protected AccessibleFocusHandler(){} - public void focusGained(FocusEvent event){} - public void focusLost(FocusEvent valevent){} + protected AccessibleFocusHandler() + { + // TODO: Implement this properly. + } + public void focusGained(FocusEvent event) + { + // TODO: Implement this properly. + } + public void focusLost(FocusEvent valevent) + { + // TODO: Implement this properly. + } } /** @@ -132,9 +144,18 @@ public abstract class JComponent extends Container implements Serializable protected class AccessibleContainerHandler implements ContainerListener { - protected AccessibleContainerHandler() {} - public void componentAdded(ContainerEvent event) {} - public void componentRemoved(ContainerEvent valevent) {} + protected AccessibleContainerHandler() + { + // TODO: Implement this properly. + } + public void componentAdded(ContainerEvent event) + { + // TODO: Implement this properly. + } + public void componentRemoved(ContainerEvent valevent) + { + // TODO: Implement this properly. + } } private static final long serialVersionUID = -7047089700479897799L; @@ -142,39 +163,217 @@ public abstract class JComponent extends Container implements Serializable protected ContainerListener accessibleContainerHandler; protected FocusListener accessibleFocusHandler; - protected AccessibleJComponent() {} - public void addPropertyChangeListener(PropertyChangeListener listener) {} - public void removePropertyChangeListener(PropertyChangeListener listener) {} - public int getAccessibleChildrenCount() { return 0; } - public Accessible getAccessibleChild(int value0) { return null; } - public AccessibleStateSet getAccessibleStateSet() { return null; } - public String getAccessibleName() { return null; } - public String getAccessibleDescription() { return null; } - public AccessibleRole getAccessibleRole() { return null; } - protected String getBorderTitle(Border value0) { return null; } - public String getToolTipText() { return null; } - public String getTitledBorderText() { return null; } - public AccessibleKeyBinding getAccessibleKeyBinding() { return null; } + /** + * Manages the property change listeners; + */ + private SwingPropertyChangeSupport changeSupport; + + protected AccessibleJComponent() + { + changeSupport = new SwingPropertyChangeSupport(this); + } + + /** + * Adds a property change listener to the list of registered listeners. + * + * @param listener the listener to add + */ + public void addPropertyChangeListener(PropertyChangeListener listener) + { + changeSupport.addPropertyChangeListener(listener); + } + + /** + * Removes a propery change listener from the list of registered listeners. + * + * @param listener the listener to remove + */ + public void removePropertyChangeListener(PropertyChangeListener listener) + { + changeSupport.removePropertyChangeListener(listener); + } + + /** + * Returns the number of accessible children of this object. + * + * @return the number of accessible children of this object + */ + public int getAccessibleChildrenCount() + { + int count = 0; + Component[] children = getComponents(); + for (int i = 0; i < children.length; ++i) + { + if (children[i] instanceof Accessible) + count++; + } + return count; + } + + /** + * 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) + { + int index = 0; + Component[] children = getComponents(); + Accessible found = null; + for (int j = 0; index != i; j++) + { + if (children[j] instanceof Accessible) + index++; + if (index == i) + found = (Accessible) children[index]; + } + // TODO: Figure out what to do when i is not a valid index. + return found; + } + + /** + * Returns the accessible state set of this component. + * + * @return the accessible state set of this component + */ + public AccessibleStateSet getAccessibleStateSet() + { + // FIXME: Figure out which states should be set here, and which are + // inherited from the super class. + return super.getAccessibleStateSet(); + } + + /** + * 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() + { + // TODO: Figure out what exactly to return here. It's possible that this + // method simply should return null. + return null; + } + + /** + * 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() + { + // TODO: Figure out what exactly to return here. It's possible that this + // method simply should return null. + return null; + } + + /** + * Returns the accessible role of this component. + * + * @return the accessible role of this component + * + * @see AccessibleRole + */ + public AccessibleRole getAccessibleRole() + { + // TODO: Check if this is correct. + 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() + { + // TODO: Implement this properly. + return null; + } } /** * An explicit value for the component's preferred size; if not set by a * user, this is calculated on the fly by delegating to the {@link - * ComponentUI.getPreferredSize} method on the {@link #ui} property. + * ComponentUI#getPreferredSize} method on the {@link #ui} property. */ Dimension preferredSize; /** * An explicit value for the component's minimum size; if not set by a * user, this is calculated on the fly by delegating to the {@link - * ComponentUI.getMinimumSize} method on the {@link #ui} property. + * ComponentUI#getMinimumSize} method on the {@link #ui} property. */ Dimension minimumSize; /** * An explicit value for the component's maximum size; if not set by a * user, this is calculated on the fly by delegating to the {@link - * ComponentUI.getMaximumSize} method on the {@link #ui} property. + * ComponentUI#getMaximumSize} method on the {@link #ui} property. */ Dimension maximumSize; @@ -191,7 +390,7 @@ public abstract class JComponent extends Container implements Serializable * @see javax.swing.OverlayLayout * @see javax.swing.BoxLayout */ - float alignmentX = 0.5f; + float alignmentX = -1.0F; /** * A value between 0.0 and 1.0 indicating the preferred vertical @@ -206,7 +405,7 @@ public abstract class JComponent extends Container implements Serializable * @see javax.swing.OverlayLayout * @see javax.swing.BoxLayout */ - float alignmentY = 0.5f; + float alignmentY = -1.0F; /** * The border painted around this component. @@ -219,14 +418,14 @@ public abstract class JComponent extends Container implements Serializable * The text to show in the tooltip associated with this component. * * @see #setToolTipText - * @see #getToolTipText + * @see #getToolTipText() */ String toolTipText; /** * <p>Whether to double buffer this component when painting. This flag - * should generally be <code>false</code>, except for top level - * components such as {@link JFrame} or {@link JApplet}.</p> + * 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 @@ -234,22 +433,21 @@ public abstract class JComponent extends Container implements Serializable * * @see #setDoubleBuffered * @see #isDoubleBuffered - * @see #paintLock * @see #paint */ - boolean doubleBuffered = false; + 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}. + * {@link DebugGraphics#NONE_OPTION}, {@link DebugGraphics#LOG_OPTION}, + * {@link DebugGraphics#FLASH_OPTION}, or {@link + * DebugGraphics#BUFFERED_OPTION}. * - * @see setDebugGraphicsOptions - * @see getDebugGraphicsOptions + * @see #setDebugGraphicsOptions + * @see #getDebugGraphicsOptions * @see DebugGraphics - * @see getComponentGraphics + * @see #getComponentGraphics */ int debugGraphicsOptions; @@ -299,7 +497,7 @@ public abstract class JComponent extends Container implements Serializable * try to request focus, but the request might fail. Thus it is only * a hint guiding swing's behavior. * - * @see #requestFocus + * @see #requestFocus() * @see #isRequestFocusEnabled * @see #setRequestFocusEnabled */ @@ -312,12 +510,18 @@ public abstract class JComponent extends Container implements Serializable * 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 + * @see #setAutoscrolls + * @see #getAutoscrolls */ boolean autoscrolls = false; /** + * Indicates whether the current paint call is already double buffered or + * not. + */ + static boolean isPaintingDoubleBuffered = false; + + /** * Listeners for events other than {@link PropertyChangeEvent} are * handled by this listener list. PropertyChangeEvents are handled in * {@link #changeSupport}. @@ -342,7 +546,7 @@ public abstract class JComponent extends Container implements Serializable private InputMap inputMap_whenFocused; private InputMap inputMap_whenAncestorOfFocused; - private InputMap inputMap_whenInFocusedWindow; + private ComponentInputMap inputMap_whenInFocusedWindow; private ActionMap actionMap; /** @since 1.3 */ private boolean verifyInputWhenFocusTarget; @@ -350,16 +554,17 @@ public abstract class JComponent extends Container implements Serializable private TransferHandler transferHandler; - /** - * A lock held during recursive painting; this is used to serialize - * access to the double buffer, and also to select the "top level" - * object which should acquire the double buffer in a given widget - * tree (which may have multiple double buffered children). - * - * @see #doubleBuffered - * @see #paint + /** + * Indicates if this component is currently painting a tile or not. + */ + private boolean paintingTile; + + /** + * 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 final Object paintLock = new Object(); + private static transient Rectangle rectCache; /** * The default locale of the component. @@ -404,6 +609,13 @@ public abstract class JComponent extends Container implements Serializable public static final int WHEN_IN_FOCUSED_WINDOW = 2; /** + * Indicates if this component is completely dirty or not. This is used + * by the RepaintManager's + * {@link RepaintManager#isCompletelyDirty(JComponent)} method. + */ + boolean isCompletelyDirty = false; + + /** * Creates a new <code>JComponent</code> instance. */ public JComponent() @@ -452,7 +664,9 @@ public abstract class JComponent extends Container implements Serializable /** * 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. + * 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 @@ -463,10 +677,13 @@ public abstract class JComponent extends Container implements Serializable */ public final void putClientProperty(Object key, Object value) { + Hashtable t = getClientProperties(); + Object old = t.get(key); if (value != null) - getClientProperties().put(key, value); + t.put(key, value); else - getClientProperties().remove(key); + t.remove(key); + firePropertyChange(key.toString(), old, value); } /** @@ -771,7 +988,8 @@ public abstract class JComponent extends Container implements Serializable { VetoableChangeListener[] listeners = getVetoableChangeListeners(); - PropertyChangeEvent evt = new PropertyChangeEvent(this, propertyName, oldValue, newValue); + PropertyChangeEvent evt = + new PropertyChangeEvent(this, propertyName, oldValue, newValue); for (int i = 0; i < listeners.length; i++) listeners[i].vetoableChange(evt); @@ -797,7 +1015,12 @@ public abstract class JComponent extends Container implements Serializable */ public float getAlignmentX() { - return alignmentX; + float ret = alignmentX; + if (alignmentX < 0) + // alignment has not been set explicitly. + ret = super.getAlignmentX(); + + return ret; } /** @@ -810,7 +1033,12 @@ public abstract class JComponent extends Container implements Serializable */ public float getAlignmentY() { - return alignmentY; + float ret = alignmentY; + if (alignmentY < 0) + // alignment has not been set explicitly. + ret = super.getAlignmentY(); + + return ret; } /** @@ -832,9 +1060,13 @@ public abstract class JComponent extends Container implements Serializable */ public void setBorder(Border newBorder) { - Border oldBorder = border; + Border oldBorder = getBorder(); + if (oldBorder == newBorder) + return; + border = newBorder; firePropertyChange("border", oldBorder, newBorder); + repaint(); } /** @@ -885,10 +1117,19 @@ public abstract class JComponent extends Container implements Serializable * @see #paint */ protected Graphics getComponentGraphics(Graphics g) - { - g.setFont (this.getFont()); - g.setColor (this.getForeground()); - return 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; } /** @@ -901,7 +1142,19 @@ public abstract class JComponent extends Container implements Serializable */ public int getDebugGraphicsOptions() { - return 0; + 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; } /** @@ -1298,6 +1551,7 @@ public abstract class JComponent extends Container implements Serializable */ public void grabFocus() { + // TODO: Implement this properly. } /** @@ -1366,13 +1620,16 @@ public abstract class JComponent extends Container implements Serializable } /** - * Return <code>true</code> if this component is currently painting a tile. + * 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 + * @return whether the component is painting a tile */ public boolean isPaintingTile() { - return false; + return paintingTile; } /** @@ -1406,16 +1663,6 @@ public abstract class JComponent extends Container implements Serializable * RepaintManager}. Client code should usually call {@link #repaint()} to * trigger painting.</p> * - * <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> - * * <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 @@ -1431,32 +1678,30 @@ public abstract class JComponent extends Container implements Serializable */ public void paint(Graphics g) { - Graphics g2 = g; - Image doubleBuffer = null; RepaintManager rm = RepaintManager.currentManager(this); - - if (isDoubleBuffered() - && (rm.isDoubleBufferingEnabled()) - && (! Thread.holdsLock(paintLock))) - { - doubleBuffer = rm.getOffscreenBuffer(this, getWidth(), getHeight()); - } - - synchronized (paintLock) + // 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 (!isPaintingDoubleBuffered && isDoubleBuffered() + && rm.isDoubleBufferingEnabled()) + paintDoubleBuffered(g); + else { - if (doubleBuffer != null) - { - g2 = doubleBuffer.getGraphics(); - g2.setClip(g.getClipBounds()); - } - - g2 = getComponentGraphics(g2); + if (g.getClip() == null) + g.setClip(0, 0, getWidth(), getHeight()); + Graphics g2 = getComponentGraphics(g); paintComponent(g2); paintBorder(g2); paintChildren(g2); - - if (doubleBuffer != null) - g.drawImage(doubleBuffer, 0, 0, (ImageObserver) null); + Rectangle clip = g2.getClipBounds(); + if (clip.x == 0 && clip.y == 0 && clip.width == getWidth() + && clip.height == getHeight()) + RepaintManager.currentManager(this).markCompletelyClean(this); } } @@ -1495,7 +1740,69 @@ public abstract class JComponent extends Container implements Serializable */ protected void paintChildren(Graphics g) { - super.paint(g); + Shape originalClip = g.getClip(); + Rectangle inner = SwingUtilities.calculateInnerArea(this, rectCache); + g.clipRect(inner.x, inner.y, inner.width, inner.height); + Component[] children = getComponents(); + + // Find the bottommost component that needs to be painted. This is a + // component that completely covers the current clip and is opaque. In + // this case we don't need to paint the components below it. + int startIndex = children.length - 1; + // No need to check for overlapping components when this component is + // optimizedDrawingEnabled (== it tiles its children). + if (! isOptimizedDrawingEnabled()) + { + Rectangle clip = g.getClipBounds(); + for (int i = 0; i < children.length; i++) + { + Rectangle childBounds = children[i].getBounds(); + if (children[i].isOpaque() + && SwingUtilities.isRectangleContainingRectangle(childBounds, + g.getClipBounds())) + { + startIndex = i; + break; + } + } + } + // paintingTile becomes true just before we start painting the component's + // children. + paintingTile = true; + for (int i = startIndex; i >= 0; --i) + { + // paintingTile must be set to false before we begin to start painting + // the last tile. + if (i == 0) + paintingTile = false; + + if (!children[i].isVisible()) + continue; + + Rectangle bounds = children[i].getBounds(rectCache); + Rectangle oldClip = g.getClipBounds(); + if (oldClip == null) + oldClip = bounds; + + if (!g.hitClip(bounds.x, bounds.y, bounds.width, bounds.height)) + continue; + + boolean translated = false; + try + { + g.clipRect(bounds.x, bounds.y, bounds.width, bounds.height); + g.translate(bounds.x, bounds.y); + translated = true; + children[i].paint(g); + } + finally + { + if (translated) + g.translate(-bounds.x, -bounds.y); + g.setClip(oldClip); + } + } + g.setClip(originalClip); } /** @@ -1518,7 +1825,7 @@ public abstract class JComponent extends Container implements Serializable Graphics g2 = g; if (!(g instanceof Graphics2D)) g2 = g.create(); - ui.update(getComponentGraphics(g2), this); + ui.update(g2, this); if (!(g instanceof Graphics2D)) g2.dispose(); } @@ -1544,24 +1851,93 @@ public abstract class JComponent extends Container implements Serializable * 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) { - Component root = SwingUtilities.getRoot(this); - if (root == null || ! root.isShowing()) + // Try to find a root pane for this component. + //Component root = findPaintRoot(r); + Component root = findPaintRoot(r); + // If no paint root is found, then this component is completely overlapped + // by another component and we don't need repainting. + if (root == null) return; - Graphics g = root.getGraphics(); - if (g == null) + if (root == null || !root.isShowing()) return; - Rectangle clip = SwingUtilities.convertRectangle(this, r, root); - g.setClip(clip); - root.paint(g); + Rectangle rootClip = SwingUtilities.convertRectangle(this, r, root); + if (root instanceof JComponent) + ((JComponent) root).paintImmediately2(rootClip); + else + root.repaint(rootClip.x, rootClip.y, rootClip.width, rootClip.height); + } + + /** + * Performs the actual work of paintImmediatly on the repaint root. + * + * @param r the area to be repainted + */ + void paintImmediately2(Rectangle r) + { + RepaintManager rm = RepaintManager.currentManager(this); + Graphics g = getGraphics(); + g.setClip(r.x, r.y, r.width, r.height); + if (rm.isDoubleBufferingEnabled() && isDoubleBuffered()) + paintDoubleBuffered(g); + else + paintSimple(g); g.dispose(); } /** + * Performs double buffered repainting. + * + * @param g the graphics context to paint to + */ + void paintDoubleBuffered(Graphics g) + { + + Rectangle r = g.getClipBounds(); + if (r == null) + r = new Rectangle(0, 0, getWidth(), getHeight()); + RepaintManager rm = RepaintManager.currentManager(this); + + // Paint on the offscreen buffer. + Image buffer = rm.getOffscreenBuffer(this, getWidth(), getHeight()); + Graphics g2 = buffer.getGraphics(); + g2 = getComponentGraphics(g2); + g2.setClip(r.x, r.y, r.width, r.height); + isPaintingDoubleBuffered = true; + paint(g2); + isPaintingDoubleBuffered = false; + g2.dispose(); + + // Paint the buffer contents on screen. + g.drawImage(buffer, 0, 0, this); + } + + /** + * Performs normal painting without double buffering. + * + * @param g the graphics context to use + */ + void paintSimple(Graphics g) + { + Graphics g2 = getComponentGraphics(g); + paint(g2); + } + + /** * Return a string representation for this component, for use in * debugging. * @@ -1685,7 +2061,11 @@ public abstract class JComponent extends Container implements Serializable break; case WHEN_IN_FOCUSED_WINDOW: - inputMap_whenInFocusedWindow = map; + 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: @@ -1711,7 +2091,7 @@ public abstract class JComponent extends Container implements Serializable case WHEN_IN_FOCUSED_WINDOW: if (inputMap_whenInFocusedWindow == null) - inputMap_whenInFocusedWindow = new InputMap(); + inputMap_whenInFocusedWindow = new ComponentInputMap(this); return inputMap_whenInFocusedWindow; case UNDEFINED_CONDITION: @@ -1797,6 +2177,7 @@ public abstract class JComponent extends Container implements Serializable */ protected void processComponentKeyEvent(KeyEvent e) { + // This method does nothing, it is meant to be overridden by subclasses. } /** @@ -1825,40 +2206,56 @@ public abstract class JComponent extends Container implements Serializable // 4. The WHEN_IN_FOCUSED_WINDOW maps of all the enabled components in // the focused window are searched. - if (processKeyBinding(KeyStroke.getKeyStrokeForEvent(e), - e, WHEN_FOCUSED, e.getID() == KeyEvent.KEY_PRESSED)) - // This is step 1 from above comment. - e.consume(); - else if (processKeyBinding(KeyStroke.getKeyStrokeForEvent(e), - e, WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, - e.getID() == KeyEvent.KEY_PRESSED)) - // This is step 2 from above comment. - e.consume(); - else + KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(e); + boolean pressed = e.getID() == KeyEvent.KEY_PRESSED; + + if (processKeyBinding(keyStroke, e, WHEN_FOCUSED, pressed)) { - // This is step 3 from above comment. - Container current = this; - while ((current = current.getParent()) instanceof JComponent) + // 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)) { - if (((JComponent)current).processKeyBinding - (KeyStroke.getKeyStrokeForEvent(e), e, - WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, - e.getID() == KeyEvent.KEY_PRESSED)) - { - e.consume(); - break; - } - if (current instanceof Window || current instanceof Applet - || current instanceof JInternalFrame) - break; - } - if (e.isConsumed()) - return; + 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; - // This is step 4 from above comment. - // FIXME: Implement. Note, should use ComponentInputMaps rather - // than walking the entire containment hierarchy. + // 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, @@ -1975,8 +2372,19 @@ public abstract class JComponent extends Container implements Serializable */ public void revalidate() { - invalidate(); - RepaintManager.currentManager(this).addInvalidComponent(this); + if (! EventQueue.isDispatchThread()) + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + revalidate(); + } + }); + else + { + invalidate(); + RepaintManager.currentManager(this).addInvalidComponent(this); + } } /** @@ -1999,7 +2407,12 @@ public abstract class JComponent extends Container implements Serializable */ public void setAlignmentX(float a) { - alignmentX = a; + if (a < 0.0F) + alignmentX = 0.0F; + else if (a > 1.0) + alignmentX = 1.0F; + else + alignmentX = a; } /** @@ -2009,7 +2422,12 @@ public abstract class JComponent extends Container implements Serializable */ public void setAlignmentY(float a) { - alignmentY = a; + if (a < 0.0F) + alignmentY = 0.0F; + else if (a > 1.0) + alignmentY = 1.0F; + else + alignmentY = a; } /** @@ -2049,9 +2467,11 @@ public abstract class JComponent extends Container implements Serializable */ public void setEnabled(boolean enable) { - boolean oldEnabled = isEnabled(); + if (enable == isEnabled()) + return; super.setEnabled(enable); - firePropertyChange("enabled", oldEnabled, enable); + firePropertyChange("enabled", !enable, enable); + repaint(); } /** @@ -2061,7 +2481,11 @@ public abstract class JComponent extends Container implements Serializable */ public void setFont(Font f) { + if (f == getFont()) + return; super.setFont(f); + revalidate(); + repaint(); } /** @@ -2071,7 +2495,10 @@ public abstract class JComponent extends Container implements Serializable */ public void setBackground(Color bg) { + if (bg == getBackground()) + return; super.setBackground(bg); + repaint(); } /** @@ -2081,42 +2508,51 @@ public abstract class JComponent extends Container implements Serializable */ public void setForeground(Color fg) { + if (fg == getForeground()) + return; super.setForeground(fg); + repaint(); } /** - * Set the value of the {@link #maximumSize} property. + * Set the value of the {@link #maximumSize} property. The passed value is + * copied, the later direct changes on the argument have no effect on the + * property value. * * @param max The new value of the property */ public void setMaximumSize(Dimension max) { Dimension oldMaximumSize = maximumSize; - maximumSize = max; + maximumSize = new Dimension(max); firePropertyChange("maximumSize", oldMaximumSize, maximumSize); } /** - * Set the value of the {@link #minimumSize} property. + * Set the value of the {@link #minimumSize} property. The passed value is + * copied, the later direct changes on the argument have no effect on the + * property value. * * @param min The new value of the property */ public void setMinimumSize(Dimension min) { Dimension oldMinimumSize = minimumSize; - minimumSize = min; + minimumSize = new Dimension(min); firePropertyChange("minimumSize", oldMinimumSize, minimumSize); } /** - * Set the value of the {@link #preferredSize} property. + * Set the value of the {@link #preferredSize} property. The passed value is + * copied, the later direct changes on the argument have no effect on the + * property value. * * @param pref The new value of the property */ public void setPreferredSize(Dimension pref) { Dimension oldPreferredSize = preferredSize; - preferredSize = pref; + preferredSize = new Dimension(pref); firePropertyChange("preferredSize", oldPreferredSize, preferredSize); } @@ -2131,6 +2567,7 @@ public abstract class JComponent extends Container implements Serializable */ public void setNextFocusableComponent(Component aComponent) { + // TODO: Implement this properly. } /** @@ -2191,11 +2628,29 @@ public abstract class JComponent extends Container implements Serializable /** * 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(); } /** @@ -2248,7 +2703,8 @@ public abstract class JComponent extends Container implements Serializable ui.installUI(this); firePropertyChange("UI", oldUI, newUI); - + revalidate(); + repaint(); } /** @@ -2427,47 +2883,22 @@ public abstract class JComponent extends Container implements Serializable */ 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(); - // let parents inherit the keybord mapping - InputMap input = getInputMap(); - ActionMap actions = getActionMap(); - - Container parent = getParent(); - while ((parent != null) && (parent instanceof JComponent)) - { - JComponent jParent = (JComponent) parent; - InputMap parentInput = jParent.getInputMap(); - ActionMap parentAction = jParent.getActionMap(); - - KeyStroke[] ikeys = input.keys(); - for (int i = 0; i < ikeys.length; i++) - { - Object o = input.get(ikeys[i]); - parentInput.put(ikeys[i], o); - } - - Object[] akeys = actions.keys(); - for (int i = 0; i < akeys.length; i++) - { - Action a = actions.get(akeys[i]); - parentAction.put(akeys[i], a); - } - - parent = jParent.getParent(); - } - - // notify ancestor listeners - AncestorListener[] ls = getAncestorListeners(); - AncestorEvent ev = new AncestorEvent(this, AncestorEvent.ANCESTOR_ADDED, - this, parent); - for (int i = 0; i < ls.length; i++) - { - ls[i].ancestorAdded(ev); - } + // Notify AncestorListeners. + fireAncestorEvent(this, AncestorEvent.ANCESTOR_ADDED); // fire property change event for 'ancestor' - firePropertyChange("ancestor", null, parent); + firePropertyChange("ancestor", null, getParent()); } /** @@ -2490,43 +2921,14 @@ public abstract class JComponent extends Container implements Serializable { super.removeNotify(); - // let parents inherit the keybord mapping - InputMap input = getInputMap(); - ActionMap actions = getActionMap(); - - Container parent = getParent(); - while ((parent != null) && (parent instanceof JComponent)) - { - JComponent jParent = (JComponent) parent; - InputMap parentInput = jParent.getInputMap(); - ActionMap parentAction = jParent.getActionMap(); - - KeyStroke[] ikeys = input.allKeys(); - for (int i = 0; i < ikeys.length; i++) - { - parentInput.remove(ikeys[i]); - } - - Object[] akeys = actions.allKeys(); - for (int i = 0; i < akeys.length; i++) - { - parentAction.remove(akeys[i]); - } - - parent = jParent.getParent(); - } - - // notify ancestor listeners - AncestorListener[] ls = getAncestorListeners(); - AncestorEvent ev = new AncestorEvent(this, AncestorEvent.ANCESTOR_ADDED, - this, parent); - for (int i = 0; i < ls.length; i++) - { - ls[i].ancestorAdded(ev); - } + // FIXME: remove the WHEN_IN_FOCUSED_WINDOW bindings from the + // KeyboardManager + + // Notify ancestor listeners. + fireAncestorEvent(this, AncestorEvent.ANCESTOR_REMOVED); // fire property change event for 'ancestor' - firePropertyChange("ancestor", parent, null); + firePropertyChange("ancestor", getParent(), null); } /** @@ -2733,6 +3135,215 @@ public abstract class JComponent extends Container implements Serializable */ 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. + Component[] children = getComponents(); + for (int i = 0; i < children.length; i++) + { + if (!(children[i] instanceof JComponent)) + continue; + JComponent jc = (JComponent) children[i]; + jc.fireAncestorEvent(ancestor, id); + } + } + + /** + * Finds a suitable paint root for painting this component. This method first + * checks if this component is overlapped using + * {@link #findOverlapFreeParent(Rectangle)}. The returned paint root is then + * feeded to {@link #findOpaqueParent(Component)} to find the nearest opaque + * component for this paint root. If no paint is necessary, then we return + * <code>null</code>. + * + * @param c the clip of this component + * + * @return the paint root or <code>null</code> if no painting is necessary + */ + private Component findPaintRoot(Rectangle c) + { + Component p = findOverlapFreeParent(c); + if (p == null) + return null; + Component root = findOpaqueParent(p); + return root; + } + + /** + * Scans the containment hierarchy upwards for components that overlap the + * this component in the specified clip. This method returns + * <code>this</code>, if no component overlaps this component. It returns + * <code>null</code> if another component completely covers this component + * in the specified clip (no repaint necessary). If another component partly + * overlaps this component in the specified clip, then the parent of this + * component is returned (this is the component that must be used as repaint + * root). For efficient lookup, the method + * {@link #isOptimizedDrawingEnabled()} is used. + * + * @param clip the clip of this component + * + * @return the paint root, or <code>null</code> if no paint is necessary + */ + private Component findOverlapFreeParent(Rectangle clip) + { + Rectangle currentClip = clip; + Component found = this; + Container parent = this; + while (parent != null && !(parent instanceof Window)) + { + Container newParent = parent.getParent(); + if (newParent == null) + break; + // If the parent is optimizedDrawingEnabled, then its children are + // tiled and cannot have an overlapping child. Go directly to next + // parent. + if (newParent instanceof JComponent + && ((JComponent) newParent).isOptimizedDrawingEnabled()) + { + parent = newParent; + continue; + } + + // First we must check if the new parent itself somehow clips the + // target rectangle. This can happen in JViewports. + Rectangle parRect = new Rectangle(0, 0, newParent.getWidth(), + newParent.getHeight()); + Rectangle target = SwingUtilities.convertRectangle(found, + currentClip, + newParent); + if (target.contains(parRect) || target.intersects(parRect)) + { + found = newParent; + currentClip = target; + parent = newParent; + continue; + } + + // Otherwise we must check if one of the children of this parent + // overlaps with the current component. + Component[] children = newParent.getComponents(); + // This flag is used to skip components that are 'below' the component + // in question. + boolean skip = true; + for (int i = children.length - 1; i >= 0; i--) + { + if (children[i] == parent) + skip = false; + if (skip) + continue; + Component c = children[i]; + Rectangle compBounds = c.getBounds(); + // If the component completely overlaps the clip in question, we + // don't need to repaint. Return null. + if (compBounds.contains(target)) + return null; + if (compBounds.intersects(target)) + { + // We found a parent whose children overlap with our current + // component. Make this the current component. + found = newParent; + currentClip = target; + break; + } + } + parent = newParent; + } + return found; + } + + /** + * Finds the nearest component to <code>c</code> (upwards in the containment + * hierarchy), that is opaque. If <code>c</code> itself is opaque, + * this returns <code>c</code> itself. + * + * @param c the start component for the search + * @return the nearest component to <code>c</code> (upwards in the containment + * hierarchy), that is opaque; If <code>c</code> itself is opaque, + * this returns <code>c</code> itself + */ + private Component findOpaqueParent(Component c) + { + Component found = c; + while (true) + { + if ((found instanceof JComponent) && ((JComponent) found).isOpaque()) + break; + else if (!(found instanceof JComponent)) + break; + Container p = found.getParent(); + if (p == null) + break; + else + found = p; + } + return found; + } + + /** + * This is the method that gets called when the WHEN_IN_FOCUSED_WINDOW map + * is changed. + * + * @param changed the JComponent associated with the WHEN_IN_FOCUSED_WINDOW + * map + */ + void updateComponentInputMap(ComponentInputMap changed) + { + // Since you can change a component's input map via + // setInputMap, we have to check if <code>changed</code> + // is still in our WHEN_IN_FOCUSED_WINDOW map hierarchy + InputMap curr = getInputMap(WHEN_IN_FOCUSED_WINDOW); + while (curr != null && curr != changed) + curr = curr.getParent(); + + // If curr is null then changed is not in the hierarchy + if (curr == null) + return; + + // Now we have to update the keyboard manager's hashtable + KeyboardManager km = KeyboardManager.getManager(); + + // This is a poor strategy, should be improved. We currently + // delete all the old bindings for the component and then register + // the current bindings. + km.clearBindingsForComp(changed.getComponent()); + km.registerEntireMap((ComponentInputMap) + getInputMap(WHEN_IN_FOCUSED_WINDOW)); } } |