Package pivot.wtk

Source Code of pivot.wtk.Window$WindowListenerList

/*
* 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 pivot.wtk;

import java.net.URL;

import pivot.collections.ArrayList;
import pivot.collections.ArrayStack;
import pivot.collections.Dictionary;
import pivot.collections.HashMap;
import pivot.collections.Sequence;
import pivot.util.ListenerList;
import pivot.util.Vote;
import pivot.wtk.media.Image;

/**
* Top-level container representing the entry point into a user interface.
* Windows are direct descendants of the display.
*
* @author gbrown
*/
public class Window extends Container {
    /**
     * Action dictionary implementation.
     *
     * @author gbrown
     */
    public final class ActionDictionary
        implements Dictionary<Keyboard.KeyStroke, Action> {
        private ActionDictionary() {
        }

        public Action get(Keyboard.KeyStroke key) {
            return actions.get(key);
        }

        public Action put(Keyboard.KeyStroke key, Action value) {
            return actions.put(key, value);

            // TODO Fire WindowActionListener#actionAdded()/actionUpdated()
        }

        public Action remove(Keyboard.KeyStroke key) {
            return actions.remove(key);

            // TODO Fire WindowActionListener#actionRemoved()
        }

        public boolean containsKey(Keyboard.KeyStroke key) {
            return actions.containsKey(key);
        }

        public boolean isEmpty() {
            return actions.isEmpty();
        }
    }

    /**
     * Window listener list.
     *
     * @author gbrown
     */
    private static class WindowListenerList extends ListenerList<WindowListener>
        implements WindowListener {
        public void titleChanged(Window window, String previousTitle) {
            for (WindowListener listener : this) {
                listener.titleChanged(window, previousTitle);
            }
        }

        public void iconChanged(Window window, Image previousIcon) {
            for (WindowListener listener : this) {
                listener.iconChanged(window, previousIcon);
            }
        }

        public void contentChanged(Window window, Component previousContent) {
            for (WindowListener listener : this) {
                listener.contentChanged(window, previousContent);
            }
        }

        public void windowMoved(Window window, int from, int to) {
            for (WindowListener listener : this) {
                listener.windowMoved(window, from, to);
            }
        }

        public void ownerChanged(Window window, Window previousOwner) {
            for (WindowListener listener : this) {
                listener.ownerChanged(window, previousOwner);
            }
        }

        public void activeChanged(Window window) {
            for (WindowListener listener : this) {
                listener.activeChanged(window);
            }
        }

        public void maximizedChanged(Window window) {
            for (WindowListener listener : this) {
                listener.maximizedChanged(window);
            }
        }
    }

    /**
     * Window state listener list.
     *
     * @author gbrown
     */
    private static class WindowStateListenerList extends ListenerList<WindowStateListener>
        implements WindowStateListener {
        public Vote previewWindowOpen(Window window, Display display) {
            Vote vote = Vote.APPROVE;

            for (WindowStateListener listener : this) {
                vote = vote.tally(listener.previewWindowOpen(window, display));
            }

            return vote;
        }

        public void windowOpenVetoed(Window window, Vote reason) {
            for (WindowStateListener listener : this) {
                listener.windowOpenVetoed(window, reason);
            }
        }

        public void windowOpened(Window window) {
            for (WindowStateListener listener : this) {
                listener.windowOpened(window);
            }
        }

        public Vote previewWindowClose(Window window) {
            Vote vote = Vote.APPROVE;

            for (WindowStateListener listener : this) {
                vote = vote.tally(listener.previewWindowClose(window));
            }

            return vote;
        }

        public void windowCloseVetoed(Window window, Vote reason) {
            for (WindowStateListener listener : this) {
                listener.windowCloseVetoed(window, reason);
            }
        }

        public void windowClosed(Window window, Display display) {
            for (WindowStateListener listener : this) {
                listener.windowClosed(window, display);
            }
        }
    }

