diff --git a/src/share/classes/java/applet/Applet.java b/src/share/classes/java/applet/Applet.java index 0e2bcb87b566dd5861b1aa50e8bea736d7f08733..b12e6fb308174793c50826a3f912cd1c185b4662 100644 --- a/src/share/classes/java/applet/Applet.java +++ b/src/share/classes/java/applet/Applet.java @@ -229,6 +229,21 @@ public class Applet extends Panel { resize(d.width, d.height); } + /** + * Indicates if this container is a validate root. + *
+ * {@code Applet} objects are the validate roots, and, therefore, they
+ * override this method to return {@code true}.
+ *
+ * @return {@code true}
+ * @since 1.7
+ * @see java.awt.Container#isValidateRoot
+ */
+ @Override
+ public boolean isValidateRoot() {
+ return true;
+ }
+
/**
* Requests that the argument string be displayed in the
* "status window". Many browsers and applet viewers
diff --git a/src/share/classes/java/awt/Component.java b/src/share/classes/java/awt/Component.java
index 42093d16c9c5023d59858087447b32a323e273f4..6a942e9ec5dd8adadcd6afa202cdd7564c0b0aa4 100644
--- a/src/share/classes/java/awt/Component.java
+++ b/src/share/classes/java/awt/Component.java
@@ -2764,8 +2764,11 @@ public abstract class Component implements ImageObserver, MenuContainer,
}
/**
- * Ensures that this component has a valid layout. This method is
- * primarily intended to operate on instances of Container
.
+ * Validates this component.
+ *
+ * The meaning of the term validating is defined by the ancestors of + * this class. See {@link Container#validate} for more details. + * * @see #invalidate * @see #doLayout() * @see LayoutManager @@ -2794,12 +2797,24 @@ public abstract class Component implements ImageObserver, MenuContainer, } /** - * Invalidates this component. This component and all parents - * above it are marked as needing to be laid out. This method can - * be called often, so it needs to execute quickly. + * Invalidates this component and its ancestors. + *
+ * All the ancestors of this component up to the nearest validate root are + * marked invalid also. If there is no a validate root container for this + * component, all of its ancestors up to the root of the hierarchy are + * marked invalid as well. Marking a container invalid indicates + * that the container needs to be laid out. + *
+ * This method is called automatically when any layout-related information + * changes (e.g. setting the bounds of the component, or adding the + * component to a container). + *
+ * This method might be called often, so it should work fast. + * * @see #validate * @see #doLayout * @see LayoutManager + * @see java.awt.Container#isValidateRoot * @since JDK1.0 */ public void invalidate() { @@ -2818,9 +2833,18 @@ public abstract class Component implements ImageObserver, MenuContainer, if (!isMaximumSizeSet()) { maxSize = null; } - if (parent != null) { - parent.invalidateIfValid(); - } + invalidateParent(); + } + } + + /** + * Invalidates the parent of this component if any. + * + * This method MUST BE invoked under the TreeLock. + */ + void invalidateParent() { + if (parent != null) { + parent.invalidateIfValid(); } } diff --git a/src/share/classes/java/awt/Container.java b/src/share/classes/java/awt/Container.java index 706cffa89dc05b320adcf89635b8ae506e4bbc42..84334c9973e789f639b7f3368e9bb50ae5321333 100644 --- a/src/share/classes/java/awt/Container.java +++ b/src/share/classes/java/awt/Container.java @@ -1492,20 +1492,59 @@ public class Container extends Component { } /** - * Invalidates the container. The container and all parents - * above it are marked as needing to be laid out. This method can - * be called often, so it needs to execute quickly. + * Indicates if this container is a validate root. + *
+ * Layout-related changes, such as bounds of the validate root descendants, + * do not affect the layout of the validate root parent. This peculiarity + * enables the {@code invalidate()} method to stop invalidating the + * component hierarchy when the method encounters a validate root. + *
+ * If a component hierarchy contains validate roots, the {@code validate()} + * method must be invoked on the validate root of a previously invalidated + * component, rather than on the top-level container (such as a {@code + * Frame} object) to restore the validity of the hierarchy later. + *
+ * The {@code Window} class and the {@code Applet} class are the validate + * roots in AWT. Swing introduces more validate roots. * - *
If the {@code LayoutManager} installed on this container is - * an instance of {@code LayoutManager2}, then - * {@link LayoutManager2#invalidateLayout(Container)} is invoked on - * it supplying this {@code Container} as the argument. + * @return whether this container is a validate root + * @see #invalidate + * @see java.awt.Component#invalidate + * @see javax.swing.JComponent#isValidateRoot + * @see javax.swing.JComponent#revalidate + * @since 1.7 + */ + public boolean isValidateRoot() { + return false; + } + + /** + * Invalidates the parent of the container unless the container + * is a validate root. + */ + @Override + void invalidateParent() { + if (!isValidateRoot()) { + super.invalidateParent(); + } + } + + /** + * Invalidates the container. + *
+ * If the {@code LayoutManager} installed on this container is an instance + * of the {@code LayoutManager2} interface, then + * the {@link LayoutManager2#invalidateLayout(Container)} method is invoked + * on it supplying this {@code Container} as the argument. + *
+ * Afterwards this method marks this container invalid, and invalidates its + * ancestors. See the {@link Component#invalidate} method for more details. * * @see #validate * @see #layout - * @see LayoutManager - * @see LayoutManager2#invalidateLayout(Container) + * @see LayoutManager2 */ + @Override public void invalidate() { LayoutManager layoutMgr = this.layoutMgr; if (layoutMgr instanceof LayoutManager2) { @@ -1518,35 +1557,39 @@ public class Container extends Component { /** * Validates this container and all of its subcomponents. *
- * The validate
method is used to cause a container
- * to lay out its subcomponents again. It should be invoked when
- * this container's subcomponents are modified (added to or
- * removed from the container, or layout-related information
- * changed) after the container has been displayed.
- *
- *
If this {@code Container} is not valid, this method invokes + * Validating a container means laying out its subcomponents. + * Layout-related changes, such as setting the bounds of a component, or + * adding a component to the container, invalidate the container + * automatically. Note that the ancestors of the container may be + * invalidated also (see {@link Component#invalidate} for details.) + * Therefore, to restore the validity of the hierarchy, the {@code + * validate()} method should be invoked on a validate root of an + * invalidated component, or on the top-most container if the hierarchy + * does not contain validate roots. + *
+ * Validating the container may be a quite time-consuming operation. For + * performance reasons a developer may postpone the validation of the + * hierarchy till a set of layout-related operations completes, e.g. after + * adding all the children to the container. + *
+ * If this {@code Container} is not valid, this method invokes * the {@code validateTree} method and marks this {@code Container} * as valid. Otherwise, no action is performed. - *
- * Note that the {@code invalidate()} method may invalidate not only the - * component it is called upon, but also the parents of the component. - * Therefore, to restore the validity of the hierarchy, the {@code - * validate()} method must be invoked on the top-most invalid container of - * the hierarchy. For performance reasons a developer may postpone the - * validation of the hierarchy till a bunch of layout-related operations - * completes, e.g. after adding all the children to the container. * * @see #add(java.awt.Component) * @see #invalidate + * @see Container#isValidateRoot * @see javax.swing.JComponent#revalidate() * @see #validateTree */ public void validate() { /* Avoid grabbing lock unless really necessary. */ - if (!isValid()) { + if (!isValid() || descendUnconditionallyWhenValidating) { boolean updateCur = false; synchronized (getTreeLock()) { - if (!isValid() && peer != null) { + if ((!isValid() || descendUnconditionallyWhenValidating) + && peer != null) + { ContainerPeer p = null; if (peer instanceof ContainerPeer) { p = (ContainerPeer) peer; @@ -1557,7 +1600,11 @@ public class Container extends Component { validateTree(); if (p != null) { p.endValidate(); - updateCur = isVisible(); + // Avoid updating cursor if this is an internal call. + // See validateUnconditionally() for details. + if (!descendUnconditionallyWhenValidating) { + updateCur = isVisible(); + } } } } @@ -1567,6 +1614,39 @@ public class Container extends Component { } } + /** + * Indicates whether valid containers should also traverse their + * children and call the validateTree() method on them. + * + * Synchronization: TreeLock. + * + * The field is allowed to be static as long as the TreeLock itself is + * static. + * + * @see #validateUnconditionally() + */ + private static boolean descendUnconditionallyWhenValidating = false; + + /** + * Unconditionally validate the component hierarchy. + */ + final void validateUnconditionally() { + boolean updateCur = false; + synchronized (getTreeLock()) { + descendUnconditionallyWhenValidating = true; + + validate(); + if (peer instanceof ContainerPeer) { + updateCur = isVisible(); + } + + descendUnconditionallyWhenValidating = false; + } + if (updateCur) { + updateCursorImmediately(); + } + } + /** * Recursively descends the container tree and recomputes the * layout for any subtrees marked as needing it (those marked as @@ -1578,16 +1658,20 @@ public class Container extends Component { */ protected void validateTree() { checkTreeLock(); - if (!isValid()) { + if (!isValid() || descendUnconditionallyWhenValidating) { if (peer instanceof ContainerPeer) { ((ContainerPeer)peer).beginLayout(); } - doLayout(); + if (!isValid()) { + doLayout(); + } for (int i = 0; i < component.size(); i++) { Component comp = component.get(i); if ( (comp instanceof Container) && !(comp instanceof Window) - && !comp.isValid()) { + && (!comp.isValid() || + descendUnconditionallyWhenValidating)) + { ((Container)comp).validateTree(); } else { comp.validate(); diff --git a/src/share/classes/java/awt/Window.java b/src/share/classes/java/awt/Window.java index 8ea9ec821d8d5396faf3f54776edc99f79ff9d40..c039a717984842dbf5d3acc7a9f69f6b4b9a16a8 100644 --- a/src/share/classes/java/awt/Window.java +++ b/src/share/classes/java/awt/Window.java @@ -767,7 +767,7 @@ public class Window extends Container implements Accessible { isPacked = true; } - validate(); + validateUnconditionally(); } /** @@ -943,7 +943,7 @@ public class Window extends Container implements Accessible { if (peer == null) { addNotify(); } - validate(); + validateUnconditionally(); isInShow = true; if (visible) { @@ -2599,6 +2599,21 @@ public class Window extends Container implements Accessible { super.addPropertyChangeListener(propertyName, listener); } + /** + * Indicates if this container is a validate root. + *
+ * {@code Window} objects are the validate roots, and, therefore, they
+ * override this method to return {@code true}.
+ *
+ * @return {@code true}
+ * @since 1.7
+ * @see java.awt.Container#isValidateRoot
+ */
+ @Override
+ public boolean isValidateRoot() {
+ return true;
+ }
+
/**
* Dispatches an event to this window or one of its sub components.
* @param e the event
diff --git a/src/share/classes/javax/swing/JComponent.java b/src/share/classes/javax/swing/JComponent.java
index 6c97ebd5e7147bc3f9fcb99bb8f4b0c6292dd38c..37f7e7d47307da81e9aa15ba02f542780e345f1f 100644
--- a/src/share/classes/javax/swing/JComponent.java
+++ b/src/share/classes/javax/swing/JComponent.java
@@ -4878,7 +4878,9 @@ public abstract class JComponent extends Container implements Serializable,
* @see #revalidate
* @see java.awt.Component#invalidate
* @see java.awt.Container#validate
+ * @see java.awt.Container#isValidateRoot
*/
+ @Override
public boolean isValidateRoot() {
return false;
}
diff --git a/src/share/classes/javax/swing/JRootPane.java b/src/share/classes/javax/swing/JRootPane.java
index a210c3791c4e889e21ef2b2cd56ae4256b064f40..f6964f728ef73809f5c9cb91e2ad4d083b1e3c16 100644
--- a/src/share/classes/javax/swing/JRootPane.java
+++ b/src/share/classes/javax/swing/JRootPane.java
@@ -725,8 +725,10 @@ public class JRootPane extends JComponent implements Accessible {
* because both classes override isValidateRoot
to return true.
*
* @see JComponent#isValidateRoot
+ * @see java.awt.Container#isValidateRoot
* @return true
*/
+ @Override
public boolean isValidateRoot() {
return true;
}
diff --git a/src/share/classes/javax/swing/JScrollPane.java b/src/share/classes/javax/swing/JScrollPane.java
index b2be43cd15fc8fed68cff816399266e945f28828..b813d4fccdc0747857874c3dfc8df0f1757f0bab 100644
--- a/src/share/classes/javax/swing/JScrollPane.java
+++ b/src/share/classes/javax/swing/JScrollPane.java
@@ -453,10 +453,12 @@ public class JScrollPane extends JComponent implements ScrollPaneConstants, Acce
* @see java.awt.Container#validate
* @see JComponent#revalidate
* @see JComponent#isValidateRoot
+ * @see java.awt.Container#isValidateRoot
*
* @beaninfo
* hidden: true
*/
+ @Override
public boolean isValidateRoot() {
return true;
}
diff --git a/src/share/classes/javax/swing/JSplitPane.java b/src/share/classes/javax/swing/JSplitPane.java
index b5d8ec7d2b513997df020fb823acf83e2c2c85f4..f3baff66492b2d23590f6067b118e72403023dce 100644
--- a/src/share/classes/javax/swing/JSplitPane.java
+++ b/src/share/classes/javax/swing/JSplitPane.java
@@ -947,10 +947,12 @@ public class JSplitPane extends JComponent implements Accessible
*
* @return true
* @see JComponent#revalidate
+ * @see java.awt.Container#isValidateRoot
*
* @beaninfo
* hidden: true
*/
+ @Override
public boolean isValidateRoot() {
return true;
}
diff --git a/src/share/classes/javax/swing/JTextField.java b/src/share/classes/javax/swing/JTextField.java
index de408914d2222c156ba4530bda43237b00b3f4af..12fc0a70c4f30a6f196acd35be09ae9f205dbbbf 100644
--- a/src/share/classes/javax/swing/JTextField.java
+++ b/src/share/classes/javax/swing/JTextField.java
@@ -288,7 +288,9 @@ public class JTextField extends JTextComponent implements SwingConstants {
*
* @see JComponent#revalidate
* @see JComponent#isValidateRoot
+ * @see java.awt.Container#isValidateRoot
*/
+ @Override
public boolean isValidateRoot() {
return SwingUtilities2.getViewport(this) == null;
}
diff --git a/src/share/classes/javax/swing/JViewport.java b/src/share/classes/javax/swing/JViewport.java
index 734e80a05570d03e004aad8a37ee557811b592d0..5073a891af4e76db0ae31d3b532f116d483e65d9 100644
--- a/src/share/classes/javax/swing/JViewport.java
+++ b/src/share/classes/javax/swing/JViewport.java
@@ -469,49 +469,12 @@ public class JViewport extends JComponent implements Accessible
* is the synchronous version of a revalidate
.
*/
private void validateView() {
- Component validateRoot = null;
+ Component validateRoot = SwingUtilities.getValidateRoot(this, false);
- /* Find the first JComponent ancestor of this component whose
- * isValidateRoot() method returns true.
- */
- for(Component c = this; c != null; c = c.getParent()) {
- if ((c instanceof CellRendererPane) || (c.getPeer() == null)) {
- return;
- }
- if ((c instanceof JComponent) &&
- (((JComponent)c).isValidateRoot())) {
- validateRoot = c;
- break;
- }
- }
-
- // If no validateRoot, nothing to validate from.
if (validateRoot == null) {
return;
}
- // Make sure all ancestors are visible.
- Component root = null;
-
- for(Component c = validateRoot; c != null; c = c.getParent()) {
- // We don't check isVisible here, otherwise if the component
- // is contained in something like a JTabbedPane when the
- // component is made visible again it won't have scrolled
- // to the correct location.
- if (c.getPeer() == null) {
- return;
- }
- if ((c instanceof Window) || (c instanceof Applet)) {
- root = c;
- break;
- }
- }
-
- // Make sure there is a Window ancestor.
- if (root == null) {
- return;
- }
-
// Validate the root.
validateRoot.validate();
diff --git a/src/share/classes/javax/swing/RepaintManager.java b/src/share/classes/javax/swing/RepaintManager.java
index a988b99fbd35d6a0b45d061a28410bac51a9f8a7..c8607d2e4b99042daaa42dda75cf00c9f8900b76 100644
--- a/src/share/classes/javax/swing/RepaintManager.java
+++ b/src/share/classes/javax/swing/RepaintManager.java
@@ -310,47 +310,13 @@ public class RepaintManager
delegate.addInvalidComponent(invalidComponent);
return;
}
- Component validateRoot = null;
+ Component validateRoot =
+ SwingUtilities.getValidateRoot(invalidComponent, true);
- /* Find the first JComponent ancestor of this component whose
- * isValidateRoot() method returns true.
- */
- for(Component c = invalidComponent; c != null; c = c.getParent()) {
- if ((c instanceof CellRendererPane) || (c.getPeer() == null)) {
- return;
- }
- if ((c instanceof JComponent) && (((JComponent)c).isValidateRoot())) {
- validateRoot = c;
- break;
- }
- }
-
- /* There's no validateRoot to apply validate to, so we're done.
- */
if (validateRoot == null) {
return;
}
- /* If the validateRoot and all of its ancestors aren't visible
- * then we don't do anything. While we're walking up the tree
- * we find the root Window or Applet.
- */
- Component root = null;
-
- for(Component c = validateRoot; c != null; c = c.getParent()) {
- if (!c.isVisible() || (c.getPeer() == null)) {
- return;
- }
- if ((c instanceof Window) || (c instanceof Applet)) {
- root = c;
- break;
- }
- }
-
- if (root == null) {
- return;
- }
-
/* Lazily create the invalidateComponents vector and add the
* validateRoot if it's not there already. If this validateRoot
* is already in the vector, we're done.
diff --git a/src/share/classes/javax/swing/SwingUtilities.java b/src/share/classes/javax/swing/SwingUtilities.java
index d7a26a0dd729555011a93a6ead80acf5c54f6b87..54ac2f55680621d1b5fa84fda26f3d207112b01d 100644
--- a/src/share/classes/javax/swing/SwingUtilities.java
+++ b/src/share/classes/javax/swing/SwingUtilities.java
@@ -1967,4 +1967,54 @@ public class SwingUtilities implements SwingConstants
SwingUtilities.updateComponentTreeUI(component);
}
}
+
+ /**
+ * Retrieves the validate root of a given container.
+ *
+ * If the container is contained within a {@code CellRendererPane}, this
+ * method returns {@code null} due to the synthetic nature of the {@code
+ * CellRendererPane}.
+ *
+ * The component hierarchy must be displayable up to the toplevel component + * (either a {@code Frame} or an {@code Applet} object.) Otherwise this + * method returns {@code null}. + *
+ * If the {@code visibleOnly} argument is {@code true}, the found validate + * root and all its parents up to the toplevel component must also be + * visible. Otherwise this method returns {@code null}. + * + * @return the validate root of the given container or null + * @see java.awt.Component#isDisplayable() + * @see java.awt.Component#isVisible() + * @since 1.7 + */ + static Container getValidateRoot(Container c, boolean visibleOnly) { + Container root = null; + + for (; c != null; c = c.getParent()) + { + if (!c.isDisplayable() || c instanceof CellRendererPane) { + return null; + } + if (c.isValidateRoot()) { + root = c; + break; + } + } + + if (root == null) { + return null; + } + + for (; c != null; c = c.getParent()) { + if (!c.isDisplayable() || (visibleOnly && !c.isVisible())) { + return null; + } + if (c instanceof Window || c instanceof Applet) { + return root; + } + } + + return null; + } } diff --git a/test/java/awt/Container/ValidateRoot/InvalidateMustRespectValidateRoots.java b/test/java/awt/Container/ValidateRoot/InvalidateMustRespectValidateRoots.java new file mode 100644 index 0000000000000000000000000000000000000000..7bf34012dd9155b0257c5eeda5c865cb5292a8da --- /dev/null +++ b/test/java/awt/Container/ValidateRoot/InvalidateMustRespectValidateRoots.java @@ -0,0 +1,114 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + @test + @bug 6852592 + @summary invalidate() must stop when it encounters a validate root + @author anthony.petrov@sun.com + @run main InvalidateMustRespectValidateRoots +*/ + +import javax.swing.*; +import java.awt.event.*; + +public class InvalidateMustRespectValidateRoots { + private static volatile JRootPane rootPane; + + public static void main(String args[]) throws Exception { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + // The JRootPane is a validate root. We'll check if + // invalidate() stops on the root pane, or goes further + // up to the frame. + JFrame frame = new JFrame(); + final JButton button = new JButton(); + + frame.add(button); + + // To enable running the test manually: use the Ctrl-Shift-F1 + // to print the component hierarchy to the console + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent ev) { + if (button.isValid()) { + button.invalidate(); + } else { + button.revalidate(); + } + } + }); + + rootPane = frame.getRootPane(); + + // Now the component hierarchy looks like: + // frame + // --> rootPane + // --> layered pane + // --> content pane + // --> button + + // Make all components valid first via showing the frame + // We have to make the frame visible. Otherwise revalidate() is + // useless (see RepaintManager.addInvalidComponent()). + frame.pack(); // To enable running this test manually + frame.setVisible(true); + + if (!frame.isValid()) { + throw new RuntimeException( + "setVisible(true) failed to validate the frame"); + } + + // Now invalidate the button + button.invalidate(); + + // Check if the 'valid' status is what we expect it to be + if (rootPane.isValid()) { + throw new RuntimeException( + "invalidate() failed to invalidate the root pane"); + } + + if (!frame.isValid()) { + throw new RuntimeException( + "invalidate() invalidated the frame"); + } + + // Now validate the hierarchy again + button.revalidate(); + + // Now let the validation happen on the EDT + } + }); + + Thread.sleep(1000); + + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + // Check if the root pane finally became valid + if (!rootPane.isValid()) { + throw new RuntimeException( + "revalidate() failed to validate the hierarchy"); + } + } + }); + } +}