diff options
Diffstat (limited to 'libjava/classpath/java/awt/Component.java')
-rw-r--r-- | libjava/classpath/java/awt/Component.java | 1039 |
1 files changed, 686 insertions, 353 deletions
diff --git a/libjava/classpath/java/awt/Component.java b/libjava/classpath/java/awt/Component.java index 3d3dcc319cc..44f277ac783 100644 --- a/libjava/classpath/java/awt/Component.java +++ b/libjava/classpath/java/awt/Component.java @@ -70,6 +70,7 @@ import java.awt.image.ImageProducer; import java.awt.image.VolatileImage; import java.awt.peer.ComponentPeer; import java.awt.peer.LightweightPeer; +import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.IOException; @@ -213,6 +214,12 @@ public abstract class Component */ static final Object treeLock = new String("AWT_TREE_LOCK"); + /** + * The default maximum size. + */ + private static final Dimension DEFAULT_MAX_SIZE + = new Dimension(Short.MAX_VALUE, Short.MAX_VALUE); + // Serialized fields from the serialization spec. /** @@ -427,6 +434,24 @@ public abstract class Component Dimension minSize; /** + * Flag indicating whether the minimum size for the component has been set + * by a call to {@link #setMinimumSize(Dimension)} with a non-null value. + */ + boolean minSizeSet; + + /** + * The maximum size for the component. + * @see #setMaximumSize(Dimension) + */ + Dimension maxSize; + + /** + * A flag indicating whether the maximum size for the component has been set + * by a call to {@link #setMaximumSize(Dimension)} with a non-null value. + */ + boolean maxSizeSet; + + /** * Cached information on the preferred size. Should have been transient. * * @serial ignore @@ -434,6 +459,12 @@ public abstract class Component Dimension prefSize; /** + * Flag indicating whether the preferred size for the component has been set + * by a call to {@link #setPreferredSize(Dimension)} with a non-null value. + */ + boolean prefSizeSet; + + /** * Set to true if an event is to be handled by this component, false if * it is to be passed up the hierarcy. * @@ -563,6 +594,17 @@ public abstract class Component transient BufferStrategy bufferStrategy; /** + * The number of hierarchy listeners of this container plus all of its + * children. This is needed for efficient handling of HierarchyEvents. + * These must be propagated to all child components with HierarchyListeners + * attached. To avoid traversal of the whole subtree, we keep track of + * the number of HierarchyListeners here and only walk the paths that + * actually have listeners. + */ + int numHierarchyListeners; + int numHierarchyBoundsListeners; + + /** * true if requestFocus was called on this component when its * top-level ancestor was not focusable. */ @@ -607,16 +649,19 @@ public abstract class Component } /** - * Sets the name of this component to the specified name. + * Sets the name of this component to the specified name (this is a bound + * property with the name 'name'). * - * @param name the new name of this component + * @param name the new name (<code>null</code> permitted). * @see #getName() * @since 1.1 */ public void setName(String name) { nameExplicitlySet = true; + String old = this.name; this.name = name; + firePropertyChange("name", old, name); } /** @@ -718,7 +763,9 @@ public abstract class Component */ public boolean isValid() { - return valid; + // Tests show that components are invalid as long as they are not showing, even after validate() + // has been called on them. + return peer != null && valid; } /** @@ -804,9 +851,17 @@ public abstract class Component */ public void enable() { - this.enabled = true; - if (peer != null) - peer.setEnabled (true); + if (! enabled) + { + // Need to lock the tree here, because the peers are involved. + synchronized (getTreeLock()) + { + enabled = true; + ComponentPeer p = peer; + if (p != null) + p.enable(); + } + } } /** @@ -831,9 +886,17 @@ public abstract class Component */ public void disable() { - this.enabled = false; - if (peer != null) - peer.setEnabled (false); + if (enabled) + { + // Need to lock the tree here, because the peers are involved. + synchronized (getTreeLock()) + { + enabled = false; + ComponentPeer p = peer; + if (p != null) + p.disable(); + } + } } /** @@ -898,16 +961,38 @@ public abstract class Component // and its children. if(!isVisible()) { - this.visible = true; - // Avoid NullPointerExceptions by creating a local reference. - ComponentPeer currentPeer=peer; - if (currentPeer != null) - currentPeer.show(); - - // The JDK repaints the component before invalidating the parent. - // So do we. - if (isShowing() && isLightweight()) - repaint(); + // Need to lock the tree here to avoid races and inconsistencies. + synchronized (getTreeLock()) + { + visible = true; + // Avoid NullPointerExceptions by creating a local reference. + ComponentPeer currentPeer=peer; + if (currentPeer != null) + { + currentPeer.show(); + + // Fire HierarchyEvent. + fireHierarchyEvent(HierarchyEvent.HIERARCHY_CHANGED, + this, parent, + HierarchyEvent.SHOWING_CHANGED); + + // The JDK repaints the component before invalidating the parent. + // So do we. + if (isLightweight()) + repaint(); + } + + // Only post an event if this component actually has a listener + // or has this event explicitly enabled. + if (componentListener != null + || (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0) + { + ComponentEvent ce = + new ComponentEvent(this,ComponentEvent.COMPONENT_SHOWN); + getToolkit().getSystemEventQueue().postEvent(ce); + } + } + // Invalidate the parent if we have one. The component itself must // not be invalidated. We also avoid NullPointerException with // a local reference here. @@ -915,9 +1000,6 @@ public abstract class Component if (currentParent != null) currentParent.invalidate(); - ComponentEvent ce = - new ComponentEvent(this,ComponentEvent.COMPONENT_SHOWN); - getToolkit().getSystemEventQueue().postEvent(ce); } } @@ -945,27 +1027,45 @@ public abstract class Component { if (isVisible()) { - // Avoid NullPointerExceptions by creating a local reference. - ComponentPeer currentPeer=peer; - if (currentPeer != null) - currentPeer.setVisible(false); - boolean wasShowing = isShowing(); - this.visible = false; - - // The JDK repaints the component before invalidating the parent. - // So do we. - if (wasShowing) - repaint(); - // Invalidate the parent if we have one. The component itself must + // Need to lock the tree here to avoid races and inconsistencies. + synchronized (getTreeLock()) + { + visible = false; + + // Avoid NullPointerExceptions by creating a local reference. + ComponentPeer currentPeer=peer; + if (currentPeer != null) + { + currentPeer.hide(); + + // Fire hierarchy event. + fireHierarchyEvent(HierarchyEvent.HIERARCHY_CHANGED, + this, parent, + HierarchyEvent.SHOWING_CHANGED); + // The JDK repaints the component before invalidating the + // parent. So do we. This only applies for lightweights. + if (peer instanceof LightweightPeer) + repaint(); + } + + // Only post an event if this component actually has a listener + // or has this event explicitly enabled. + if (componentListener != null + || (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0) + { + ComponentEvent ce = + new ComponentEvent(this,ComponentEvent.COMPONENT_HIDDEN); + getToolkit().getSystemEventQueue().postEvent(ce); + } + } + + // Invalidate the parent if we have one. The component itself need // not be invalidated. We also avoid NullPointerException with // a local reference here. Container currentParent = parent; if (currentParent != null) currentParent.invalidate(); - ComponentEvent ce = - new ComponentEvent(this,ComponentEvent.COMPONENT_HIDDEN); - getToolkit().getSystemEventQueue().postEvent(ce); } } @@ -1088,16 +1188,13 @@ public abstract class Component */ public void setFont(Font newFont) { - if((newFont != null && (font == null || !font.equals(newFont))) - || newFont == null) - { - Font oldFont = font; - font = newFont; - if (peer != null) - peer.setFont(font); - firePropertyChange("font", oldFont, newFont); - invalidate(); - } + Font oldFont = font; + font = newFont; + if (peer != null) + peer.setFont(font); + firePropertyChange("font", oldFont, newFont); + if (valid) + invalidate(); } /** @@ -1189,8 +1286,15 @@ public abstract class Component throw new IllegalComponentStateException("component " + getClass().getName() + " not showing"); - // We know peer != null here. - return peer.getLocationOnScreen(); + + // Need to lock the tree here. We get crazy races and explosions when + // the tree changes while we are trying to find the location of this + // component. + synchronized (getTreeLock()) + { + // We know peer != null here. + return peer.getLocationOnScreen(); + } } /** @@ -1384,53 +1488,98 @@ public abstract class Component */ public void reshape(int x, int y, int width, int height) { - int oldx = this.x; - int oldy = this.y; - int oldwidth = this.width; - int oldheight = this.height; + // We need to lock the tree here, otherwise we risk races and + // inconsistencies. + synchronized (getTreeLock()) + { + int oldx = this.x; + int oldy = this.y; + int oldwidth = this.width; + int oldheight = this.height; - if (this.x == x && this.y == y && this.width == width - && this.height == height) - return; + boolean resized = oldwidth != width || oldheight != height; + boolean moved = oldx != x || oldy != y; - invalidate(); - - this.x = x; - this.y = y; - this.width = width; - this.height = height; - if (peer != null) - peer.setBounds (x, y, width, height); - - // Erase old bounds and repaint new bounds for lightweights. - if (isLightweight() && isShowing()) - { - if (parent != null) + if (resized || moved) { - Rectangle oldBounds = new Rectangle(oldx, oldy, oldwidth, - oldheight); - Rectangle newBounds = new Rectangle(x, y, width, height); - Rectangle destroyed = oldBounds.union(newBounds); - if (!destroyed.isEmpty()) - parent.repaint(0, destroyed.x, destroyed.y, destroyed.width, - destroyed.height); + // Update the fields. + this.x = x; + this.y = y; + this.width = width; + this.height = height; + + if (peer != null) + { + peer.setBounds (x, y, width, height); + if (resized) + invalidate(); + if (parent != null && parent.valid) + parent.invalidate(); + } + + // Send some events to interested listeners. + notifyReshape(resized, moved); + + // Repaint this component and the parent if appropriate. + if (parent != null && peer instanceof LightweightPeer + && isShowing()) + { + // The parent repaints the area that we occupied before. + parent.repaint(oldx, oldy, oldwidth, oldheight); + // This component repaints the area that we occupy now. + repaint(); + } } } + } - // Only post event if this component is visible and has changed size. - if (isShowing () - && (oldx != x || oldy != y)) + private void notifyReshape(boolean resized, boolean moved) + { + // Only post an event if this component actually has a listener + // or has this event explicitly enabled. + if (componentListener != null + || (eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0) { - ComponentEvent ce = new ComponentEvent(this, - ComponentEvent.COMPONENT_MOVED); - getToolkit().getSystemEventQueue().postEvent(ce); + // Fire component event on this component. + if (moved) + { + ComponentEvent ce = new ComponentEvent(this, + ComponentEvent.COMPONENT_MOVED); + getToolkit().getSystemEventQueue().postEvent(ce); + } + if (resized) + { + ComponentEvent ce = new ComponentEvent(this, + ComponentEvent.COMPONENT_RESIZED); + getToolkit().getSystemEventQueue().postEvent(ce); + } } - if (isShowing () - && (oldwidth != width || oldheight != height)) + else { - ComponentEvent ce = new ComponentEvent(this, - ComponentEvent.COMPONENT_RESIZED); - getToolkit().getSystemEventQueue().postEvent(ce); + // Otherwise we might need to notify child components when this is + // a Container. + if (this instanceof Container) + { + Container cont = (Container) this; + if (resized) + { + for (int i = 0; i < cont.getComponentCount(); i++) + { + Component child = cont.getComponent(i); + child.fireHierarchyEvent(HierarchyEvent.ANCESTOR_RESIZED, + this, parent, 0); + } + } + if (moved) + { + for (int i = 0; i < cont.getComponentCount(); i++) + { + Component child = cont.getComponent(i); + child.fireHierarchyEvent(HierarchyEvent.ANCESTOR_MOVED, + this, parent, 0); + } + } + } } } @@ -1584,6 +1733,7 @@ public abstract class Component * * @return the component's preferred size * @see #getMinimumSize() + * @see #setPreferredSize(Dimension) * @see LayoutManager */ public Dimension getPreferredSize() @@ -1592,6 +1742,40 @@ public abstract class Component } /** + * Sets the preferred size that will be returned by + * {@link #getPreferredSize()} always, and sends a + * {@link PropertyChangeEvent} (with the property name 'preferredSize') to + * all registered listeners. + * + * @param size the preferred size (<code>null</code> permitted). + * + * @since 1.5 + * + * @see #getPreferredSize() + */ + public void setPreferredSize(Dimension size) + { + Dimension old = prefSizeSet ? prefSize : null; + prefSize = size; + prefSizeSet = (size != null); + firePropertyChange("preferredSize", old, size); + } + + /** + * Returns <code>true</code> if the current preferred size is not + * <code>null</code> and was set by a call to + * {@link #setPreferredSize(Dimension)}, otherwise returns <code>false</code>. + * + * @return A boolean. + * + * @since 1.5 + */ + public boolean isPreferredSizeSet() + { + return prefSizeSet; + } + + /** * Returns the component's preferred size. * * @return the component's preferred size @@ -1599,14 +1783,36 @@ public abstract class Component */ public Dimension preferredSize() { - if (prefSize == null) + // Create a new Dimension object, so that the application doesn't mess + // with the actual values. + return new Dimension(preferredSizeImpl()); + } + + /** + * The actual calculation is pulled out of preferredSize() so that + * we can call it from Container.preferredSize() and avoid creating a + * new intermediate Dimension object. + * + * @return the preferredSize of the component + */ + Dimension preferredSizeImpl() + { + Dimension size = prefSize; + // Try to use a cached value. + if (size == null || !(valid || prefSizeSet)) { - if (peer == null) - prefSize = minimumSize(); - else - prefSize = peer.getPreferredSize(); + // We need to lock here, because the calculation depends on the + // component structure not changing. + synchronized (getTreeLock()) + { + ComponentPeer p = peer; + if (p != null) + size = peer.preferredSize(); + else + size = minimumSizeImpl(); + } } - return prefSize; + return size; } /** @@ -1614,6 +1820,7 @@ public abstract class Component * * @return the component's minimum size * @see #getPreferredSize() + * @see #setMinimumSize(Dimension) * @see LayoutManager */ public Dimension getMinimumSize() @@ -1622,6 +1829,39 @@ public abstract class Component } /** + * Sets the minimum size that will be returned by {@link #getMinimumSize()} + * always, and sends a {@link PropertyChangeEvent} (with the property name + * 'minimumSize') to all registered listeners. + * + * @param size the minimum size (<code>null</code> permitted). + * + * @since 1.5 + * + * @see #getMinimumSize() + */ + public void setMinimumSize(Dimension size) + { + Dimension old = minSizeSet ? minSize : null; + minSize = size; + minSizeSet = (size != null); + firePropertyChange("minimumSize", old, size); + } + + /** + * Returns <code>true</code> if the current minimum size is not + * <code>null</code> and was set by a call to + * {@link #setMinimumSize(Dimension)}, otherwise returns <code>false</code>. + * + * @return A boolean. + * + * @since 1.5 + */ + public boolean isMinimumSizeSet() + { + return minSizeSet; + } + + /** * Returns the component's minimum size. * * @return the component's minimum size @@ -1629,10 +1869,36 @@ public abstract class Component */ public Dimension minimumSize() { - if (minSize == null) - minSize = (peer != null ? peer.getMinimumSize() - : new Dimension(width, height)); - return minSize; + // Create a new Dimension object, so that the application doesn't mess + // with the actual values. + return new Dimension(minimumSizeImpl()); + } + + /** + * The actual calculation is pulled out of minimumSize() so that + * we can call it from Container.preferredSize() and + * Component.preferredSizeImpl and avoid creating a + * new intermediate Dimension object. + * + * @return the minimum size of the component + */ + Dimension minimumSizeImpl() + { + Dimension size = minSize; + if (size == null || !(valid || minSizeSet)) + { + // We need to lock here, because the calculation depends on the + // component structure not changing. + synchronized (getTreeLock()) + { + ComponentPeer p = peer; + if (p != null) + size = peer.minimumSize(); + else + size = size(); + } + } + return size; } /** @@ -1640,15 +1906,66 @@ public abstract class Component * * @return the component's maximum size * @see #getMinimumSize() + * @see #setMaximumSize(Dimension) * @see #getPreferredSize() * @see LayoutManager */ public Dimension getMaximumSize() { - return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE); + return new Dimension(maximumSizeImpl()); + } + + /** + * This is pulled out from getMaximumSize(), so that we can access it + * from Container.getMaximumSize() without creating an additional + * intermediate Dimension object. + * + * @return the maximum size of the component + */ + Dimension maximumSizeImpl() + { + Dimension size; + if (maxSizeSet) + size = maxSize; + else + size = DEFAULT_MAX_SIZE; + return size; + } + + /** + * Sets the maximum size that will be returned by {@link #getMaximumSize()} + * always, and sends a {@link PropertyChangeEvent} (with the property name + * 'maximumSize') to all registered listeners. + * + * @param size the maximum size (<code>null</code> permitted). + * + * @since 1.5 + * + * @see #getMaximumSize() + */ + public void setMaximumSize(Dimension size) + { + Dimension old = maxSizeSet ? maxSize : null; + maxSize = size; + maxSizeSet = (size != null); + firePropertyChange("maximumSize", old, size); } /** + * Returns <code>true</code> if the current maximum size is not + * <code>null</code> and was set by a call to + * {@link #setMaximumSize(Dimension)}, otherwise returns <code>false</code>. + * + * @return A boolean. + * + * @since 1.5 + */ + public boolean isMaximumSizeSet() + { + return maxSizeSet; + } + + /** * Returns the preferred horizontal alignment of this component. The value * returned will be between {@link #LEFT_ALIGNMENT} and * {@link #RIGHT_ALIGNMENT}, inclusive. @@ -1716,11 +2033,25 @@ public abstract class Component */ public void invalidate() { - valid = false; - prefSize = null; - minSize = null; - if (parent != null && parent.isValid()) - parent.invalidate(); + // Need to lock here, to avoid races and other ugly stuff when doing + // layout or structure changes in other threads. + synchronized (getTreeLock()) + { + // Invalidate. + valid = false; + + // Throw away cached layout information. + if (! minSizeSet) + minSize = null; + if (! prefSizeSet) + prefSize = null; + if (! maxSizeSet) + maxSize = null; + + // Also invalidate the parent, if it hasn't already been invalidated. + if (parent != null && parent.isValid()) + parent.invalidate(); + } } /** @@ -1826,11 +2157,9 @@ public abstract class Component } /** - * Updates this component. This is called in response to - * <code>repaint</code>. This method fills the component with the - * background color, then sets the foreground color of the specified - * graphics context to the foreground color of this component and calls - * the <code>paint()</code> method. The coordinates of the graphics are + * Updates this component. This is called for heavyweight components in + * response to {@link #repaint()}. The default implementation simply forwards + * to {@link #paint(Graphics)}. The coordinates of the graphics are * relative to this component. Subclasses should call either * <code>super.update(g)</code> or <code>paint(g)</code>. * @@ -1838,27 +2167,17 @@ public abstract class Component * * @see #paint(Graphics) * @see #repaint() - * - * @specnote In contrast to what the spec says, tests show that the exact - * behaviour is to clear the background on lightweight and - * top-level components only. Heavyweight components are not - * affected by this method and only call paint(). */ public void update(Graphics g) { - // Tests show that the clearing of the background is only done in - // two cases: - // - If the component is lightweight (yes this is in contrast to the spec). - // or - // - If the component is a toplevel container. - if (isLightweight() || getParent() == null) - { - Rectangle clip = g.getClipBounds(); - if (clip == null) - g.clearRect(0, 0, width, height); - else - g.clearRect(clip.x, clip.y, clip.width, clip.height); - } + // Note 1: We used to clear the background here for lightweights and + // toplevel components. Tests show that this is not what the JDK does + // here. Note that there is some special handling and background + // clearing code in Container.update(Graphics). + + // Note 2 (for peer implementors): The JDK doesn't seem call update() for + // toplevel components, even when an UPDATE event is sent (as a result + // of repaint). paint(g); } @@ -1934,11 +2253,46 @@ public abstract class Component */ public void repaint(long tm, int x, int y, int width, int height) { - if (isShowing()) + // The repaint() call has previously been delegated to + // {@link ComponentPeer.repaint()}. Testing on the JDK using some + // dummy peers show that this methods is never called. I think it makes + // sense to actually perform the tasks below here, since it's pretty + // much peer independent anyway, and makes sure only heavyweights are + // bothered by this. + ComponentPeer p = peer; + + // Let the nearest heavyweight parent handle repainting for lightweight + // components. + // This goes up the hierarchy until we hit + // a heavyweight component that handles this and translates the + // rectangle while doing so. + + // We perform some boundary checking to restrict the paint + // region to this component. + int px = (x < 0 ? 0 : x); + int py = (y < 0 ? 0 : y); + int pw = width; + int ph = height; + Component par = this; + while (par != null && p instanceof LightweightPeer) + { + px += par.x; + py += par.y; + // We perform some boundary checking to restrict the paint + // region to this component. + pw = Math.min(pw, par.width); + ph = Math.min(ph, par.height); + par = par.parent; + p = par.peer; + } + + // Now send an UPDATE event to the heavyweight component that we've found. + if (par != null && par.isVisible() && p != null && pw > 0 && ph > 0) { - ComponentPeer p = peer; - if (p != null) - p.repaint(tm, x, y, width, height); + assert ! (p instanceof LightweightPeer); + PaintEvent pe = new PaintEvent(par, PaintEvent.UPDATE, + new Rectangle(px, py, pw, ph)); + getToolkit().getSystemEventQueue().postEvent(pe); } } @@ -1957,10 +2311,7 @@ public abstract class Component } /** - * Prints this component, including all sub-components. This method is - * provided so that printing can be done in a different manner from - * painting. However, the implementation in this class simply calls the - * <code>paintAll()</code> method. + * Prints this component, including all sub-components. * * @param g the graphics context of the print device * @@ -1968,7 +2319,9 @@ public abstract class Component */ public void printAll(Graphics g) { - paintAll(g); + if( peer != null ) + peer.print( g ); + paintAll( g ); } /** @@ -2318,6 +2671,17 @@ public abstract class Component } /** + * By default, no old mouse events should be ignored. + * This can be overridden by subclasses. + * + * @return false, no mouse events are ignored. + */ + static boolean ignoreOldMouseEvents() + { + return false; + } + + /** * AWT 1.0 event handler. * * This method simply calls handleEvent and returns the result. @@ -2449,6 +2813,14 @@ public abstract class Component hierarchyListener = AWTEventMulticaster.add(hierarchyListener, listener); if (hierarchyListener != null) enableEvents(AWTEvent.HIERARCHY_EVENT_MASK); + + // Need to lock the tree, otherwise we might end up inconsistent. + synchronized (getTreeLock()) + { + numHierarchyListeners++; + if (parent != null) + parent.updateHierarchyListenerCount(AWTEvent.HIERARCHY_EVENT_MASK, 1); + } } /** @@ -2464,6 +2836,15 @@ public abstract class Component public synchronized void removeHierarchyListener(HierarchyListener listener) { hierarchyListener = AWTEventMulticaster.remove(hierarchyListener, listener); + + // Need to lock the tree, otherwise we might end up inconsistent. + synchronized (getTreeLock()) + { + numHierarchyListeners--; + if (parent != null) + parent.updateHierarchyListenerCount(AWTEvent.HIERARCHY_EVENT_MASK, + -1); + } } /** @@ -2499,6 +2880,16 @@ public abstract class Component AWTEventMulticaster.add(hierarchyBoundsListener, listener); if (hierarchyBoundsListener != null) enableEvents(AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK); + + // Need to lock the tree, otherwise we might end up inconsistent. + synchronized (getTreeLock()) + { + numHierarchyBoundsListeners++; + if (parent != null) + parent.updateHierarchyListenerCount + (AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK, + 1); + } } /** @@ -2516,6 +2907,16 @@ public abstract class Component { hierarchyBoundsListener = AWTEventMulticaster.remove(hierarchyBoundsListener, listener); + + // Need to lock the tree, otherwise we might end up inconsistent. + synchronized (getTreeLock()) + { + numHierarchyBoundsListeners--; + if (parent != null) + parent.updateHierarchyListenerCount + (AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK, + -1); + } } /** @@ -2534,6 +2935,40 @@ public abstract class Component } /** + * Fires a HierarchyEvent or HierarchyChangeEvent on this component. + * + * @param id the event id + * @param changed the changed component + * @param parent the parent + * @param flags the event flags + */ + void fireHierarchyEvent(int id, Component changed, Container parent, + long flags) + { + boolean enabled = false; + switch (id) + { + case HierarchyEvent.HIERARCHY_CHANGED: + enabled = hierarchyListener != null + || (eventMask & AWTEvent.HIERARCHY_EVENT_MASK) != 0; + break; + case HierarchyEvent.ANCESTOR_MOVED: + case HierarchyEvent.ANCESTOR_RESIZED: + enabled = hierarchyBoundsListener != null + || (eventMask & AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK) != 0; + break; + default: + assert false : "Should not reach here"; + } + if (enabled) + { + HierarchyEvent ev = new HierarchyEvent(this, id, changed, parent, + flags); + dispatchEvent(ev); + } + } + + /** * Adds the specified listener to this component. This is harmless if the * listener is null, but if the listener has already been registered, it * will now be registered twice. @@ -3435,18 +3870,24 @@ public abstract class Component */ public void addNotify() { - if (peer == null) - peer = getToolkit().createComponent(this); - else if (parent != null && parent.isLightweight()) - new HeavyweightInLightweightListener(parent); - /* Now that all the children has gotten their peers, we should + // We need to lock the tree here to avoid races and inconsistencies. + synchronized (getTreeLock()) + { + if (peer == null) + peer = getToolkit().createComponent(this); + else if (parent != null && parent.isLightweight()) + new HeavyweightInLightweightListener(parent); + /* Now that all the children has gotten their peers, we should have the event mask needed for this component and its lightweight subcomponents. */ - peer.setEventMask(eventMask); - /* We do not invalidate here, but rather leave that job up to + peer.setEventMask(eventMask); + /* We do not invalidate here, but rather leave that job up to the peer. For efficiency, the peer can choose not to invalidate if it is happy with the current dimensions, etc. */ + if (dropTarget != null) + dropTarget.addNotify(peer); + } } /** @@ -3460,17 +3901,21 @@ public abstract class Component */ public void removeNotify() { - // We null our peer field before disposing of it, such that if we're - // not the event dispatch thread and the dispatch thread is awoken by - // the dispose call, there will be no race checking the peer's null - // status. - - ComponentPeer tmp = peer; - peer = null; - if (tmp != null) + // We need to lock the tree here to avoid races and inconsistencies. + synchronized (getTreeLock()) { - tmp.hide(); - tmp.dispose(); + // We null our peer field before disposing of it, such that if we're + // not the event dispatch thread and the dispatch thread is awoken by + // the dispose call, there will be no race checking the peer's null + // status. + + ComponentPeer tmp = peer; + peer = null; + if (tmp != null) + { + tmp.hide(); + tmp.dispose(); + } } } @@ -3791,56 +4236,7 @@ public abstract class Component */ public void requestFocus () { - if (isDisplayable () - && isShowing () - && isFocusable ()) - { - synchronized (getTreeLock ()) - { - // Find this Component's top-level ancestor. - Container parent = (this instanceof Container) ? (Container) this - : getParent(); - while (parent != null - && !(parent instanceof Window)) - parent = parent.getParent (); - - if (parent == null) - return; - - Window toplevel = (Window) parent; - if (toplevel.isFocusableWindow ()) - { - if (peer != null && !isLightweight()) - // This call will cause a FOCUS_GAINED event to be - // posted to the system event queue if the native - // windowing system grants the focus request. - peer.requestFocus (); - else - { - // Either our peer hasn't been created yet or we're a - // lightweight component. In either case we want to - // post a FOCUS_GAINED event. - EventQueue eq = Toolkit.getDefaultToolkit ().getSystemEventQueue (); - synchronized (eq) - { - KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); - Component currentFocusOwner = manager.getGlobalPermanentFocusOwner (); - if (currentFocusOwner != null) - { - eq.postEvent (new FocusEvent(currentFocusOwner, FocusEvent.FOCUS_LOST, - false, this)); - eq.postEvent (new FocusEvent(this, FocusEvent.FOCUS_GAINED, false, - currentFocusOwner)); - } - else - eq.postEvent (new FocusEvent(this, FocusEvent.FOCUS_GAINED, false)); - } - } - } - else - pendingFocusRequest = new FocusEvent(this, FocusEvent.FOCUS_GAINED); - } - } + requestFocusImpl(false, true); } /** @@ -3880,61 +4276,7 @@ public abstract class Component */ protected boolean requestFocus (boolean temporary) { - if (isDisplayable () - && isShowing () - && isFocusable ()) - { - synchronized (getTreeLock ()) - { - // Find this Component's top-level ancestor. - Container parent = getParent (); - - while (parent != null - && !(parent instanceof Window)) - parent = parent.getParent (); - - Window toplevel = (Window) parent; - if (toplevel.isFocusableWindow ()) - { - if (peer != null && !isLightweight()) - // This call will cause a FOCUS_GAINED event to be - // posted to the system event queue if the native - // windowing system grants the focus request. - peer.requestFocus (); - else - { - // Either our peer hasn't been created yet or we're a - // lightweight component. In either case we want to - // post a FOCUS_GAINED event. - EventQueue eq = Toolkit.getDefaultToolkit ().getSystemEventQueue (); - synchronized (eq) - { - KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); - Component currentFocusOwner = manager.getGlobalPermanentFocusOwner (); - if (currentFocusOwner != null) - { - eq.postEvent (new FocusEvent(currentFocusOwner, - FocusEvent.FOCUS_LOST, - temporary, this)); - eq.postEvent (new FocusEvent(this, - FocusEvent.FOCUS_GAINED, - temporary, - currentFocusOwner)); - } - else - eq.postEvent (new FocusEvent(this, FocusEvent.FOCUS_GAINED, temporary)); - } - } - } - else - // FIXME: need to add a focus listener to our top-level - // ancestor, so that we can post this event when it becomes - // the focused window. - pendingFocusRequest = new FocusEvent(this, FocusEvent.FOCUS_GAINED, temporary); - } - } - // Always return true. - return true; + return requestFocusImpl(temporary, true); } /** @@ -3962,7 +4304,7 @@ public abstract class Component */ public boolean requestFocusInWindow () { - return requestFocusInWindow (false); + return requestFocusImpl(false, false); } /** @@ -3993,65 +4335,84 @@ public abstract class Component */ protected boolean requestFocusInWindow (boolean temporary) { - KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager (); - - Window focusedWindow = manager.getFocusedWindow (); + return requestFocusImpl(temporary, false); + } - if (isDisplayable () - && isShowing () - && isFocusable ()) + /** + * Helper method for all 4 requestFocus variants. + * + * @param temporary indicates if the focus change is temporary + * @param focusWindow indicates if the window focus may be changed + * + * @return <code>false</code> if the request has been definitely denied, + * <code>true</code> otherwise + */ + private boolean requestFocusImpl(boolean temporary, boolean focusWindow) + { + boolean retval = false; + + // Don't try to focus non-focusable and non-visible components. + if (isFocusable() && isVisible()) { - if (focusedWindow != null) + ComponentPeer myPeer = peer; + if (peer != null) { - synchronized (getTreeLock ()) + // Find Window ancestor and find out if we're showing while + // doing this. + boolean showing = true; + Component window = this; + while (! (window instanceof Window)) { - Container parent = getParent (); - - while (parent != null - && !(parent instanceof Window)) - parent = parent.getParent (); - - Window toplevel = (Window) parent; - - // Check if top-level ancestor is currently focused window. - if (focusedWindow == toplevel) + if (! window.isVisible()) + showing = false; + window = window.parent; + } + // Don't allow focus when there is no window or the window + // is not focusable. + if (window != null && ((Window) window).isFocusableWindow() + && showing) + { + // Search for nearest heavy ancestor (including this + // component). + Component heavyweightParent = this; + while (heavyweightParent.peer instanceof LightweightPeer) + heavyweightParent = heavyweightParent.parent; + + // Don't allow focus on lightweight components without + // visible heavyweight ancestor + if (heavyweightParent != null && heavyweightParent.isVisible()) { - if (peer != null - && !isLightweight() - && !(this instanceof Window)) - // This call will cause a FOCUS_GAINED event to be - // posted to the system event queue if the native - // windowing system grants the focus request. - peer.requestFocus (); - else + // Don't allow focus when heavyweightParent has no peer. + myPeer = heavyweightParent.peer; + if (myPeer != null) { - // Either our peer hasn't been created yet or we're a - // lightweight component. In either case we want to - // post a FOCUS_GAINED event. - EventQueue eq = Toolkit.getDefaultToolkit ().getSystemEventQueue (); - synchronized (eq) + // Register lightweight focus request. + if (heavyweightParent != this) + { + KeyboardFocusManager + .addLightweightFocusRequest(heavyweightParent, + this); + } + + // Try to focus the component. + long time = EventQueue.getMostRecentEventTime(); + boolean success = myPeer.requestFocus(this, temporary, + focusWindow, + time); + if (! success) { - Component currentFocusOwner = manager.getGlobalPermanentFocusOwner (); - if (currentFocusOwner != null) - { - eq.postEvent (new FocusEvent(currentFocusOwner, FocusEvent.FOCUS_LOST, - temporary, this)); - eq.postEvent (new FocusEvent(this, FocusEvent.FOCUS_GAINED, temporary, - currentFocusOwner)); - } - else - eq.postEvent (new FocusEvent(this, FocusEvent.FOCUS_GAINED, temporary)); + // Dequeue key events if focus request failed. + KeyboardFocusManager kfm = + KeyboardFocusManager.getCurrentKeyboardFocusManager(); + kfm.dequeueKeyEvents(time, this); } + retval = success; } } - else - return false; } } - - return true; } - return false; + return retval; } /** @@ -4552,7 +4913,7 @@ p * <li>the set of backward traversal keys Object newValue) { if (changeSupport != null) - changeSupport.firePropertyChange(propertyName, oldValue, newValue); + changeSupport.firePropertyChange(propertyName, oldValue, newValue); } /** @@ -4693,14 +5054,12 @@ p * <li>the set of backward traversal keys * {@link #applyComponentOrientation(ComponentOrientation)} affects the * entire hierarchy. * - * @param o the new orientation - * @throws NullPointerException if o is null + * @param o the new orientation (<code>null</code> is accepted) * @see #getComponentOrientation() */ public void setComponentOrientation(ComponentOrientation o) { - if (o == null) - throw new NullPointerException(); + ComponentOrientation oldOrientation = orientation; orientation = o; firePropertyChange("componentOrientation", oldOrientation, o); @@ -4709,7 +5068,7 @@ p * <li>the set of backward traversal keys /** * Determines the text layout orientation used by this component. * - * @return the component orientation + * @return the component orientation (this can be <code>null</code>) * @see #setComponentOrientation(ComponentOrientation) */ public ComponentOrientation getComponentOrientation() @@ -4864,7 +5223,7 @@ p * <li>the set of backward traversal keys if ((mods & InputEvent.ALT_DOWN_MASK) != 0) oldMods |= Event.ALT_MASK; - if (e instanceof MouseEvent) + if (e instanceof MouseEvent && !ignoreOldMouseEvents()) { if (id == MouseEvent.MOUSE_PRESSED) oldID = Event.MOUSE_DOWN; @@ -5045,52 +5404,26 @@ p * <li>the set of backward traversal keys void dispatchEventImpl(AWTEvent e) { - // This boolean tells us not to process focus events when the focus - // opposite component is the same as the focus component. - boolean ignoreFocus = - (e instanceof FocusEvent && - ((FocusEvent)e).getComponent() == ((FocusEvent)e).getOppositeComponent()); - - if (eventTypeEnabled (e.id)) + // Retarget focus events before dispatching it to the KeyboardFocusManager + // in order to handle lightweight components properly. + boolean dispatched = false; + if (! e.isFocusManagerEvent) { - if (e.id != PaintEvent.PAINT && e.id != PaintEvent.UPDATE - && !ignoreFocus) - processEvent(e); - - // the trick we use to communicate between dispatch and redispatch - // is to have KeyboardFocusManager.redispatch synchronize on the - // object itself. we then do not redispatch to KeyboardFocusManager - // if we are already holding the lock. - if (! Thread.holdsLock(e)) + e = KeyboardFocusManager.retargetFocusEvent(e); + dispatched = KeyboardFocusManager.getCurrentKeyboardFocusManager() + .dispatchEvent(e); + } + + if (! dispatched) + { + if (eventTypeEnabled (e.id)) { - switch (e.id) - { - case WindowEvent.WINDOW_GAINED_FOCUS: - case WindowEvent.WINDOW_LOST_FOCUS: - case KeyEvent.KEY_PRESSED: - case KeyEvent.KEY_RELEASED: - case KeyEvent.KEY_TYPED: - case FocusEvent.FOCUS_GAINED: - case FocusEvent.FOCUS_LOST: - if (KeyboardFocusManager - .getCurrentKeyboardFocusManager() - .dispatchEvent(e)) - return; - case MouseEvent.MOUSE_PRESSED: - // A mouse click on an enabled lightweight component - // which has not yet been marked as consumed by any - // other mouse listener results in a focus traversal - // to that component. - if (isLightweight() - && isEnabled() && !e.isConsumed()) - requestFocus(); - break; - } + if (e.id != PaintEvent.PAINT && e.id != PaintEvent.UPDATE) + processEvent(e); } + if (peer != null) + peer.handleEvent(e); } - - if (peer != null) - peer.handleEvent(e); } /** |