    /**
     * Window class listener list.
     *
     * @author tvolkert
     */
    private static class WindowClassListenerList
        extends ListenerList<WindowClassListener>
        implements WindowClassListener {
        public void activeWindowChanged(Window previousActiveWindow) {
            for (WindowClassListener listener : this) {
                listener.activeWindowChanged(previousActiveWindow);
            }
        }
    }

    private boolean auxilliary;

    private Window owner = null;
    private ArrayList<Window> ownedWindows = new ArrayList<Window>();

    private HashMap<Keyboard.KeyStroke, Action> actions = new HashMap<Keyboard.KeyStroke, Action>();
    private ActionDictionary actionDictionary = new ActionDictionary();

    private String title = null;
    private Image icon = null;
    private Component content = null;
    private Component activeDescendant = null;

    private boolean opening = false;
    private boolean closing = false;

    private boolean maximized = false;

    private WindowListenerList windowListeners = new WindowListenerList();
    private WindowStateListenerList windowStateListeners = new WindowStateListenerList();
    private static WindowClassListenerList windowClassListeners = new WindowClassListenerList();

    private static Window activeWindow = null;

    public Window() {
        this(null, false);
    }

    public Window(boolean auxilliary) {
        this(null, auxilliary);
    }

    public Window(Component content) {
        this(content, false);
    }

    public Window(Component content, boolean auxilliary) {
        this.auxilliary = auxilliary;

        setContent(content);
        installSkin(Window.class);
    }

    @Override
    protected void setParent(Container parent) {
        if (parent != null
            && (!(parent instanceof Display))) {
            throw new IllegalArgumentException("Window parent must be null or display.");
        }

        if (parent == null
            && isActive()) {
            setActiveWindow(null);
        }

        super.setParent(parent);
    }

    @Override
    public Sequence<Component> remove(int index, int count) {
        for (int i = index, n = index + count; i < n; i++) {
            Component component = get(i);
            if (component == content) {
                throw new UnsupportedOperationException();
            }
        }

        // Call the base method to remove the components
        return super.remove(index, count);
    }

    /**
     * Sets the displayable state of this window and all of its owned
     * descendant windows.
     *
     * @param displayable
     * If <tt>true</tt>, the window and its owned descendants are displayable;
     * otherwise, they are not displayable.
     */
    @Override
    public void setDisplayable(boolean displayable) {
        super.setDisplayable(displayable);

        if (!displayable) {
            if (isActive()) {
                setActiveWindow(null);
            }
        }

        // Show/hide owned windows
        for (Window ownedWindow : ownedWindows) {
            ownedWindow.setDisplayable(displayable);
        }
    }

    /**
     * Sets the enabled state of this window and all of its owned
     * descendant windows.
     *
     * @param enabled
     * If <tt>true</tt>, the window and its owned descendants are enabled;
     * otherwise, they are not enabled.
     */
    @Override
    public void setEnabled(boolean enabled) {
        if (isEnabled() != enabled) {
            super.setEnabled(enabled);

            if (isEnabled() == enabled) {
                if (!enabled
                    && isActive()) {
                    setActiveWindow(null);
                }

                // Enable/disable owned windows
                for (Window ownedWindow : ownedWindows) {
                    ownedWindow.setEnabled(enabled);
                }
            }
        }
    }

    public Window getOwner() {
        return owner;
    }

    public void setOwner(Window owner) {
        if (owner != null
            && owner.isAuxilliary()
            && !isAuxilliary()) {
            throw new IllegalArgumentException("Primary windows must have a"
                + " primary owner.");
        }

        Window previousOwner = this.owner;

        if (previousOwner != owner) {
            if (previousOwner != null) {
                previousOwner.ownedWindows.remove(this);
            }

            if (owner != null) {
                owner.ownedWindows.add(this);
                setDisplayable(owner.isDisplayable());
                setEnabled(owner.isEnabled());
            }

            this.owner = owner;

            windowListeners.ownerChanged(this, previousOwner);
        }
    }

