| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to you under the Apache License, |
| * Version 2.0 (the "License"); you may not use this file except in |
| * compliance with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.pivot.wtk; |
| |
| import java.awt.Graphics2D; |
| import java.awt.GraphicsConfiguration; |
| import java.awt.Rectangle; |
| import java.awt.Transparency; |
| import java.util.Iterator; |
| |
| import org.apache.pivot.collections.ArrayList; |
| import org.apache.pivot.collections.Sequence; |
| import org.apache.pivot.util.ImmutableIterator; |
| import org.apache.pivot.util.ListenerList; |
| import org.apache.pivot.wtk.effects.Decorator; |
| |
| /** |
| * Abstract base class for containers. |
| */ |
| public abstract class Container extends Component implements Sequence<Component>, |
| Iterable<Component> { |
| private static class ContainerListenerList extends WTKListenerList<ContainerListener> implements |
| ContainerListener { |
| @Override |
| public void componentInserted(Container container, int index) { |
| for (ContainerListener listener : this) { |
| listener.componentInserted(container, index); |
| } |
| } |
| |
| @Override |
| public void componentsRemoved(Container container, int index, Sequence<Component> components) { |
| for (ContainerListener listener : this) { |
| listener.componentsRemoved(container, index, components); |
| } |
| } |
| |
| @Override |
| public void componentMoved(Container container, int from, int to) { |
| for (ContainerListener listener : this) { |
| listener.componentMoved(container, from, to); |
| } |
| } |
| |
| @Override |
| public void focusTraversalPolicyChanged(Container container, |
| FocusTraversalPolicy previousFocusTraversalPolicy) { |
| for (ContainerListener listener : this) { |
| listener.focusTraversalPolicyChanged(container, previousFocusTraversalPolicy); |
| } |
| } |
| } |
| |
| private static class ContainerMouseListenerList extends WTKListenerList<ContainerMouseListener> |
| implements ContainerMouseListener { |
| @Override |
| public boolean mouseMove(Container container, int x, int y) { |
| boolean consumed = false; |
| |
| for (ContainerMouseListener listener : this) { |
| consumed |= listener.mouseMove(container, x, y); |
| } |
| |
| return consumed; |
| } |
| |
| @Override |
| public boolean mouseDown(Container container, Mouse.Button button, int x, int y) { |
| boolean consumed = false; |
| |
| for (ContainerMouseListener listener : this) { |
| consumed |= listener.mouseDown(container, button, x, y); |
| } |
| |
| return consumed; |
| } |
| |
| @Override |
| public boolean mouseUp(Container container, Mouse.Button button, int x, int y) { |
| boolean consumed = false; |
| |
| for (ContainerMouseListener listener : this) { |
| consumed |= listener.mouseUp(container, button, x, y); |
| } |
| |
| return consumed; |
| } |
| |
| @Override |
| public boolean mouseWheel(Container container, Mouse.ScrollType scrollType, |
| int scrollAmount, int wheelRotation, int x, int y) { |
| boolean consumed = false; |
| |
| for (ContainerMouseListener listener : this) { |
| consumed |= listener.mouseWheel(container, scrollType, scrollAmount, wheelRotation, |
| x, y); |
| } |
| |
| return consumed; |
| } |
| } |
| |
| private ArrayList<Component> components = new ArrayList<>(); |
| |
| private FocusTraversalPolicy focusTraversalPolicy = null; |
| |
| private Component mouseOverComponent = null; |
| private boolean mouseDown = false; |
| private Component mouseDownComponent = null; |
| private long mouseDownTime = 0; |
| private int mouseClickCount = 0; |
| private boolean mouseClickConsumed = false; |
| |
| // The component's double-buffering buffer and flags |
| private boolean doubleBuffering = false; |
| private java.awt.image.BufferedImage doubleBufferImage = null; |
| private boolean doubleBufferedRepaintRequired = false; |
| |
| private ContainerListenerList containerListeners = new ContainerListenerList(); |
| private ContainerMouseListenerList containerMouseListeners = new ContainerMouseListenerList(); |
| |
| @Override |
| public final int add(Component component) { |
| int index = getLength(); |
| insert(component, index); |
| |
| return index; |
| } |
| |
| @Override |
| public void insert(Component component, int index) { |
| assertEventDispatchThread(); |
| if (component == null) { |
| throw new IllegalArgumentException("component is null."); |
| } |
| |
| if (component instanceof Container && ((Container) component).isAncestor(this)) { |
| throw new IllegalArgumentException("Component already exists in ancestry."); |
| } |
| |
| if (component.getParent() != null) { |
| throw new IllegalArgumentException("Component already has a parent."); |
| } |
| |
| component.setParent(Container.this); |
| components.insert(component, index); |
| |
| // Repaint the area occupied by the new component |
| repaint(component.getDecoratedBounds()); |
| |
| invalidate(); |
| |
| containerListeners.componentInserted(Container.this, index); |
| } |
| |
| @Override |
| public Component update(int index, Component component) { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public final int remove(Component component) { |
| int index = indexOf(component); |
| if (index != -1) { |
| remove(index, 1); |
| } |
| |
| return index; |
| } |
| |
| @Override |
| public Sequence<Component> remove(int index, int count) { |
| assertEventDispatchThread(); |
| Sequence<Component> removed = components.remove(index, count); |
| |
| // Set the removed components' parent to null and repaint the area |
| // formerly occupied by the components |
| for (int i = 0, n = removed.getLength(); i < n; i++) { |
| Component component = removed.get(i); |
| if (component == mouseOverComponent) { |
| if (mouseOverComponent.isMouseOver()) { |
| mouseOverComponent.mouseOut(); |
| } |
| |
| mouseOverComponent = null; |
| Mouse.setCursor(this); |
| } |
| |
| repaint(component.getDecoratedBounds()); |
| |
| component.setParent(null); |
| } |
| |
| if (removed.getLength() > 0) { |
| invalidate(); |
| containerListeners.componentsRemoved(Container.this, index, removed); |
| } |
| |
| return removed; |
| } |
| |
| public final Sequence<Component> removeAll() { |
| return remove(0, getLength()); |
| } |
| |
| /** |
| * Moves a component within the component sequence. |
| * |
| * @param from The index where the component is currently located. |
| * @param to The index to move it to. |
| */ |
| public void move(int from, int to) { |
| assertEventDispatchThread(); |
| if (from != to) { |
| int n = components.getLength(); |
| |
| indexBoundsCheck("from", from, 0, n - 1); |
| indexBoundsCheck("to", to, 0, n - 1); |
| |
| Sequence<Component> removed = components.remove(from, 1); |
| Component component = removed.get(0); |
| components.insert(component, to); |
| |
| // Repaint the area occupied by the component |
| repaint(component.getDecoratedBounds()); |
| |
| // Notify listeners |
| containerListeners.componentMoved(this, from, to); |
| } |
| } |
| |
| @Override |
| public Component get(int index) { |
| assertEventDispatchThread(); |
| return components.get(index); |
| } |
| |
| @Override |
| public int indexOf(Component component) { |
| assertEventDispatchThread(); |
| return components.indexOf(component); |
| } |
| |
| @Override |
| public int getLength() { |
| assertEventDispatchThread(); |
| return components.getLength(); |
| } |
| |
| @Override |
| public Iterator<Component> iterator() { |
| assertEventDispatchThread(); |
| return new ImmutableIterator<>(components.iterator()); |
| } |
| |
| @Override |
| protected void setParent(Container parent) { |
| // If this container is being removed from the component hierarchy |
| // and contains the focused component, clear the focus |
| if (parent == null && containsFocus()) { |
| clearFocus(); |
| } |
| |
| super.setParent(parent); |
| } |
| |
| public Component getComponentAt(int x, int y) { |
| assertEventDispatchThread(); |
| Component component = null; |
| |
| int i = components.getLength() - 1; |
| while (i >= 0) { |
| component = components.get(i); |
| if (component.isVisible()) { |
| Bounds bounds = component.getBounds(); |
| if (bounds.contains(x, y)) { |
| break; |
| } |
| } |
| |
| i--; |
| } |
| |
| if (i < 0) { |
| component = null; |
| } |
| |
| return component; |
| } |
| |
| public Component getDescendantAt(int x, int y) { |
| Component component = getComponentAt(x, y); |
| |
| if (component instanceof Container) { |
| Container container = (Container) component; |
| component = container.getDescendantAt(x - container.getX(), y - container.getY()); |
| } |
| |
| if (component == null) { |
| component = this; |
| } |
| |
| return component; |
| } |
| |
| public Component getNamedComponent(String name) { |
| if (name == null) { |
| throw new IllegalArgumentException(); |
| } |
| |
| Component namedComponent = null; |
| |
| for (Component component : this) { |
| if (name.equals(component.getName())) { |
| namedComponent = component; |
| break; |
| } |
| } |
| |
| return namedComponent; |
| } |
| |
| @Override |
| public void setVisible(boolean visible) { |
| if (!visible && containsFocus()) { |
| clearFocus(); |
| } |
| |
| super.setVisible(visible); |
| } |
| |
| @Override |
| protected void layout() { |
| super.layout(); |
| |
| for (int i = 0, n = components.getLength(); i < n; i++) { |
| Component component = components.get(i); |
| component.validate(); |
| } |
| } |
| |
| @Override |
| public void paint(Graphics2D graphics) { |
| if (!doubleBuffering) { |
| paint0(graphics); |
| } else { |
| boolean freshImage = false; |
| if (doubleBufferImage == null) { |
| GraphicsConfiguration gc = graphics.getDeviceConfiguration(); |
| doubleBufferImage = gc.createCompatibleImage(getWidth(), getHeight(), |
| Transparency.OPAQUE); |
| doubleBufferedRepaintRequired = true; |
| freshImage = true; |
| } |
| |
| Graphics2D bufferedImageGraphics = (Graphics2D) doubleBufferImage.getGraphics(); |
| try { |
| if (doubleBufferedRepaintRequired) { |
| if (!freshImage) { |
| bufferedImageGraphics.setClip(graphics.getClip()); |
| } |
| paint0(bufferedImageGraphics); |
| doubleBufferedRepaintRequired = false; |
| } |
| graphics.drawImage(doubleBufferImage, 0, 0, null); |
| } finally { |
| bufferedImageGraphics.dispose(); |
| } |
| |
| } |
| } |
| |
| private void paint0(Graphics2D graphics) { |
| int count = getLength(); |
| |
| // Determine the paint bounds |
| Bounds paintBounds = new Bounds(0, 0, getWidth(), getHeight()); |
| Rectangle clipBounds = graphics.getClipBounds(); |
| if (clipBounds != null) { |
| paintBounds = paintBounds.intersect(clipBounds); |
| } |
| |
| // Determine if we need to paint the container, or if it's completely |
| // obscured by a child component. |
| boolean paintContainer = true; |
| for (int i = 0; i < count; i++) { |
| Component component = get(i); |
| |
| if (component.isVisible() && component.isOpaque() |
| && component.getBounds().contains(paintBounds)) { |
| paintContainer = false; |
| break; |
| } |
| } |
| |
| if (paintContainer) { |
| // Give the base method a copy of the graphics context; otherwise, |
| // container skins can change the graphics state before it is passed |
| // to subcomponents |
| Graphics2D containerGraphics = (Graphics2D) graphics.create(); |
| super.paint(containerGraphics); |
| containerGraphics.dispose(); |
| } |
| |
| for (int i = 0; i < count; i++) { |
| Component component = get(i); |
| |
| // Calculate the decorated bounds |
| Bounds decoratedBounds = component.getDecoratedBounds(); |
| |
| // Only paint components that are visible and intersect the |
| // current clip rectangle |
| if (component.isVisible() && decoratedBounds.intersects(paintBounds)) { |
| Bounds componentBounds = component.getBounds(); |
| |
| // Create a copy of the current graphics context and |
| // translate to the component's coordinate system |
| Graphics2D decoratedGraphics = (Graphics2D) graphics.create(); |
| decoratedGraphics.translate(componentBounds.x, componentBounds.y); |
| |
| // Prepare the decorators |
| DecoratorSequence decorators = component.getDecorators(); |
| int n = decorators.getLength(); |
| for (int j = n - 1; j >= 0; j--) { |
| Decorator decorator = decorators.get(j); |
| decoratedGraphics = decorator.prepare(component, decoratedGraphics); |
| } |
| |
| // Paint the component |
| Graphics2D componentGraphics = (Graphics2D) decoratedGraphics.create(); |
| componentGraphics.clipRect(0, 0, componentBounds.width, componentBounds.height); |
| component.paint(componentGraphics); |
| componentGraphics.dispose(); |
| |
| // Update the decorators |
| for (int j = 0; j < n; j++) { |
| Decorator decorator = decorators.get(j); |
| decorator.update(); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Tests if this container is an ancestor of a given component. A container |
| * is considered to be its own ancestor. |
| * |
| * @param component The component to test. |
| * @return <tt>true</tt> if this container is an ancestor of |
| * <tt>component</tt>; <tt>false</tt> otherwise. |
| */ |
| public boolean isAncestor(Component component) { |
| boolean ancestor = false; |
| |
| Component parent = component; |
| while (parent != null) { |
| if (parent == this) { |
| ancestor = true; |
| break; |
| } |
| |
| parent = parent.getParent(); |
| } |
| |
| return ancestor; |
| } |
| |
| /** |
| * Requests that focus be given to this container. If this container is not |
| * focusable, this requests that focus be set to the first focusable |
| * descendant in this container. |
| * |
| * @return <tt>true</tt> if the container or one of its descendants gained |
| * the focus or <tt>false</tt> if no component was found to gain the focus. |
| */ |
| @Override |
| public boolean requestFocus() { |
| boolean focused = false; |
| |
| if (isFocusable()) { |
| focused = super.requestFocus(); |
| } else { |
| if (focusTraversalPolicy != null) { |
| Component first = focusTraversalPolicy.getNextComponent(this, null, |
| FocusTraversalDirection.FORWARD); |
| |
| Component component = first; |
| while (component != null && !component.requestFocus()) { |
| component = focusTraversalPolicy.getNextComponent(this, component, |
| FocusTraversalDirection.FORWARD); |
| |
| // Ensure that we don't get into an infinite loop |
| if (component == first) { |
| break; |
| } |
| } |
| |
| focused = (component != null); |
| } |
| } |
| |
| return focused; |
| } |
| |
| /** |
| * Transfers focus to the next focusable component. |
| * |
| * @param component The component from which focus will be transferred. |
| * @param direction The direction in which to transfer focus. |
| * @return The newly focused component. |
| */ |
| public Component transferFocus(Component component, FocusTraversalDirection direction) { |
| Component componentUpdated = component; |
| if (focusTraversalPolicy == null) { |
| // The container has no traversal policy; move up a level |
| componentUpdated = transferFocus(direction); |
| } else { |
| do { |
| componentUpdated = focusTraversalPolicy.getNextComponent(this, componentUpdated, |
| direction); |
| |
| if (componentUpdated != null) { |
| if (componentUpdated.isFocusable()) { |
| componentUpdated.requestFocus(); |
| } else { |
| if (componentUpdated instanceof Container) { |
| Container container = (Container) componentUpdated; |
| componentUpdated = container.transferFocus(null, direction); |
| } |
| } |
| } |
| } while (componentUpdated != null && !componentUpdated.isFocused()); |
| |
| if (componentUpdated == null) { |
| // We are at the end of the traversal |
| componentUpdated = transferFocus(direction); |
| } |
| } |
| |
| return componentUpdated; |
| } |
| |
| /** |
| * @return This container's focus traversal policy. |
| */ |
| public FocusTraversalPolicy getFocusTraversalPolicy() { |
| return this.focusTraversalPolicy; |
| } |
| |
| /** |
| * Sets this container's focus traversal policy. |
| * |
| * @param focusTraversalPolicy The focus traversal policy to use with this |
| * container. |
| */ |
| public void setFocusTraversalPolicy(FocusTraversalPolicy focusTraversalPolicy) { |
| FocusTraversalPolicy previousFocusTraversalPolicy = this.focusTraversalPolicy; |
| |
| if (previousFocusTraversalPolicy != focusTraversalPolicy) { |
| this.focusTraversalPolicy = focusTraversalPolicy; |
| containerListeners.focusTraversalPolicyChanged(this, previousFocusTraversalPolicy); |
| } |
| } |
| |
| /** |
| * Tests whether this container is an ancestor of the currently focused |
| * component. |
| * |
| * @return <tt>true</tt> if a component is focused and this container is an |
| * ancestor of the component; <tt>false</tt>, otherwise. |
| */ |
| public boolean containsFocus() { |
| Component focusedComponent = getFocusedComponent(); |
| return (focusedComponent != null && isAncestor(focusedComponent)); |
| } |
| |
| protected void descendantAdded(Component descendant) { |
| Container parent = getParent(); |
| |
| if (parent != null) { |
| parent.descendantAdded(descendant); |
| } |
| } |
| |
| protected void descendantRemoved(Component descendant) { |
| Container parent = getParent(); |
| |
| if (parent != null) { |
| parent.descendantRemoved(descendant); |
| } |
| } |
| |
| protected void descendantGainedFocus(Component descendant, Component previousFocusedComponent) { |
| Container parent = getParent(); |
| |
| if (parent != null) { |
| parent.descendantGainedFocus(descendant, previousFocusedComponent); |
| } |
| } |
| |
| protected void descendantLostFocus(Component descendant) { |
| Container parent = getParent(); |
| |
| if (parent != null) { |
| parent.descendantLostFocus(descendant); |
| } |
| } |
| |
| /** |
| * Propagates binding to subcomponents. |
| * |
| * @param context The object we're binding to. |
| */ |
| @Override |
| public void load(Object context) { |
| for (Component component : components) { |
| component.load(context); |
| } |
| } |
| |
| /** |
| * Propagates binding to subcomponents. |
| * |
| * @param context The object we're binding to. |
| */ |
| @Override |
| public void store(Object context) { |
| for (Component component : components) { |
| component.store(context); |
| } |
| } |
| |
| /** |
| * Propagates clear operation to subcomponents. |
| */ |
| @Override |
| public void clear() { |
| for (Component component : components) { |
| component.clear(); |
| } |
| } |
| |
| @Override |
| protected boolean mouseMove(int x, int y) { |
| boolean consumed = false; |
| |
| // Clear the mouse over component if its mouse-over state has |
| // changed (e.g. if its enabled or visible properties have |
| // changed) |
| if (mouseOverComponent != null && !mouseOverComponent.isMouseOver()) { |
| mouseOverComponent = null; |
| } |
| |
| if (isEnabled()) { |
| // Synthesize mouse over/out events |
| Component component = getComponentAt(x, y); |
| |
| if (mouseOverComponent != component) { |
| if (mouseOverComponent != null) { |
| mouseOverComponent.mouseOut(); |
| } |
| |
| mouseOverComponent = null; |
| Mouse.setCursor(this); |
| } |
| |
| // Notify container listeners |
| consumed = containerMouseListeners.mouseMove(this, x, y); |
| |
| if (!consumed) { |
| if (mouseOverComponent != component) { |
| mouseOverComponent = component; |
| |
| if (mouseOverComponent != null) { |
| mouseOverComponent.mouseOver(); |
| Mouse.setCursor(mouseOverComponent); |
| } |
| } |
| |
| // Propagate event to subcomponents |
| if (component != null) { |
| consumed = component.mouseMove(x - component.getX(), y - component.getY()); |
| } |
| |
| // Notify the base class |
| if (!consumed) { |
| consumed = super.mouseMove(x, y); |
| } |
| } |
| } |
| |
| return consumed; |
| } |
| |
| @Override |
| protected void mouseOut() { |
| // Ensure that mouse out is called on descendant components |
| if (mouseOverComponent != null && mouseOverComponent.isMouseOver()) { |
| mouseOverComponent.mouseOut(); |
| } |
| |
| mouseOverComponent = null; |
| |
| super.mouseOut(); |
| } |
| |
| @Override |
| protected boolean mouseDown(Mouse.Button button, int x, int y) { |
| boolean consumed = false; |
| |
| mouseDown = true; |
| |
| if (isEnabled()) { |
| // Notify container listeners |
| consumed = containerMouseListeners.mouseDown(this, button, x, y); |
| |
| if (!consumed) { |
| // Synthesize mouse click event |
| Component component = getComponentAt(x, y); |
| |
| long currentTime = System.currentTimeMillis(); |
| int multiClickInterval = Platform.getMultiClickInterval(); |
| if (mouseDownComponent == component |
| && currentTime - mouseDownTime < multiClickInterval) { |
| mouseClickCount++; |
| } else { |
| mouseDownTime = System.currentTimeMillis(); |
| mouseClickCount = 1; |
| } |
| |
| mouseDownComponent = component; |
| |
| // Propagate event to subcomponents |
| if (component != null) { |
| // Ensure that mouse over is called |
| if (!component.isMouseOver()) { |
| component.mouseOver(); |
| } |
| |
| consumed = component.mouseDown(button, x - component.getX(), |
| y - component.getY()); |
| } |
| |
| // Notify the base class |
| if (!consumed) { |
| consumed = super.mouseDown(button, x, y); |
| } |
| } |
| } |
| |
| return consumed; |
| } |
| |
| @Override |
| protected boolean mouseUp(Mouse.Button button, int x, int y) { |
| boolean consumed = false; |
| mouseClickConsumed = false; |
| |
| if (isEnabled()) { |
| // Notify container listeners |
| consumed = containerMouseListeners.mouseUp(this, button, x, y); |
| |
| if (!consumed) { |
| // Propagate event to subcomponents |
| Component component = getComponentAt(x, y); |
| |
| if (component != null) { |
| // Ensure that mouse over is called |
| if (!component.isMouseOver()) { |
| component.mouseOver(); |
| } |
| |
| consumed = component.mouseUp(button, x - component.getX(), y - component.getY()); |
| } |
| |
| // Notify the base class |
| if (!consumed) { |
| consumed = super.mouseUp(button, x, y); |
| } |
| |
| // Synthesize mouse click event |
| if (mouseDown && component != null && component == mouseDownComponent |
| && component.isEnabled() && component.isVisible()) { |
| mouseClickConsumed = component.mouseClick(button, x - component.getX(), y |
| - component.getY(), mouseClickCount); |
| } |
| } |
| } |
| |
| mouseDown = false; |
| |
| return consumed; |
| } |
| |
| @Override |
| protected boolean mouseClick(Mouse.Button button, int x, int y, int count) { |
| if (isEnabled()) { |
| if (!mouseClickConsumed) { |
| // Allow the event to propagate |
| mouseClickConsumed = super.mouseClick(button, x, y, count); |
| } |
| } |
| |
| return mouseClickConsumed; |
| } |
| |
| @Override |
| protected boolean mouseWheel(Mouse.ScrollType scrollType, int scrollAmount, int wheelRotation, |
| int x, int y) { |
| boolean consumed = false; |
| |
| if (isEnabled()) { |
| // Notify container listeners |
| consumed = containerMouseListeners.mouseWheel(this, scrollType, scrollAmount, |
| wheelRotation, x, y); |
| |
| if (!consumed) { |
| // Propagate event to subcomponents |
| Component component = getComponentAt(x, y); |
| |
| if (component != null) { |
| // Ensure that mouse over is called |
| if (!component.isMouseOver()) { |
| component.mouseOver(); |
| } |
| |
| consumed = component.mouseWheel(scrollType, scrollAmount, wheelRotation, x |
| - component.getX(), y - component.getY()); |
| } |
| |
| // Notify the base class |
| if (!consumed) { |
| consumed = super.mouseWheel(scrollType, scrollAmount, wheelRotation, x, y); |
| } |
| } |
| } |
| |
| return consumed; |
| } |
| |
| @Override |
| public void invalidate() { |
| super.invalidate(); |
| |
| if (doubleBuffering) { |
| doubleBufferImage = null; |
| } |
| } |
| |
| @Override |
| public void repaint(int x, int y, int width, int height, boolean immediate) { |
| super.repaint(x, y, width, height, immediate); |
| |
| if (doubleBuffering) { |
| doubleBufferedRepaintRequired = true; |
| } |
| } |
| |
| @Override |
| public Graphics2D getGraphics() { |
| Graphics2D g = super.getGraphics(); |
| doubleBufferedRepaintRequired = true; |
| return g; |
| } |
| |
| public boolean isDoubleBuffered() { |
| return doubleBuffering; |
| } |
| |
| public void setDoubleBuffered(boolean b) { |
| doubleBuffering = b; |
| if (b) { |
| invalidate(); |
| } else { |
| doubleBufferImage = null; |
| doubleBufferedRepaintRequired = false; |
| } |
| } |
| |
| public ListenerList<ContainerListener> getContainerListeners() { |
| return containerListeners; |
| } |
| |
| public ListenerList<ContainerMouseListener> getContainerMouseListeners() { |
| return containerMouseListeners; |
| } |
| |
| public interface EDT_Checker { |
| public void check(Component component); |
| } |
| |
| protected final void assertEventDispatchThread() { |
| assertEventDispatchThread(this); |
| } |
| |
| private static EDT_Checker EDT_CHECKER = new EDT_Checker() { |
| @Override |
| public void check(Component component) { |
| String threadName = Thread.currentThread().getName(); |
| /* |
| * Currently, application startup happens on the main thread, so we |
| * need to allow that thread to modify WTK state. |
| */ |
| if (threadName.equals("main") || threadName.equals("javawsApplicationMain")) { |
| return; |
| } |
| // Allow components to be constructed from outside the event thread |
| if (component.getDisplay() == null) { |
| return; |
| } |
| /* |
| * See Sun/Oracle bug 6424157. There is a race condition where we |
| * can be running on the event thread but isDispatchThread() will |
| * return false. |
| */ |
| if (threadName.startsWith("AWT-EventQueue-")) { |
| return; |
| } |
| if (!java.awt.EventQueue.isDispatchThread()) { |
| throw new IllegalStateException( |
| "this method can only be called from the AWT event dispatch thread" |
| + ", and not from \"" + threadName + "\""); |
| } |
| } |
| }; |
| |
| public static final void assertEventDispatchThread(Component component) { |
| if (EDT_CHECKER != null) { |
| EDT_CHECKER.check(component); |
| } |
| } |
| |
| public static final void setEventDispatchThreadChecker(EDT_Checker runnable) { |
| EDT_CHECKER = runnable; |
| } |
| } |