You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by tv...@apache.org on 2009/03/26 00:12:17 UTC

svn commit: r758461 [27/47] - in /incubator/pivot/branches: ./ 1.1/ 1.1/charts-test/ 1.1/charts-test/src/ 1.1/charts-test/src/pivot/ 1.1/charts-test/src/pivot/charts/ 1.1/charts-test/src/pivot/charts/test/ 1.1/charts/ 1.1/charts/lib/ 1.1/charts/src/ 1....

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/Window.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/Window.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/Window.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/Window.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,975 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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;
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/WindowClassListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/WindowClassListener.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/WindowClassListener.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/WindowClassListener.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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;
+
+/**
+ * Window class listener interface.
+ *
+ * @author tvolkert
+ */
+public interface WindowClassListener {
+    /**
+     * Called when the active window has changed.
+     *
+     * @param previousActiveWindow
+     */
+    public void activeWindowChanged(Window previousActiveWindow);
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/WindowListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/WindowListener.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/WindowListener.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/WindowListener.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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 pivot.wtk.media.Image;
+
+/**
+ * Window listener interface.
+ *
+ * @author gbrown
+ */
+public interface WindowListener {
+    /**
+     * Called when a window's title has changed.
+     *
+     * @param window
+     * @param previousTitle
+     */
+    public void titleChanged(Window window, String previousTitle);
+
+    /**
+     * Called when a window's icon has changed.
+     *
+     * @param window
+     * @param previousIcon
+     */
+    public void iconChanged(Window window, Image previousIcon);
+
+    /**
+     * Called when a window's content component has changed.
+     *
+     * @param window
+     * @param previousContent
+     */
+    public void contentChanged(Window window, Component previousContent);
+
+    /**
+     * Called when a window's owner has changed.
+     *
+     * @param window
+     * @param previousOwner
+     */
+    public void ownerChanged(Window window, Window previousOwner);
+
+    /**
+     * Called when a window's active state has changed.
+     *
+     * @param window
+     */
+    public void activeChanged(Window window);
+
+    /**
+     * Called when a window's maximized state has changed.
+     *
+     * @param window
+     */
+    public void maximizedChanged(Window window);
+
+    /**
+     * Called when a window's position has changed.
+     *
+     * @param window
+     * @param from
+     * @param to
+     */
+    public void windowMoved(Window window, int from, int to);
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/WindowStateListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/WindowStateListener.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/WindowStateListener.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/WindowStateListener.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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 pivot.util.Vote;
+
+/**
+ * Window state listener interface.
+ *
+ * @author gbrown
+ * @author tvolkert
+ */
+public interface WindowStateListener {
+    /**
+     * Called to preview a window open event.
+     *
+     * @param window
+     * @param display
+     */
+    public Vote previewWindowOpen(Window window, Display display);
+
+    /**
+     * Called when a window open event has been vetoed.
+     *
+     * @param window
+     * @param reason
+     */
+    public void windowOpenVetoed(Window window, Vote reason);
+
+    /**
+     * Called when a window has opened.
+     *
+     * @param window
+     */
+    public void windowOpened(Window window);
+
+    /**
+     * Called to preview a window close event.
+     *
+     * @param window
+     */
+    public Vote previewWindowClose(Window window);
+
+    /**
+     * Called when a window close event has been vetoed.
+     *
+     * @param window
+     * @param reason
+     */
+    public void windowCloseVetoed(Window window, Vote reason);
+
+    /**
+     * Called when a window has closed.
+     *
+     * @param window
+     * @param display
+     */
+    public void windowClosed(Window window, Display display);
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ButtonData.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ButtonData.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ButtonData.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ButtonData.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.content;
+
+import java.net.URL;
+
+import pivot.wtk.ApplicationContext;
+import pivot.wtk.media.Image;
+
+/**
+ * Default button data implementation.
+ *
+ * @author gbrown
+ */
+public class ButtonData {
+    private Image icon;
+    private String text;
+
+    public ButtonData() {
+        this(null, null);
+    }
+
+    public ButtonData(Image icon) {
+        this(icon, null);
+    }
+
+    public ButtonData(String text) {
+        this(null, text);
+    }
+
+    public ButtonData(Image icon, String text) {
+        this.icon = icon;
+        this.text = text;
+    }
+
+    public Image getIcon() {
+        return icon;
+    }
+
+    public void setIcon(Image icon) {
+        this.icon = icon;
+    }
+
+    public void setIcon(URL iconURL) {
+        Image icon = (Image)ApplicationContext.getResourceCache().get(iconURL);
+
+        if (icon == null) {
+            icon = Image.load(iconURL);
+            ApplicationContext.getResourceCache().put(iconURL, icon);
+        }
+
+        setIcon(icon);
+    }
+
+    public void setIcon(String iconName) {
+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+        setIcon(classLoader.getResource(iconName));
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ButtonDataRenderer.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ButtonDataRenderer.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ButtonDataRenderer.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ButtonDataRenderer.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.content;
+
+import java.awt.Color;
+import java.awt.Font;
+
+import pivot.wtk.Button;
+import pivot.wtk.Component;
+import pivot.wtk.FlowPane;
+import pivot.wtk.HorizontalAlignment;
+import pivot.wtk.VerticalAlignment;
+import pivot.wtk.ImageView;
+import pivot.wtk.Label;
+import pivot.wtk.media.Image;
+
+/**
+ * Default button data renderer.
+ *
+ * @author gbrown
+ */
+public class ButtonDataRenderer extends FlowPane implements Button.DataRenderer {
+    protected ImageView imageView = new ImageView();
+    protected Label label = new Label();
+
+    public ButtonDataRenderer() {
+        getStyles().put("horizontalAlignment", HorizontalAlignment.CENTER);
+        getStyles().put("verticalAlignment", VerticalAlignment.CENTER);
+
+        add(imageView);
+        add(label);
+
+        imageView.getStyles().put("backgroundColor", null);
+    }
+
+    @Override
+    public void setSize(int width, int height) {
+        super.setSize(width, height);
+
+        // Since this component doesn't have a parent, it won't be validated
+        // via layout; ensure that it is valid here
+        validate();
+    }
+
+    public void render(Object data, Button button, boolean highlighted) {
+        Image icon = null;
+        String text = null;
+
+        if (data instanceof ButtonData) {
+            ButtonData buttonData = (ButtonData)data;
+            icon = buttonData.getIcon();
+            text = buttonData.getText();
+        } else if (data instanceof Image) {
+            icon = (Image)data;
+        } else {
+            if (data != null) {
+                text = data.toString();
+            }
+        }
+
+        // Show/hide the image view
+        if (icon == null) {
+            imageView.setDisplayable(false);
+        } else {
+            imageView.setDisplayable(true);
+            imageView.setImage(icon);
+
+            imageView.getStyles().put("opacity", button.isEnabled() ? 1.0f : 0.5f);
+        }
+
+        // Show/hide the label
+        if (text == null) {
+            label.setDisplayable(false);
+        } else {
+            label.setDisplayable(true);
+            label.setText(text);
+
+            // Update the label styles
+            Component.StyleDictionary labelStyles = label.getStyles();
+
+            Object labelFont = button.getStyles().get("font");
+            if (labelFont instanceof Font) {
+                labelStyles.put("font", labelFont);
+            }
+
+            Object color = button.isEnabled() ?
+                button.getStyles().get("color") :
+                    button.getStyles().get("disabledColor");
+
+            if (color instanceof Color) {
+                labelStyles.put("color", color);
+            }
+        }
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/CalendarButtonDataRenderer.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/CalendarButtonDataRenderer.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/CalendarButtonDataRenderer.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/CalendarButtonDataRenderer.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.content;
+
+import java.text.DateFormat;
+import java.util.Locale;
+
+import pivot.util.CalendarDate;
+import pivot.wtk.Button;
+import pivot.wtk.CalendarButton;
+import pivot.wtk.HorizontalAlignment;
+
+/**
+ * Default calendar button data renderer.
+ * <p>
+ * TODO Add showIcon property to this class so the size of the button doesn't
+ * change when changing selection between items with and without icons.
+ *
+ * @author gbrown
+ */
+public class CalendarButtonDataRenderer extends ButtonDataRenderer {
+    public CalendarButtonDataRenderer() {
+        getStyles().put("horizontalAlignment", HorizontalAlignment.LEFT);
+    }
+
+    @Override
+    public void render(Object data, Button button, boolean highlight) {
+        CalendarButton calendarButton = (CalendarButton)button;
+        Locale locale = calendarButton.getLocale();
+
+        if (data == null) {
+            data = "";
+        } else {
+            if (data instanceof CalendarDate) {
+                CalendarDate date = (CalendarDate)data;
+
+                DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
+                data = dateFormat.format(date.toCalendar().getTime());
+            }
+        }
+
+        super.render(data, button, highlight);
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/CalendarDateSpinnerData.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/CalendarDateSpinnerData.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/CalendarDateSpinnerData.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/CalendarDateSpinnerData.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.content;
+
+import java.util.Calendar;
+import java.util.Comparator;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.TimeZone;
+
+import pivot.collections.List;
+import pivot.collections.ListListener;
+import pivot.collections.Sequence;
+import pivot.util.CalendarDate;
+import pivot.util.ListenerList;
+
+/**
+ * Spinner data model that presents a bounded list of calendar dates.
+ * <p>
+ * This is a lightweight class that spoofs the actual list data by using an
+ * internal calendar instance from which <tt>CalendarDate</tt> instances are
+ * created on demand.
+ *
+ * @author tvolkert
+ */
+public class CalendarDateSpinnerData implements List<CalendarDate> {
+    /**
+     * Iterator that simply wraps calls to the list. Since the internal list
+     * data is spoofed, each accessor runs in constant time, so there's no
+     * performance hit in making the iterator delegate its implementation to
+     * the list.
+     *
+     * @author tvolkert
+     */
+    private class DataIterator implements Iterator<CalendarDate> {
+        private int index = 0;
+
+        public boolean hasNext() {
+            return (index < length);
+        }
+
+        public CalendarDate next() {
+            if (!hasNext()) {
+                throw new NoSuchElementException();
+            }
+
+            return get(index++);
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    private GregorianCalendar calendar;
+    private int calendarIndex;
+
+    // Calculated during construction
+    private transient int length;
+
+    private ListListenerList<CalendarDate> listListeners =
+        new ListListenerList<CalendarDate>();
+
+    /**
+     * Creates a new <tt>CalendarDateSpinnerData</tt> bounded from
+     * <tt>1900-01-01</tt> to <tt>2099-12-31</tt>.
+     */
+    public CalendarDateSpinnerData() {
+        this(new CalendarDate(1900, 0, 0), new CalendarDate(2099, 11, 30));
+    }
+
+    /**
+     * Creates a new <tt>CalendarDateSpinnerData</tt> bounded by the specified
+     * calendar dates (inclusive).
+     *
+     * @param lowerBound
+     * The earliest date to include in this spinner data.
+     *
+     * @param upperBound
+     * The latest date to include in this spinner data.
+     */
+    public CalendarDateSpinnerData(CalendarDate lowerBound, CalendarDate upperBound) {
+        if (lowerBound == null) {
+            throw new IllegalArgumentException("lowerBound is null.");
+        }
+
+        if (upperBound == null) {
+            throw new IllegalArgumentException("upperBound is null.");
+        }
+
+        if (lowerBound.compareTo(upperBound) >= 0) {
+            throw new IllegalArgumentException("lowerBound must be before upperBound.");
+        }
+
+        calendar = new GregorianCalendar(lowerBound.getYear(), lowerBound.getMonth(),
+            lowerBound.getDay() + 1);
+        calendar.setTimeZone(TimeZone.getTimeZone("GMT"));
+        calendarIndex = 0;
+
+        // Calculate our length and cache it, since it is guaranteed to
+        // remain fixed
+        GregorianCalendar upperBoundCalendar = new GregorianCalendar(upperBound.getYear(),
+            upperBound.getMonth(), upperBound.getDay() + 1);
+        upperBoundCalendar.setTimeZone(TimeZone.getTimeZone("GMT"));
+        long lowerBoundMilliseconds = calendar.getTimeInMillis();
+        long upperBoundMilliseconds = upperBoundCalendar.getTimeInMillis();
+        long indexDiff = (upperBoundMilliseconds - lowerBoundMilliseconds) /
+            (1000 * 60 * 60 * 24);
+        length = (int)indexDiff + 1;
+    }
+
+    /**
+     * Throws <tt>UnsupportedOperationException</tt>.
+     */
+    public int add(CalendarDate item) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Throws <tt>UnsupportedOperationException</tt>.
+     */
+    public void insert(CalendarDate item, int index) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Throws <tt>UnsupportedOperationException</tt>.
+     */
+    public CalendarDate update(int index, CalendarDate item) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Throws <tt>UnsupportedOperationException</tt>.
+     */
+    public int remove(CalendarDate item) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Throws <tt>UnsupportedOperationException</tt>.
+     */
+    public Sequence<CalendarDate> remove(int index, int count) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Gets the calendar date at the specified index.
+     *
+     * @param index
+     * The index of the calendar date to retrieve.
+     */
+    public CalendarDate get(int index) {
+        if (index < 0 || index >= length) {
+            throw new IndexOutOfBoundsException("Index out of bounds: " + index);
+        }
+
+        // Move the calendar's fields to match the specified index
+        calendar.add(Calendar.DAY_OF_YEAR, index - calendarIndex);
+        calendarIndex = index;
+
+        // Calculate the desired fields
+        int year = calendar.get(Calendar.YEAR);
+        int month = calendar.get(Calendar.MONTH);
+        int day = calendar.get(Calendar.DAY_OF_MONTH) - 1;
+
+        return new CalendarDate(year, month, day);
+    }
+
+    public int indexOf(CalendarDate item) {
+        long currentMilliseconds = calendar.getTimeInMillis();
+
+        int year = item.getYear();
+        int month = item.getMonth();
+        int day = item.getDay() + 1;
+
+        GregorianCalendar tmpCalendar = new GregorianCalendar(year, month, day);
+        tmpCalendar.setTimeZone(TimeZone.getTimeZone("GMT"));
+        long itemMilliseconds = tmpCalendar.getTimeInMillis();
+
+        long indexDiff = (itemMilliseconds - currentMilliseconds) / (1000 * 60 * 60 * 24);
+        int index = calendarIndex + (int)indexDiff;
+
+        return (index < 0 || index >= length) ? -1 : index;
+    }
+
+    /**
+     * Throws <tt>UnsupportedOperationException</tt>.
+     */
+    public void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Gets the number of entries in this list.
+     *
+     * @return
+     * The number of calendar dates in this list.
+     */
+    public int getLength() {
+        return length;
+    }
+
+    /**
+     * Gets the comparator for this list, which is guaranteed to always be
+     * <tt>null</tt>. This class does not support comparators since there's no
+     * real data to sort (it's all spoofed).
+     */
+    public Comparator<CalendarDate> getComparator() {
+        return null;
+    }
+
+    /**
+     * Throws <tt>UnsupportedOperationException</tt>.
+     */
+    public void setComparator(Comparator<CalendarDate> comparator) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Iterator<CalendarDate> iterator() {
+        return new DataIterator();
+    }
+
+    public ListenerList<ListListener<CalendarDate>> getListListeners() {
+        return listListeners;
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ColorItem.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ColorItem.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ColorItem.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ColorItem.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.content;
+
+import java.awt.Color;
+
+/**
+ * List item representing a color.
+ *
+ * @author gbrown
+ */
+public class ColorItem {
+	private Color color;
+	private String name;
+
+	public ColorItem() {
+		this(Color.BLACK, null);
+	}
+
+	public ColorItem(Color color) {
+		this(color, null);
+	}
+
+	public ColorItem(Color color, String name) {
+		this.color = color;
+		this.name = name;
+	}
+
+	public Color getColor() {
+		return color;
+	}
+
+	public void setColor(Color color) {
+		if (color == null) {
+			throw new IllegalArgumentException("color is null.");
+		}
+
+		this.color = color;
+	}
+
+	public void setColor(String color) {
+		if (color == null) {
+			throw new IllegalArgumentException("color is null.");
+		}
+
+		setColor(Color.decode(color));
+	}
+
+	public String getName() {
+		String name = this.name;
+
+		if (name == null) {
+	        name = String.format("#%02X%02X%02X", color.getRed(), color.getGreen(),
+	            color.getBlue());
+		}
+
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/LinkButtonDataRenderer.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/LinkButtonDataRenderer.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/LinkButtonDataRenderer.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/LinkButtonDataRenderer.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.content;
+
+import pivot.wtk.Button;
+import pivot.wtk.TextDecoration;
+
+/**
+ * Default link button data renderer.
+ *
+ * @author gbrown
+ */
+public class LinkButtonDataRenderer extends ButtonDataRenderer {
+    public LinkButtonDataRenderer() {
+        label.getStyles().put("wrapText", true);
+    }
+
+    @Override
+    public void render(Object data, Button button, boolean highlighted) {
+        super.render(data, button, highlighted);
+
+        label.getStyles().put("textDecoration", highlighted ?
+            TextDecoration.UNDERLINE : null);
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListButtonColorRenderer.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListButtonColorRenderer.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListButtonColorRenderer.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListButtonColorRenderer.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009 VMware, Inc.
+ *
+ * Licensed 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.content;
+
+import java.awt.Color;
+
+import pivot.wtk.Button;
+import pivot.wtk.ImageView;
+
+/**
+ * List button renderer for displaying color swatches.
+ *
+ * @author gbrown
+ */
+public class ListButtonColorRenderer extends ImageView
+	implements Button.DataRenderer {
+    private ListViewColorRenderer.ColorBadge colorBadge =
+    	new ListViewColorRenderer.ColorBadge();
+
+    public ListButtonColorRenderer() {
+    	setImage(colorBadge);
+    }
+
+    public void render(Object data, Button button, boolean highlighted) {
+    	Color color;
+    	if (data instanceof ColorItem) {
+    		ColorItem colorItem = (ColorItem)data;
+    		color = colorItem.getColor();
+    	} else {
+        	if (data instanceof Color) {
+        		color = (Color)data;
+        	} else {
+        		color = Color.decode(data.toString());
+        	}
+    	}
+
+    	colorBadge.setColor(color);
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListButtonDataRenderer.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListButtonDataRenderer.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListButtonDataRenderer.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListButtonDataRenderer.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.content;
+
+import pivot.wtk.Button;
+import pivot.wtk.HorizontalAlignment;
+
+/**
+ * Default list button data renderer.
+ * <p>
+ * TODO Add showIcon property to this class so the size of the button doesn't
+ * change when changing selection between items with and without icons.
+ *
+ * @author gbrown
+ */
+public class ListButtonDataRenderer extends ButtonDataRenderer {
+    public ListButtonDataRenderer() {
+        getStyles().put("horizontalAlignment", HorizontalAlignment.LEFT);
+    }
+
+    @Override
+    public void render(Object data, Button button, boolean highlight) {
+        if (data == null) {
+            data = "";
+        } else {
+            if (data instanceof ListItem) {
+                ListItem listItem = (ListItem)data;
+                data = new ButtonData(listItem.getIcon(), listItem.getText());
+            }
+        }
+
+        super.render(data, button, highlight);
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListItem.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListItem.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListItem.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListItem.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.content;
+
+import java.net.URL;
+
+import pivot.wtk.ApplicationContext;
+import pivot.wtk.media.Image;
+
+/**
+ * Default list item implementation.
+ *
+ * @author gbrown
+ */
+public class ListItem {
+    private Image icon;
+    private String text;
+
+    public ListItem() {
+        this(null, null);
+    }
+
+    public ListItem(Image icon) {
+        this(icon, null);
+    }
+
+    public ListItem(String text) {
+        this(null, text);
+    }
+
+    public ListItem(Image icon, String text) {
+        this.icon = icon;
+        this.text = text;
+    }
+
+    public Image getIcon() {
+        return icon;
+    }
+
+    public void setIcon(Image icon) {
+        this.icon = icon;
+    }
+
+    public void setIcon(URL iconURL) {
+        Image icon = (Image)ApplicationContext.getResourceCache().get(iconURL);
+
+        if (icon == null) {
+            icon = Image.load(iconURL);
+            ApplicationContext.getResourceCache().put(iconURL, icon);
+        }
+
+        setIcon(icon);
+    }
+
+    public void setIcon(String iconName) {
+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+        setIcon(classLoader.getResource(iconName));
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListViewColorRenderer.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListViewColorRenderer.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListViewColorRenderer.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListViewColorRenderer.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.content;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+
+import pivot.wtk.ListView;
+import pivot.wtk.media.Image;
+
+/**
+ * List view renderer for displaying color swatches.
+ *
+ * @author gbrown
+ */
+public class ListViewColorRenderer extends ListViewItemRenderer {
+    /**
+     * Internal image class for rendering color swatches.
+     *
+     * @author gbrown
+     */
+    public static class ColorBadge extends Image {
+        private Color color = Color.BLACK;
+        public static final int SIZE = 14;
+
+        public int getWidth() {
+            return SIZE;
+        }
+
+        public int getHeight() {
+            return SIZE;
+        }
+
+        public Color getColor() {
+            return color;
+        }
+
+        public void setColor(Color color) {
+            this.color = color;
+        }
+
+        public void paint(Graphics2D graphics) {
+            graphics.setColor(Color.WHITE);
+            graphics.fillRect(0, 0, SIZE, SIZE);
+            graphics.setColor(color);
+            graphics.fillRect(2, 2, SIZE - 4, SIZE - 4);
+            graphics.setColor(Color.GRAY);
+            graphics.drawRect(0, 0, SIZE - 1, SIZE - 1);
+        }
+    }
+
+    private ColorBadge colorBadge = new ColorBadge();
+    private ListItem listItem = new ListItem(colorBadge);
+
+    public ListViewColorRenderer() {
+    	setShowIcon(true);
+    }
+
+    public void render(Object item, ListView listView, boolean selected,
+        boolean checked, boolean highlighted, boolean disabled) {
+    	ColorItem colorItem;
+    	if (item instanceof ColorItem) {
+    		colorItem = (ColorItem)item;
+    	} else {
+    		Color color;
+        	if (item instanceof Color) {
+        		color = (Color)item;
+        	} else {
+        		color = Color.decode(item.toString());
+        	}
+
+        	colorItem = new ColorItem(color);
+    	}
+
+    	colorBadge.setColor(colorItem.getColor());
+        listItem.setText(colorItem.getName());
+
+        super.render(listItem, listView, selected, checked, highlighted, disabled);
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListViewItemEditor.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListViewItemEditor.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListViewItemEditor.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListViewItemEditor.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2009 VMware, Inc.
+ *
+ * Licensed 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.content;
+
+import pivot.collections.List;
+import pivot.util.Vote;
+import pivot.wtk.Bounds;
+import pivot.wtk.Component;
+import pivot.wtk.ComponentKeyListener;
+import pivot.wtk.Container;
+import pivot.wtk.ContainerMouseListener;
+import pivot.wtk.Display;
+import pivot.wtk.Insets;
+import pivot.wtk.Keyboard;
+import pivot.wtk.ListView;
+import pivot.wtk.ListViewItemListener;
+import pivot.wtk.ListViewListener;
+import pivot.wtk.Mouse;
+import pivot.wtk.TextInput;
+import pivot.wtk.Window;
+import pivot.wtk.WindowStateListener;
+
+/**
+ * Default list view item editor.
+ *
+ * @author gbrown
+ */
+public class ListViewItemEditor implements ListView.ItemEditor {
+    private ListView listView = null;
+    private int index = -1;
+
+    private TextInput textInput = null;
+    private Window popup = null;
+
+    private ListViewListener listViewListener = new ListViewListener() {
+        public void listDataChanged(ListView listView, List<?> previousListData) {
+            cancel();
+        }
+
+        public void itemRendererChanged(ListView listView, ListView.ItemRenderer previousItemRenderer) {
+            // No-op
+        }
+
+        public void itemEditorChanged(ListView listView, ListView.ItemEditor previousItemEditor) {
+            cancel();
+        }
+
+        public void selectModeChanged(ListView listView, ListView.SelectMode previousSelectMode) {
+            // No-op
+        }
+
+        public void checkmarksEnabledChanged(ListView listView) {
+            // No-op
+        }
+
+        public void selectedItemKeyChanged(ListView listView, String previousSelectedItemKey) {
+            // No-op
+        }
+
+        public void selectedItemsKeyChanged(ListView listView, String previousSelectedItemsKey) {
+            // No-op
+        }
+    };
+
+    private ListViewItemListener listViewItemListener = new ListViewItemListener() {
+        public void itemInserted(ListView listView, int index) {
+            cancel();
+        }
+
+        public void itemsRemoved(ListView listView, int index, int count) {
+            cancel();
+        }
+
+        public void itemUpdated(ListView listView, int index) {
+            cancel();
+        }
+
+        public void itemsSorted(ListView listView) {
+            cancel();
+        }
+    };
+
+    private ComponentKeyListener textInputKeyHandler = new ComponentKeyListener() {
+        @SuppressWarnings("unchecked")
+        public boolean keyPressed(Component component, int keyCode, Keyboard.KeyLocation keyLocation) {
+            if (keyCode == Keyboard.KeyCode.ENTER) {
+                save();
+            } else if (keyCode == Keyboard.KeyCode.ESCAPE) {
+                cancel();
+            }
+
+            return false;
+        }
+
+        public boolean keyReleased(Component component, int keyCode, Keyboard.KeyLocation keyLocation) {
+            return false;
+        }
+
+        public boolean keyTyped(Component component, char character) {
+            return false;
+        }
+    };
+
+    private WindowStateListener popupWindowStateHandler = new WindowStateListener() {
+        public Vote previewWindowOpen(Window window, Display display) {
+            return Vote.APPROVE;
+        }
+
+        public void windowOpenVetoed(Window window, Vote reason) {
+        }
+
+        public void windowOpened(Window window) {
+            Display display = window.getDisplay();
+            display.getContainerMouseListeners().add(displayMouseHandler);
+
+            listView.getListViewListeners().add(listViewListener);
+            listView.getListViewItemListeners().add(listViewItemListener);
+        }
+
+        public Vote previewWindowClose(Window window) {
+            return Vote.APPROVE;
+        }
+
+        public void windowCloseVetoed(Window window, Vote reason) {
+        }
+
+        public void windowClosed(Window window, Display display) {
+            display.getContainerMouseListeners().remove(displayMouseHandler);
+
+            listView.getListViewListeners().remove(listViewListener);
+            listView.getListViewItemListeners().remove(listViewItemListener);
+
+            listView.requestFocus();
+
+            listView = null;
+            index = -1;
+            textInput = null;
+            popup = null;
+        }
+    };
+
+    private ContainerMouseListener displayMouseHandler = new ContainerMouseListener() {
+        public boolean mouseMove(Container container, int x, int y) {
+            return false;
+        }
+
+        public boolean mouseDown(Container container, Mouse.Button button, int x, int y) {
+            Display display = (Display)container;
+            Window window = (Window)display.getComponentAt(x, y);
+            if (popup != window) {
+                save();
+            }
+
+            return false;
+        }
+
+        public boolean mouseUp(Container container, Mouse.Button button, int x, int y) {
+            return false;
+        }
+
+        public boolean mouseWheel(Container container, Mouse.ScrollType scrollType,
+            int scrollAmount, int wheelRotation, int x, int y) {
+            return true;
+        }
+    };
+
+    public void edit(ListView listView, int index) {
+        if (this.listView != null) {
+            throw new IllegalStateException("Currently editing.");
+        }
+
+        this.listView = listView;
+        this.index = index;
+
+        // Get the data being edited
+        List<?> listData = listView.getListData();
+        ListItem listItem = (ListItem)listData.get(index);
+
+        // Get the item bounds
+        Bounds itemBounds = listView.getItemBounds(index);
+        int itemIndent = listView.getItemIndent();
+        itemBounds.x += itemIndent;
+        itemBounds.width -= itemIndent;
+
+        // Render the item data
+        ListViewItemRenderer itemRenderer = (ListViewItemRenderer)listView.getItemRenderer();
+        itemRenderer.render(listItem, listView, false, false, false, false);
+        itemRenderer.setSize(itemBounds.width, itemBounds.height);
+
+        // Calculate the text bounds
+        Bounds textBounds = itemRenderer.getTextBounds();
+
+        if (textBounds != null) {
+            textInput = new TextInput();
+            Insets padding = (Insets)textInput.getStyles().get("padding");
+
+            // Calculate the bounds of what we're editing
+            Bounds editBounds = new Bounds(itemBounds);
+            editBounds.x += textBounds.x - (padding.left + 1);
+            editBounds.width -= textBounds.x;
+            editBounds.width += (padding.left + 1);
+
+            // Scroll to make the text as visible as possible
+            listView.scrollAreaToVisible(editBounds.x, editBounds.y,
+                textBounds.width + padding.left + 1, editBounds.height);
+
+            // Constrain the bounds by what is visible through Viewport ancestors
+            editBounds = listView.getVisibleArea(editBounds.x, editBounds.y,
+                editBounds.width, editBounds.height);
+
+            textInput.setText(listItem.getText());
+            textInput.setPreferredWidth(editBounds.width);
+            textInput.getComponentKeyListeners().add(textInputKeyHandler);
+
+            // Create and open the popup
+            popup = new Window(textInput);
+            popup.getWindowStateListeners().add(popupWindowStateHandler);
+
+            popup.setLocation(editBounds.x, editBounds.y
+                + (editBounds.height - textInput.getPreferredHeight(-1)) / 2);
+            popup.open(listView.getWindow());
+
+            textInput.requestFocus();
+        }
+    }
+
+    public boolean isEditing() {
+        return (listView != null);
+    }
+
+    @SuppressWarnings("unchecked")
+    public void save() {
+        if (!isEditing()) {
+            throw new IllegalStateException();
+        }
+
+        List<Object> listData = (List<Object>)listView.getListData();
+        ListItem listItem = (ListItem)listData.get(index);
+
+        // Update the item data
+        String text = textInput.getText();
+        listItem.setText(text);
+
+        // Notifying the parent will close the popup
+        listData.update(index, listItem);
+    }
+
+    public void cancel() {
+        if (!isEditing()) {
+            throw new IllegalStateException();
+        }
+
+        popup.close();
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListViewItemRenderer.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListViewItemRenderer.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListViewItemRenderer.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/ListViewItemRenderer.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.content;
+
+import java.awt.Color;
+import java.awt.Font;
+
+import pivot.wtk.Bounds;
+import pivot.wtk.Component;
+import pivot.wtk.FlowPane;
+import pivot.wtk.HorizontalAlignment;
+import pivot.wtk.ImageView;
+import pivot.wtk.Insets;
+import pivot.wtk.Label;
+import pivot.wtk.ListView;
+import pivot.wtk.VerticalAlignment;
+import pivot.wtk.media.Image;
+
+/**
+ * Default list view item renderer.
+ *
+ * @author gbrown
+ */
+public class ListViewItemRenderer extends FlowPane implements ListView.ItemRenderer {
+    protected ImageView imageView = new ImageView();
+    protected Label label = new Label();
+
+    public static final int DEFAULT_ICON_WIDTH = 16;
+    public static final int DEFAULT_ICON_HEIGHT = 16;
+    public static boolean DEFAULT_SHOW_ICON = false;
+
+    public ListViewItemRenderer() {
+        getStyles().put("horizontalAlignment", HorizontalAlignment.LEFT);
+        getStyles().put("verticalAlignment", VerticalAlignment.CENTER);
+        getStyles().put("padding", new Insets(2, 3, 2, 3));
+
+        add(imageView);
+        add(label);
+
+        imageView.setPreferredSize(DEFAULT_ICON_WIDTH, DEFAULT_ICON_HEIGHT);
+        imageView.setDisplayable(DEFAULT_SHOW_ICON);
+    }
+
+    @Override
+    public void setSize(int width, int height) {
+        super.setSize(width, height);
+
+        // Since this component doesn't have a parent, it won't be validated
+        // via layout; ensure that it is valid here
+        validate();
+    }
+
+    public void render(Object item, ListView listView, boolean selected,
+        boolean checked, boolean highlighted, boolean disabled) {
+        Image icon = null;
+        String text = null;
+
+        if (item instanceof ListItem) {
+            ListItem listItem = (ListItem)item;
+            icon = listItem.getIcon();
+            text = listItem.getText();
+        } else if (item instanceof Image) {
+            icon = (Image)item;
+        } else {
+            if (item != null) {
+                text = item.toString();
+            }
+        }
+
+        // Update the image view
+        imageView.setImage(icon);
+
+        // Show/hide the label
+        label.setText(text);
+
+        renderStyles(listView, selected, highlighted, disabled);
+    }
+
+    protected void renderStyles(ListView listView, boolean selected,
+        boolean highlighted, boolean disabled) {
+        Component.StyleDictionary listViewStyles = listView.getStyles();
+
+        Component.StyleDictionary imageViewStyles = imageView.getStyles();
+        Component.StyleDictionary labelStyles = label.getStyles();
+
+        imageViewStyles.put("opacity", listView.isEnabled() ? 1.0f : 0.5f);
+
+        if (label.getText() != null) {
+            Object labelFont = listViewStyles.get("font");
+            if (labelFont instanceof Font) {
+                labelStyles.put("font", labelFont);
+            }
+
+            Object color = null;
+            if (listView.isEnabled() && !disabled) {
+                if (selected) {
+                    if (listView.isFocused()) {
+                        color = listViewStyles.get("selectionColor");
+                    } else {
+                        color = listViewStyles.get("inactiveSelectionColor");
+                    }
+                } else {
+                    color = listViewStyles.get("color");
+                }
+            } else {
+                color = listViewStyles.get("disabledColor");
+            }
+
+            if (color instanceof Color) {
+                labelStyles.put("color", color);
+            }
+        }
+    }
+
+    public int getIconWidth() {
+        return imageView.getPreferredWidth(-1);
+    }
+
+    public void setIconWidth(int iconWidth) {
+        if (iconWidth == -1) {
+            throw new IllegalArgumentException();
+        }
+
+        imageView.setPreferredWidth(iconWidth);
+    }
+
+    public int getIconHeight() {
+        return imageView.getPreferredHeight(-1);
+    }
+
+    public void setIconHeight(int iconHeight) {
+        if (iconHeight == -1) {
+            throw new IllegalArgumentException();
+        }
+
+        imageView.setPreferredHeight(iconHeight);
+    }
+
+    public boolean getShowIcon() {
+        return imageView.isDisplayable();
+    }
+
+    public void setShowIcon(boolean showIcon) {
+        imageView.setDisplayable(showIcon);
+    }
+
+    /**
+     * Gets the bounds of the text that is rendered by this renderer.
+     *
+     * @return
+     * The bounds of the rendered text, or <tt>null</tt> if this renderer did
+     * not render any text.
+     */
+    public Bounds getTextBounds() {
+        return (label.isVisible() ? label.getBounds() : null);
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/MenuBarItemDataRenderer.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/MenuBarItemDataRenderer.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/MenuBarItemDataRenderer.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/MenuBarItemDataRenderer.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.content;
+
+import java.awt.Color;
+import java.awt.Font;
+
+import pivot.wtk.Button;
+import pivot.wtk.FlowPane;
+import pivot.wtk.HorizontalAlignment;
+import pivot.wtk.MenuBar;
+import pivot.wtk.VerticalAlignment;
+import pivot.wtk.ImageView;
+import pivot.wtk.Label;
+import pivot.wtk.media.Image;
+
+/**
+ * Default menu bar item data renderer.
+ *
+ * @author gbrown
+ */
+public class MenuBarItemDataRenderer extends FlowPane implements Button.DataRenderer {
+    protected ImageView imageView = new ImageView();
+    protected Label label = new Label();
+
+    public MenuBarItemDataRenderer() {
+        getStyles().put("horizontalAlignment", HorizontalAlignment.LEFT);
+        getStyles().put("verticalAlignment", VerticalAlignment.CENTER);
+        getStyles().put("padding", 3);
+
+        add(imageView);
+        add(label);
+
+        imageView.getStyles().put("backgroundColor", null);
+    }
+
+    @Override
+    public void setSize(int width, int height) {
+        super.setSize(width, height);
+
+        // Since this component doesn't have a parent, it won't be validated
+        // via layout; ensure that it is valid here
+        validate();
+    }
+
+    public void render(Object data, Button button, boolean highlighted) {
+        Image icon = null;
+        String text = null;
+
+        if (data instanceof ButtonData) {
+            ButtonData buttonData = (ButtonData)data;
+            icon = buttonData.getIcon();
+            text = buttonData.getText();
+        } else if (data instanceof Image) {
+            icon = (Image)data;
+        } else {
+            if (data != null) {
+                text = data.toString();
+            }
+        }
+
+        // Update the image view
+        MenuBar.Item menuBarItem = (MenuBar.Item)button;
+        MenuBar menuBar = menuBarItem.getMenuBar();
+
+        if (icon == null) {
+            imageView.setDisplayable(false);
+        } else {
+            imageView.setDisplayable(true);
+            imageView.setImage(icon);
+            imageView.getStyles().put("opacity", button.isEnabled() ? 1.0f : 0.5f);
+        }
+
+        // Update the label
+        if (text == null) {
+            label.setDisplayable(false);
+        } else {
+            label.setDisplayable(true);
+            Object font = menuBar.getStyles().get("font");
+            if (font instanceof Font) {
+                label.getStyles().put("font", font);
+            }
+
+            Object color;
+            if (button.isEnabled()) {
+                if (highlighted) {
+                    color = menuBar.getStyles().get("highlightColor");
+                } else {
+                    color = menuBar.getStyles().get("color");
+                }
+            } else {
+                color = menuBar.getStyles().get("disabledColor");
+            }
+
+            if (color instanceof Color) {
+                label.getStyles().put("color", color);
+            }
+
+            label.setText(text);
+        }
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/MenuButtonDataRenderer.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/MenuButtonDataRenderer.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/MenuButtonDataRenderer.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/content/MenuButtonDataRenderer.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed 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.content;
+
+import pivot.wtk.Button;
+import pivot.wtk.HorizontalAlignment;
+
+/**
+ * Default menu button data renderer.
+ *
+ * @author gbrown
+ */
+public class MenuButtonDataRenderer extends ButtonDataRenderer {
+    public MenuButtonDataRenderer() {
+        getStyles().put("horizontalAlignment", HorizontalAlignment.LEFT);
+    }
+
+    @Override
+    public void render(Object data, Button button, boolean highlight) {
+        if (data == null) {
+            data = "";
+        }
+
+        super.render(data, button, highlight);
+    }
+}