    public Window getRootOwner() {
        return (owner == null) ? this : owner.getRootOwner();
    }

    public Window getOwnedWindow(int index) {
        return ownedWindows.get(index);
    }

    public int getOwnedWindowCount() {
        return ownedWindows.getLength();
    }

    /**
     * Tests whether this window is an owning ancestor of a given window. A
     * window is not considered an owner of itself.
     *
     * @param window
     *
     * @return
     * <tt>true</tt> if this window is an owning ancestor of the given window;
     * <tt>false</tt>, otherwise.
     */
    public boolean isOwner(Window window) {
        if (window == null) {
            throw new IllegalArgumentException("window is null.");
        }

        Window owner = window.getOwner();

        while (owner != null
            && owner != this) {
            owner = owner.getOwner();
        }

        return (owner == this);
    }

    /**
     * Returns this window's open state.
     *
     * @return
     * <tt>true</tt> if the window is open; <tt>false</tt>, otherwise.
     */
    public boolean isOpen() {
        return (getParent() != null);
    }

    /**
     * Returns this window's opening state.
     *
     * @return
     * <tt>true</tt> if the window is open; <tt>false</tt>, otherwise.
     */
    public boolean isOpening() {
        return opening;
    }

    /**
     * Opens the window. Opening a window adds it to the display's component
     * sequence. If the window is activatable, it will become the active
     * window.
     *
     * @param display
     * The display on which the window will be opened.
     */
    public void open(Display display) {
        if (display == null) {
            throw new IllegalArgumentException("display is null.");
        }

        if (isOpen()
            && getDisplay() != display) {
            throw new IllegalStateException("Window is already open on a different display.");
        }

        if (owner != null
            && !owner.isOpen()) {
            throw new IllegalArgumentException("Owner is not open.");
        }

        if (owner != null
            && owner.getDisplay() != display) {
            throw new IllegalArgumentException("Owner is opened on a different display.");
        }

        if (!isOpen()
            && !opening) {
            Vote vote = windowStateListeners.previewWindowOpen(this, display);

            if (vote == Vote.APPROVE) {
                opening = true;

                // Add this as child of display
                display.add(this);

                // Notify listeners
                windowStateListeners.windowOpened(this);

                // Move this window to the front (which, unless this window is
                // disabled or incapable of becoming active, will activate the
                // window)
                moveToFront();

                opening = false;
            } else {
                windowStateListeners.windowOpenVetoed(this, vote);
            }
        }
    }

    /**
     * Opens the window.
     *
     * @param owner
     * The window's owner.
     */
    public void open(Window owner) {
        if (owner == null) {
            throw new IllegalArgumentException("owner is null.");
        }

        if (isOpen()
            && getOwner() != owner) {
            throw new IllegalStateException("Window is already open with a different owner.");
        }

        setOwner(owner);
        open(owner.getDisplay());
    }

    /**
     * Returns this window's closed state.
     *
     * @return
     * <tt>true</tt> if the window is closed; <tt>false</tt>, otherwise.
     */
    public boolean isClosed() {
        return !isOpen();
    }

    /**
     * Returns this window's closing state.
     *
     * @return
     * <tt>true</tt> if the window is closing; <tt>false</tt>, otherwise.
     */
    public boolean isClosing() {
        return closing;
    }

    /**
     * Closes the window. Closing a window closes all owned windows and
     * removes the window from the display's component sequence. If the window
     * was the active window, the active window will be cleared. If the window
     * was the focus host, the focused component will be cleared.
     */
    public void close() {
        if (!isClosed()
            && !closing) {
            Vote vote = windowStateListeners.previewWindowClose(this);

            if (vote.isApproved()) {
                closing = true;

                if (isActive()) {
                    setActiveWindow(null);
                }

                // Close all owned windows (create a copy of the owned window
                // list so owned windows can remove themselves from the list
                // without interrupting the iteration)
                for (Window ownedWindow : new ArrayList<Window>(this.ownedWindows)) {
                    ownedWindow.close();
                }

                // Detach from display
                Display display = getDisplay();
                display.remove(this);

                // Notify listeners
                windowStateListeners.windowClosed(this, display);

                closing = false;
            } else {
                windowStateListeners.windowCloseVetoed(this, vote);
            }
        }
    }

    /**
     * Returns the window's title.
     *
     * @return
     * The pane's title, or <tt>null</tt> if no title is set.
     */
    public String getTitle() {
        return title;
    }

    /**
     * Sets the window's title.
     *
     * @param title
     * The new title, or <tt>null</tt> for no title.
     */
    public void setTitle(String title) {
        String previousTitle = this.title;

        if (previousTitle == null ^ title == null) {
            this.title = title;
            windowListeners.titleChanged(this, previousTitle);
        }
    }

    /**
     * Returns the window's icon.
     *
     * @return
     * The window's icon, or <tt>null</tt> if the window has no icon.
     */
    public Image getIcon() {
        return icon;
    }

    /**
     * Sets the window's icon.
     *
     * @param icon
     * The window's icon, or <tt>null</tt> for no icon.
     */
    public void setIcon(Image icon) {
        Image previousIcon = this.icon;

        if (previousIcon != icon) {
            this.icon = icon;
            windowListeners.iconChanged(this, previousIcon);
        }
    }

    /**
     * Sets the window's icon by URL.
     *
     * @param icon
     * The location of the icon to set.
     */
    public void setIcon(URL icon) {
        if (icon == null) {
            throw new IllegalArgumentException("icon is null.");
        }

        setIcon(Image.load(icon));
    }

    /**
     * Sets the window's icon by resource name.
     *
     * @param icon
     * The resource name of the icon to set.
     */
    public void setIcon(String icon) {
        if (icon == null) {
            throw new IllegalArgumentException("icon is null.");
        }

        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        setIcon(classLoader.getResource(icon));
    }

    public Component getContent() {
        return content;
    }

    public void setContent(Component content) {
        Component previousContent = this.content;

        if (content != previousContent) {
            this.content = null;

            // Remove any previous content component
            if (previousContent != null) {
                remove(previousContent);
            }

            // Add the component
            if (content != null) {
                insert(content, 0);
            }

            this.content = content;

            windowListeners.contentChanged(this, previousContent);
        }
    }

    /**
     * Returns the window's auxilliary flag. Auxilliary windows must have an
     * owner, can't become active, and can only own other auxilliary windows.
     *
     * @return
     * <tt>true</tt> if this is an auxilliary window; <tt>false</tt>, otherwise.
     */
    public boolean isAuxilliary() {
        return auxilliary;
    }

    /**
     * Returns the window's active state.
     *
     * @return
     * <tt>true</tt> if the window is active; <tt>false</tt>; otherwise.
     */
    public boolean isActive() {
        return activeWindow == this;
    }

    /**
     * Called to notify a window that its active state has changed.
     *
     * @param active
     */
    protected void setActive(boolean active) {
        if (active) {
            // If this window is still an ancestor of the active descendant
            // and the active descendant can be focused, restore focus to it;
            // otherwise, clear the active descendant
            if (activeDescendant != null) {
                if (isAncestor(activeDescendant)
                    && !activeDescendant.isBlocked()
                    && activeDescendant.isShowing()) {
                    activeDescendant.requestFocus(true);
                } else {
                    activeDescendant = null;
                }
            }
        } else {
            // Temporarily clear the focus
            clearFocus(true);
        }

        windowListeners.activeChanged(this);
    }

    /**
     * Returns the currently active window.
     *
     * @return
     * The window that is currently active, or <tt>null</tt> if no window
     * is active.
     */
    public static Window getActiveWindow() {
        return activeWindow;
    }

    /**
     * Sets the active window. The window must be activatable, open, and
     * enabled. If the window is not currently visible, it will be made
     * visible.
     *
     * @param activeWindow
     * The window to activate, or <tt>null</tt> to clear the active window.
     */
    public static void setActiveWindow(Window activeWindow) {
        Window previousActiveWindow = Window.activeWindow;

        if (previousActiveWindow != activeWindow) {
            if (activeWindow != null) {
                if (activeWindow.isAuxilliary()) {
                    throw new IllegalArgumentException("activeWindow is auxilliary.");
                }

                if (!activeWindow.isOpen()) {
                    throw new IllegalArgumentException("activeWindow is not open.");
                }

                if (!activeWindow.isEnabled()) {
                    throw new IllegalArgumentException("activeWindow is not enabled.");
                }
            }

            // Set the active window
            Window.activeWindow = activeWindow;

            // Notify the windows of the state change
            if (previousActiveWindow != null) {
                previousActiveWindow.setActive(false);
            }

            // Activate the window
            if (activeWindow != null) {
                activeWindow.setActive(true);
            }

            windowClassListeners.activeWindowChanged(previousActiveWindow);
        }
    }

    /**
     * Returns the window descendant that currently has the focus.
     */
    public Component getActiveDescendant() {
        return activeDescendant;
    }

    /**
     * Sets the window's active descendant, the descendant that currently
     * has the focus.
     *
     * @param activeDescendant
     */
    protected void setActiveDescendant(Component activeDescendant) {
        this.activeDescendant = activeDescendant;
    }

    /**
     * Returns the global action map for this window.
     */
    public ActionDictionary getActions() {
        return actionDictionary;
    }

    /**
     * Moves the window to the top of the window stack. The window is removed
     * from its current position in the display's component sequence and
     * appended to the end. It is also moved to the top of its owner's owned
     * window list so it becomes top-most of all windows owned by its owner.
     * <p>
     * All windows owned by this window are subsequently moved to the front,
     * ensuring that this window's owned windows remain on top of it.
     * <p>
     * Finally, the window is made active and focus is restored to the most
     * recently focused decendant component.
     */
    public void moveToFront() {
        if (!isOpen()) {
            throw new IllegalStateException("Window is not open.");
        }

        // If this window is not currently top-most, move it to the top
        Display display = getDisplay();

        Window window = this;
        ArrayStack<Integer> ownedWindowIndexes = new ArrayStack<Integer>();
        ownedWindowIndexes.push(0);

        while (ownedWindowIndexes.getLength() > 0) {
            // Get the next owned window index for this window
            int j = ownedWindowIndexes.peek();

            if (j == 0) {
                // Move the window within the window stack
                int i = display.indexOf(window);

                if (i < display.getLength() - 1) {
                    int k = display.getLength() - 1;
                    display.move(i, k);
                    window.windowListeners.windowMoved(window, i, k);
                }
            }

            if (j < window.ownedWindows.getLength()) {
                // There is another owned window to traverse; move down
                // the tree
                ownedWindowIndexes.poke(j + 1);
                window = window.ownedWindows.get(j);

                // If the window is not open, ignore it
                if (window.isOpen()) {
                    ownedWindowIndexes.push(0);
                } else {
                    window = window.owner;
                }
            } else {
                // Activate the window
                if (window.isEnabled()
                    && !window.isAuxilliary()) {
                    setActiveWindow(window);
                }

                // This was the last owned window for the current window; move
                // up the tree
                ownedWindowIndexes.pop();
                window = window.owner;
            }
        }

        // Move this window to the top of its owner's owned window list,
        // so it becomes top-most of all windows owned by this window's
        // owner
        if (owner != null) {
            int j = owner.ownedWindows.indexOf(this);

            if (j < owner.ownedWindows.getLength() - 1) {
                owner.ownedWindows.remove(j, 1);
                owner.ownedWindows.add(this);
            }
        }
    }

    /**
     * Moves the window to the bottom of the window stack. If the window is
     * active, the active window will be cleared. If the window is the focus
     * host, the focus will be cleared.
     */
    public void moveToBack() {
        if (!isOpen()) {
            throw new IllegalStateException("Window is not open.");
        }

        if (isActive()) {
            setActiveWindow(null);
        }

        Display display = getDisplay();

        // Ensure that the window and all of its owning ancestors are moved
        // to the back
        Window window = this;
        while (window != null) {
            // If this window is not currently bottom-most, move it to the
            // bottom
            int i = display.indexOf(window);

            if (i > 0) {
                display.move(i, 0);
                window.windowListeners.windowMoved(window, i, 0);
            }

            window = window.getOwner();
        }

        // Move this window to the bottom of its owner's owned window list
        if (owner != null) {
            int j = owner.ownedWindows.indexOf(this);

            if (j > 0) {
                owner.ownedWindows.remove(j, 1);
                owner.ownedWindows.insert(this, 0);
            }
        }
    }

    public boolean isMaximized() {
        return maximized;
    }

    public void setMaximized(boolean maximized) {
        if (maximized != this.maximized) {
            this.maximized = maximized;

            invalidate();

            windowListeners.maximizedChanged(this);
        }
    }

    public void align(Bounds bounds,
        HorizontalAlignment horizontalAlignment,
        VerticalAlignment verticalAlignment) {
        align(bounds, horizontalAlignment, 0, verticalAlignment, 0);
    }

    public void align(Bounds bounds,
        HorizontalAlignment horizontalAlignment, int horizontalOffset,
        VerticalAlignment verticalAlignment, int verticalOffset) {

        int x = 0;
        int y = 0;

        Dimensions size = getSize();

        if (horizontalAlignment == HorizontalAlignment.LEFT) {
            x = bounds.x - size.width;
        }
        else if (horizontalAlignment == HorizontalAlignment.RIGHT) {
            x = bounds.x + bounds.width - size.width;
        }
        else if (horizontalAlignment == HorizontalAlignment.CENTER) {
            x = bounds.x + (int)Math.round((double)(bounds.width - size.width) / 2);
        }
        else {
            throw new IllegalArgumentException("Unsupported horizontal alignment.");
        }

        x += horizontalOffset;

        if (verticalAlignment == VerticalAlignment.TOP) {
            y = bounds.y - size.height;
        }
        else if (verticalAlignment == VerticalAlignment.BOTTOM) {
            y = bounds.y + bounds.height;
        }
        else if (verticalAlignment == VerticalAlignment.CENTER) {
            y = bounds.y + (int)Math.round((double)(bounds.height - size.height) / 2);
        }
        else {
            throw new IllegalArgumentException("Unsupported vertical alignment.");
        }

        y += verticalOffset;

        setLocation(x, y);
    }

    @Override
    protected boolean mouseDown(Mouse.Button button, int x, int y) {
        // NOTE This is done here rather than in WindowSkin because the
        // user input methods are not called on the skin when the component
        // is disabled

        if (isEnabled()) {
            // Bring this window to the front
            moveToFront();
        } else {
            ApplicationContext.beep();

            // Bring the window's owner tree to the front
            Window rootOwner = getRootOwner();
            rootOwner.moveToFront();
        }

        return super.mouseDown(button, x, y);
    }

    public ListenerList<WindowListener> getWindowListeners() {
        return windowListeners;
    }

    public ListenerList<WindowStateListener> getWindowStateListeners() {
        return windowStateListeners;
    }

    public static ListenerList<WindowClassListener> getWindowClassListeners() {
        return windowClassListeners;
    }
}
TOP

Related Classes of pivot.wtk.Window$WindowListenerList

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.