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/16 17:36:28 UTC

svn commit: r754936 [17/38] - in /incubator/pivot/tags/v1.0.1: ./ charts-test/ charts-test/src/ charts-test/src/pivot/ charts-test/src/pivot/charts/ charts-test/src/pivot/charts/test/ charts/ charts/lib/ charts/src/ charts/src/pivot/ charts/src/pivot/c...

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListView.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListView.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListView.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListView.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,1014 @@
+/*
+ * 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.util.Comparator;
+
+import pivot.collections.ArrayList;
+import pivot.collections.Dictionary;
+import pivot.collections.List;
+import pivot.collections.ListListener;
+import pivot.collections.Sequence;
+import pivot.serialization.JSONSerializer;
+import pivot.util.ListenerList;
+import pivot.util.Vote;
+import pivot.wtk.content.ListViewItemRenderer;
+
+/**
+ * Component that displays a sequence of items, optionally allowing a user
+ * to select or check one or more items.
+ * <p>
+ * TODO Add is/setItemChecked(); add events to ListViewItemListener.
+ * <p>
+ * TODO Add getCheckMode() method to allow a caller to set NONE, SINGLE, or
+ * MULTI; none disables checkboxes, single displays them as radio buttons, and
+ * multi displays them as checkboxes (we can already disable selection by
+ * setting the select mode to NONE).
+ * <p>
+ * TODO Add a showCheckmarks style to ListViewSkin.
+ *
+ * @author gbrown
+ */
+@ComponentInfo(icon="ListView.png")
+public class ListView extends Component {
+    /**
+     * Enumeration defining supported selection modes.
+     */
+    public enum SelectMode {
+        /**
+         * Selection is disabled.
+         */
+        NONE,
+
+        /**
+         * A single index may be selected at a time.
+         */
+        SINGLE,
+
+        /**
+         * Multiple indexes may be concurrently selected.
+         */
+        MULTI;
+
+        public static SelectMode decode(String value) {
+            return valueOf(value.toUpperCase());
+        }
+    }
+
+    /**
+     * List item renderer interface.
+     *
+     * @author gbrown
+     */
+    public interface ItemRenderer extends Renderer {
+        /**
+         * Prepares the renderer for layout or paint.
+         *
+         * @param item
+         * The item to render.
+         *
+         * @param listView
+         * The host component.
+         *
+         * @param selected
+         * If <tt>true</tt>, the renderer should present a selected state for
+         * the item.
+         *
+         * @param highlighted
+         * If <tt>true</tt>, the renderer should present a highlighted state
+         * for the item.
+         */
+        public void render(Object item, ListView listView, boolean selected,
+            boolean highlighted, boolean disabled);
+    }
+
+    /**
+     * List view skin interface. List view skins are required to implement
+     * this.
+     *
+     * @author gbrown
+     */
+    public interface Skin {
+        public int getItemAt(int y);
+        public Bounds getItemBounds(int index);
+    }
+
+    /**
+     * List event handler.
+     *
+     * @author gbrown
+     */
+    private class ListHandler implements ListListener<Object> {
+        public void itemInserted(List<Object> list, int index) {
+            int m = selectedRanges.insertIndex(index);
+
+            // Notify listeners that items were inserted
+            listViewItemListeners.itemInserted(ListView.this, index);
+
+            // If any spans were modified, notify listeners of selection change
+            if (m > 0) {
+                listViewSelectionListeners.selectionChanged(ListView.this);
+            }
+        }
+
+        public void itemsRemoved(List<Object> list, int index, Sequence<Object> items) {
+            if (items == null) {
+                // All items were removed; clear the selection and notify
+                // listeners
+                selectedRanges.clear();
+                listViewItemListeners.itemsRemoved(ListView.this, index, -1);
+                listViewSelectionListeners.selectionChanged(ListView.this);
+            } else {
+                int count = items.getLength();
+
+                int s = selectedRanges.getLength();
+                int m = selectedRanges.removeIndexes(index, count);
+
+                // Notify listeners that items were removed
+                listViewItemListeners.itemsRemoved(ListView.this, index, count);
+
+                // If any selection values were removed or any spans were modified,
+                // notify listeners of selection change
+                if (s != selectedRanges.getLength()
+                    || m > 0) {
+                    listViewSelectionListeners.selectionChanged(ListView.this);
+                }
+            }
+        }
+
+        public void itemUpdated(List<Object> list, int index, Object previousItem) {
+            listViewItemListeners.itemUpdated(ListView.this, index);
+        }
+
+        public void comparatorChanged(List<Object> list,
+            Comparator<Object> previousComparator) {
+            if (list.getComparator() != null) {
+                int s = selectedRanges.getLength();
+                selectedRanges.clear();
+
+                listViewItemListeners.itemsSorted(ListView.this);
+
+                if (s > 0) {
+                    listViewSelectionListeners.selectionChanged(ListView.this);
+                }
+            }
+        }
+    }
+
+    /**
+     * List view listener list.
+     *
+     * @author gbrown
+     */
+    private static class ListViewListenerList extends ListenerList<ListViewListener> implements
+            ListViewListener {
+        public void listDataChanged(ListView listView, List<?> previousListData) {
+            for (ListViewListener listener : this) {
+                listener.listDataChanged(listView, previousListData);
+            }
+        }
+
+        public void itemRendererChanged(ListView listView,
+            ListView.ItemRenderer previousItemRenderer) {
+            for (ListViewListener listener : this) {
+                listener.itemRendererChanged(listView, previousItemRenderer);
+            }
+        }
+
+        public void selectModeChanged(ListView listView,
+            ListView.SelectMode previousSelectMode) {
+            for (ListViewListener listener : this) {
+                listener.selectModeChanged(listView, previousSelectMode);
+            }
+        }
+
+        public void selectedValueKeyChanged(ListView listView, String previousSelectedValueKey) {
+            for (ListViewListener listener : this) {
+                listener.selectedValueKeyChanged(listView, previousSelectedValueKey);
+            }
+        }
+
+        public void selectedValuesKeyChanged(ListView listView, String previousSelectedValuesKey) {
+            for (ListViewListener listener : this) {
+                listener.selectedValuesKeyChanged(listView, previousSelectedValuesKey);
+            }
+        }
+    }
+
+    /**
+     * List view item listener list.
+     *
+     * @author gbrown
+     */
+    private static class ListViewItemListenerList extends ListenerList<ListViewItemListener>
+        implements ListViewItemListener {
+        public void itemInserted(ListView listView, int index) {
+            for (ListViewItemListener listener : this) {
+                listener.itemInserted(listView, index);
+            }
+        }
+
+        public void itemsRemoved(ListView listView, int index, int count) {
+            for (ListViewItemListener listener : this) {
+                listener.itemsRemoved(listView, index, count);
+            }
+        }
+
+        public void itemUpdated(ListView listView, int index) {
+            for (ListViewItemListener listener : this) {
+                listener.itemUpdated(listView, index);
+            }
+        }
+
+        public void itemsSorted(ListView listView) {
+            for (ListViewItemListener listener : this) {
+                listener.itemsSorted(listView);
+            }
+        }
+    }
+
+    /**
+     * List view item state listener list.
+     *
+     * @author gbrown
+     */
+    private static class ListViewItemStateListenerList extends ListenerList<ListViewItemStateListener>
+        implements ListViewItemStateListener {
+        public Vote previewItemDisabledChange(ListView listView, int index) {
+            Vote vote = Vote.APPROVE;
+
+            for (ListViewItemStateListener listener : this) {
+                vote = vote.tally(listener.previewItemDisabledChange(listView, index));
+            }
+
+            return vote;
+        }
+
+        public void itemDisabledChangeVetoed(ListView listView, int index, Vote reason) {
+            for (ListViewItemStateListener listener : this) {
+                listener.itemDisabledChangeVetoed(listView, index, reason);
+            }
+        }
+
+        public void itemDisabledChanged(ListView listView, int index) {
+            for (ListViewItemStateListener listener : this) {
+                listener.itemDisabledChanged(listView, index);
+            }
+        }
+    }
+
+    /**
+     * List view selection listener list.
+     *
+     * @author gbrown
+     */
+    private static class ListViewSelectionListenerList extends ListenerList<ListViewSelectionListener>
+        implements ListViewSelectionListener {
+        public void selectionChanged(ListView listView) {
+            for (ListViewSelectionListener listener : this) {
+                listener.selectionChanged(listView);
+            }
+        }
+    }
+
+    /**
+     * List view selection detail listener list.
+     *
+     * @author gbrown
+     */
+    private static class ListViewSelectionDetailListenerList extends ListenerList<ListViewSelectionDetailListener>
+        implements ListViewSelectionDetailListener {
+        public void selectedRangeAdded(ListView listView, int rangeStart, int rangeEnd) {
+            for (ListViewSelectionDetailListener listener : this) {
+                listener.selectedRangeAdded(listView, rangeStart, rangeEnd);
+            }
+        }
+
+        public void selectedRangeRemoved(ListView listView, int rangeStart, int rangeEnd) {
+            for (ListViewSelectionDetailListener listener : this) {
+                listener.selectedRangeRemoved(listView, rangeStart, rangeEnd);
+            }
+        }
+
+        public void selectionReset(ListView listView, Sequence<Span> previousSelection) {
+            for (ListViewSelectionDetailListener listener : this) {
+                listener.selectionReset(listView, previousSelection);
+            }
+        }
+    }
+
+    private List<?> listData = null;
+    private ListHandler listDataHandler = new ListHandler();
+
+    private ItemRenderer itemRenderer = null;
+
+    private SpanSequence selectedRanges = new SpanSequence();
+    private SelectMode selectMode = SelectMode.SINGLE;
+
+    private ArrayList<Integer> disabledIndexes = new ArrayList<Integer>();
+
+    private String selectedValueKey = null;
+    private String selectedValuesKey = null;
+
+    private ListViewListenerList listViewListeners = new ListViewListenerList();
+    private ListViewItemListenerList listViewItemListeners = new ListViewItemListenerList();
+    private ListViewItemStateListenerList listViewItemStateListeners =
+        new ListViewItemStateListenerList();
+    private ListViewSelectionListenerList listViewSelectionListeners =
+        new ListViewSelectionListenerList();
+    private ListViewSelectionDetailListenerList listViewSelectionDetailListeners =
+        new ListViewSelectionDetailListenerList();
+
+    private static final ItemRenderer DEFAULT_ITEM_RENDERER = new ListViewItemRenderer();
+
+    /**
+     * Creates a list view populated with an empty array list.
+     */
+    public ListView() {
+        this(new ArrayList<Object>());
+    }
+
+    /**
+     * Creates a list view populated with the given list data.
+     *
+     * @param listData
+     */
+    public ListView(List<?> listData) {
+        setItemRenderer(DEFAULT_ITEM_RENDERER);
+        setListData(listData);
+
+        installSkin(ListView.class);
+    }
+
+    /**
+     * Returns the list data.
+     *
+     * @return
+     * The data currently presented by the list view.
+     */
+    public List<?> getListData() {
+        return this.listData;
+    }
+
+    /**
+     * Sets the list data.
+     *
+     * @param listData
+     * The data to be presented by the list view.
+     */
+    @SuppressWarnings("unchecked")
+    public void setListData(List<?> listData) {
+        if (listData == null) {
+            throw new IllegalArgumentException("listData is null.");
+        }
+
+        List<?> previousListData = this.listData;
+
+        if (previousListData != listData) {
+            if (previousListData != null) {
+                // Clear any existing selection
+                clearSelection();
+
+                ((List<Object>)previousListData).getListListeners().remove(listDataHandler);
+            }
+
+            ((List<Object>)listData).getListListeners().add(listDataHandler);
+
+            // Update the list data and fire change event
+            this.listData = listData;
+            listViewListeners.listDataChanged(this, previousListData);
+        }
+    }
+
+    /**
+     * Sets the list data.
+     *
+     * @param listData
+     * The data to be presented by the list view as a JSON array.
+     */
+    public void setListData(String listData) {
+        if (listData == null) {
+            throw new IllegalArgumentException("listData is null.");
+        }
+
+        setListData(JSONSerializer.parseList(listData));
+    }
+
+    @Override
+    protected void setSkin(pivot.wtk.Skin skin) {
+        if (!(skin instanceof ListView.Skin)) {
+            throw new IllegalArgumentException("Skin class must implement "
+                + ListView.Skin.class.getName());
+        }
+
+        super.setSkin(skin);
+    }
+
+    /**
+     * Returns the item renderer used for items in this list.
+     */
+    public ItemRenderer getItemRenderer() {
+        return itemRenderer;
+    }
+
+    /**
+     * Sets the item renderer to be used for items in this list.
+     *
+     * @param itemRenderer
+     * The item renderer for the list.
+     */
+    public void setItemRenderer(ItemRenderer itemRenderer) {
+        if (itemRenderer == null) {
+            throw new IllegalArgumentException("itemRenderer is null.");
+        }
+
+        ItemRenderer previousItemRenderer = this.itemRenderer;
+
+        if (previousItemRenderer != itemRenderer) {
+            this.itemRenderer = itemRenderer;
+            listViewListeners.itemRendererChanged(this, previousItemRenderer);
+        }
+    }
+
+    /**
+     * When in single-select mode, returns the currently selected index.
+     *
+     * @return
+     * The currently selected index.
+     */
+    public int getSelectedIndex() {
+        if (selectMode != SelectMode.SINGLE) {
+            throw new IllegalStateException("List view is not in single-select mode.");
+        }
+
+        return (selectedRanges.getLength() == 0) ? -1 : selectedRanges.get(0).getStart();
+    }
+
+    /**
+     * Sets the selection to a single index.
+     *
+     * @param index
+     * The index to select, or <tt>-1</tt> to clear the selection.
+     */
+    public void setSelectedIndex(int index) {
+        ArrayList<Span> selectedRanges = new ArrayList<Span>();
+
+        if (index >= 0) {
+            selectedRanges.add(new Span(index, index));
+        }
+
+        setSelectedRanges(selectedRanges);
+    }
+
+    /**
+     * Returns the list's current selection.
+     */
+    public Sequence<Span> getSelectedRanges() {
+        // Return a copy of the selection list (including copies of the
+        // list contents)
+        ArrayList<Span> selection = new ArrayList<Span>();
+
+        for (int i = 0, n = selectedRanges.getLength(); i < n; i++) {
+            selection.add(new Span(selectedRanges.get(i)));
+        }
+
+        return selection;
+    }
+
+    /**
+     * Sets the selection to the given span sequence. Any overlapping or
+     * connecting spans will be consolidated, and the resulting selection will
+     * be sorted in ascending order.
+     *
+     * @param selectedRanges
+     */
+    public void setSelectedRanges(Sequence<Span> selectedRanges) {
+        if (selectedRanges == null) {
+            throw new IllegalArgumentException("selectedRanges is null.");
+        }
+
+        if (selectMode == SelectMode.NONE) {
+            throw new IllegalArgumentException("Selection is not enabled.");
+        }
+
+        if (selectMode == SelectMode.SINGLE) {
+            int n = selectedRanges.getLength();
+
+            if (n > 1) {
+                throw new IllegalArgumentException("Selection length is greater than 1.");
+            } else {
+                if (n > 0) {
+                    Span selectedRange = selectedRanges.get(0);
+
+                    if (selectedRange.getLength() > 1) {
+                        throw new IllegalArgumentException("Selected range length is greater than 1.");
+                    }
+                }
+            }
+        }
+
+        // Update the selection
+        SpanSequence ranges = new SpanSequence();
+
+        for (int i = 0, n = selectedRanges.getLength(); i < n; i++) {
+            Span range = selectedRanges.get(i);
+
+            if (range == null) {
+                throw new IllegalArgumentException("range is null.");
+            }
+
+            if (range.getStart() < 0 || range.getEnd() >= listData.getLength()) {
+                throw new IndexOutOfBoundsException();
+            }
+
+            ranges.add(range);
+        }
+
+        SpanSequence previousSelectedRanges = this.selectedRanges;
+        this.selectedRanges = ranges;
+
+        // Notify listeners
+        listViewSelectionDetailListeners.selectionReset(this, previousSelectedRanges);
+        listViewSelectionListeners.selectionChanged(this);
+    }
+
+    /**
+     * Returns the first selected index.
+     *
+     * @return
+     * The first selected index, or <tt>-1</tt> if nothing is selected.
+     */
+    public int getFirstSelectedIndex() {
+        return (selectedRanges.getLength() > 0) ?
+            selectedRanges.get(0).getStart() : -1;
+    }
+
+    /**
+     * Returns the last selected index.
+     *
+     * @return
+     * The last selected index, or <tt>-1</tt> if nothing is selected.
+     */
+    public int getLastSelectedIndex() {
+        return (selectedRanges.getLength() > 0) ?
+            selectedRanges.get(selectedRanges.getLength() - 1).getEnd() : -1;
+    }
+
+    /**
+     * Adds a single index to the selection.
+     *
+     * @param index
+     * The index to add.
+     */
+    public void addSelectedIndex(int index) {
+        addSelectedRange(index, index);
+    }
+
+    /**
+     * Adds a range of indexes to the selection.
+     *
+     * @param rangeStart
+     * The first index in the range.
+     *
+     * @param rangeEnd
+     * The last index in the range.
+     */
+    public void addSelectedRange(int rangeStart, int rangeEnd) {
+        addSelectedRange(new Span(rangeStart, rangeEnd));
+    }
+
+    /**
+     * Adds a range of indexes to the selection.
+     *
+     * @param range
+     * The range to add.
+     */
+    public void addSelectedRange(Span range) {
+        if (selectMode != SelectMode.MULTI) {
+            throw new IllegalStateException("List view is not in multi-select mode.");
+        }
+
+        if (range == null) {
+            throw new IllegalArgumentException("range is null.");
+        }
+
+        if (range.getStart() < 0 || range.getEnd() >= listData.getLength()) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        selectedRanges.add(range);
+
+        listViewSelectionDetailListeners.selectedRangeAdded(this,
+            range.getStart(), range.getEnd());
+        listViewSelectionListeners.selectionChanged(this);
+    }
+
+    /**
+     * Removes a single index from the selection.
+     *
+     * @param index
+     * The index to remove.
+     */
+    public void removeSelectedIndex(int index) {
+        removeSelectedRange(index, index);
+    }
+
+    /**
+     * Removes a range of indexes from the selection.
+     *
+     * @param rangeStart
+     * The start of the range to remove.
+     *
+     * @param rangeEnd
+     * The end of the range to remove.
+     */
+    public void removeSelectedRange(int rangeStart, int rangeEnd) {
+        removeSelectedRange(new Span(rangeStart, rangeEnd));
+    }
+
+    /**
+     * Removes a range of indexes from the selection.
+     *
+     * @param range
+     * The range to remove.
+     */
+    public void removeSelectedRange(Span range) {
+        if (selectMode != SelectMode.MULTI) {
+            throw new IllegalStateException("List view is not in multi-select mode.");
+        }
+
+        if (range == null) {
+            throw new IllegalArgumentException("range is null.");
+        }
+
+        if (range.getStart() < 0 || range.getEnd() >= listData.getLength()) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        selectedRanges.remove(range);
+
+        listViewSelectionDetailListeners.selectedRangeRemoved(this,
+            range.getStart(), range.getEnd());
+        listViewSelectionListeners.selectionChanged(this);
+    }
+
+    /**
+     * Clears the selection.
+     */
+    public void clearSelection() {
+        if (selectedRanges.getLength() > 0) {
+            SpanSequence previousSelectedSpans = this.selectedRanges;
+            selectedRanges = new SpanSequence();
+
+            listViewSelectionDetailListeners.selectionReset(this, previousSelectedSpans);
+            listViewSelectionListeners.selectionChanged(this);
+        }
+    }
+
+    /**
+     * Returns the selection state of a given index.
+     *
+     * @param index
+     * The index whose selection state is to be tested.
+     *
+     * @return <tt>true</tt> if the index is selected; <tt>false</tt>,
+     * otherwise.
+     */
+    public boolean isIndexSelected(int index) {
+        return isRangeSelected(index, index);
+    }
+
+    /**
+     * Returns the selection state of a given range.
+     *
+     * @param rangeStart
+     * The first index in the range.
+     *
+     * @param rangeEnd
+     * The last index in the range.
+     *
+     * @return <tt>true</tt> if the entire range is selected; <tt>false</tt>,
+     * otherwise.
+     */
+    public boolean isRangeSelected(int rangeStart, int rangeEnd) {
+        return isRangeSelected(new Span(rangeStart, rangeEnd));
+    }
+
+    /**
+     * Returns the selection state of a given range.
+     *
+     * @param range
+     * The range whose selection state is to be tested.
+     *
+     * @return <tt>true</tt> if the entire range is selected; <tt>false</tt>,
+     * otherwise.
+     */
+    public boolean isRangeSelected(Span range) {
+        boolean selected = false;
+
+        if (range == null) {
+            throw new IllegalArgumentException("range is null.");
+        }
+
+        if (range.getStart() < 0 || range.getEnd() >= listData.getLength()) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        // Locate the span in the selection
+        int i = selectedRanges.indexOf(range);
+
+        // If the selected span contains the given span, it is considered
+        // selected
+        if (i >= 0) {
+            Span selectedSpan = selectedRanges.get(i);
+            selected = selectedSpan.contains(range);
+        }
+
+        return selected;
+    }
+
+    public Object getSelectedValue() {
+        int index = getSelectedIndex();
+        Object value = null;
+
+        if (index >= 0) {
+            value = listData.get(index);
+        }
+
+        return value;
+    }
+
+    @SuppressWarnings("unchecked")
+    public void setSelectedValue(Object value) {
+        if (value == null) {
+            throw new IllegalArgumentException("value is null");
+        }
+
+        int index = ((List<Object>)listData).indexOf(value);
+        if (index == -1) {
+            throw new IllegalArgumentException("\"" + value + "\" is not a valid selection.");
+        }
+
+        setSelectedIndex(index);
+    }
+
+    public Sequence<Object> getSelectedValues() {
+        ArrayList<Object> values = new ArrayList<Object>();
+
+        for (int i = 0, n = selectedRanges.getLength(); i < n; i++) {
+            Span span = selectedRanges.get(i);
+
+            for (int index = span.getStart(), end = span.getEnd(); index <= end; index++) {
+                Object value = listData.get(index);
+                values.add(value);
+            }
+        }
+
+        return values;
+    }
+
+    @SuppressWarnings("unchecked")
+    public void setSelectedValues(Sequence<Object> values) {
+        ArrayList<Span> selectedRanges = new ArrayList<Span>();
+
+        for (int i = 0, n = values.getLength(); i < n; i++) {
+            Object value = values.get(i);
+            if (value == null) {
+                throw new IllegalArgumentException("value is null");
+            }
+
+            int index = ((List<Object>)listData).indexOf(value);
+            if (index == -1) {
+                throw new IllegalArgumentException("\"" + value + "\" is not a valid selection.");
+            }
+
+            selectedRanges.add(new Span(index, index));
+        }
+
+        setSelectedRanges(selectedRanges);
+    }
+
+    /**
+     * Returns the current selection mode.
+     */
+    public SelectMode getSelectMode() {
+        return selectMode;
+    }
+
+    /**
+     * Sets the selection mode. Clears the selection if the mode has changed.
+     *
+     * @param selectMode
+     * The new selection mode.
+     */
+    public void setSelectMode(SelectMode selectMode) {
+        if (selectMode == null) {
+            throw new IllegalArgumentException("selectMode is null.");
+        }
+
+        SelectMode previousSelectMode = this.selectMode;
+
+        if (previousSelectMode != selectMode) {
+            // Clear any current selection
+            clearSelection();
+
+            // Update the selection mode
+            this.selectMode = selectMode;
+
+            // Fire select mode change event
+            listViewListeners.selectModeChanged(this, previousSelectMode);
+        }
+    }
+
+    public void setSelectMode(String selectMode) {
+        if (selectMode == null) {
+            throw new IllegalArgumentException("selectMode is null.");
+        }
+
+        setSelectMode(SelectMode.decode(selectMode));
+    }
+
+    /**
+     * Returns the disabled state of a given item.
+     *
+     * @param index
+     * The index of the item whose disabled state is to be tested.
+     *
+     * @return
+     * <tt>true</tt> if the item is disabled; <tt>false</tt>,
+     * otherwise.
+     */
+    public boolean isItemDisabled(int index) {
+        return (Sequence.Search.binarySearch(disabledIndexes, index) >= 0);
+    }
+
+    /**
+     * Sets the disabled state of an item.
+     *
+     * @param index
+     * The index of the item whose disabled state is to be set.
+     *
+     * @param disabled
+     * <tt>true</tt> to disable the item; <tt>false</tt>, otherwise.
+     */
+    public void setItemDisabled(int index, boolean disabled) {
+        int i = Sequence.Search.binarySearch(disabledIndexes, index);
+
+        if ((i < 0 && disabled)
+            || (i >= 0 && !disabled)) {
+            Vote vote = listViewItemStateListeners.previewItemDisabledChange(this, index);
+
+            if (vote.isApproved()) {
+                if (disabled) {
+                    disabledIndexes.insert(index, -(i + 1));
+                } else {
+                    disabledIndexes.remove(i, 1);
+                }
+
+                listViewItemStateListeners.itemDisabledChanged(this, index);
+            } else {
+                listViewItemStateListeners.itemDisabledChangeVetoed(this, index, vote);
+            }
+        }
+    }
+
+    public Sequence<Integer> getDisabledIndexes() {
+        ArrayList<Integer> disabledIndexes = new ArrayList<Integer>();
+
+        for (int i = 0, n = this.disabledIndexes.getLength(); i < n; i++) {
+            disabledIndexes.add(this.disabledIndexes.get(i));
+        }
+
+        return disabledIndexes;
+    }
+
+    public String getSelectedValueKey() {
+        return selectedValueKey;
+    }
+
+    public void setSelectedValueKey(String selectedValueKey) {
+        String previousSelectedValueKey = this.selectedValueKey;
+        this.selectedValueKey = selectedValueKey;
+        listViewListeners.selectedValueKeyChanged(this, previousSelectedValueKey);
+    }
+
+    public String getSelectedValuesKey() {
+        return selectedValuesKey;
+    }
+
+    public void setSelectedValuesKey(String selectedValuesKey) {
+        String previousSelectedValuesKey = this.selectedValuesKey;
+        this.selectedValuesKey = selectedValuesKey;
+        listViewListeners.selectedValuesKeyChanged(this, previousSelectedValuesKey);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void load(Dictionary<String, Object> context) {
+        if (selectedValueKey != null
+            && context.containsKey(selectedValueKey)) {
+            Object value = context.get(selectedValueKey);
+            setSelectedValue(value);
+        }
+
+        if (selectedValuesKey != null
+            && context.containsKey(selectedValuesKey)) {
+            Sequence<Object> values = (Sequence<Object>)context.get(selectedValuesKey);
+            setSelectedValues(values);
+        }
+
+        // TODO Add support for checked values
+    }
+
+    @Override
+    public void store(Dictionary<String, Object> context) {
+        if (selectedValueKey != null) {
+            Object value = getSelectedValue();
+            context.put(selectedValueKey, value);
+        }
+
+        if (selectedValuesKey != null) {
+            Sequence<Object> values = getSelectedValues();
+            context.put(selectedValuesKey, values);
+        }
+
+        // TODO Add support for checked values
+    }
+
+    /**
+     * Returns the index of the item at a given location.
+     *
+     * @param y
+     * The y-coordinate of the item to identify.
+     *
+     * @return
+     * The item index, or <tt>-1</tt> if there is no item at the given
+     * y-coordinate.
+     */
+    public int getItemAt(int y) {
+        ListView.Skin listViewSkin = (ListView.Skin)getSkin();
+        return listViewSkin.getItemAt(y);
+    }
+
+    /**
+     * Returns the bounding area of a given item.
+     *
+     * @param index
+     * The item index.
+     *
+     * @return
+     * The bounding area of the item.
+     */
+    public Bounds getItemBounds(int index) {
+        ListView.Skin listViewSkin = (ListView.Skin)getSkin();
+        return listViewSkin.getItemBounds(index);
+    }
+
+    /**
+     * Returns the list view listener list.
+     */
+    public ListenerList<ListViewListener> getListViewListeners() {
+        return listViewListeners;
+    }
+
+    /**
+     * Returns the list view item listener list.
+     */
+    public ListenerList<ListViewItemListener> getListViewItemListeners() {
+        return listViewItemListeners;
+    }
+
+    /**
+     * Returns the list view item state listener list.
+     */
+    public ListenerList<ListViewItemStateListener> getListViewItemStateListeners() {
+        return listViewItemStateListeners;
+    }
+
+    /**
+     * Returns the list view selection listener list.
+     */
+    public ListenerList<ListViewSelectionListener> getListViewSelectionListeners() {
+        return listViewSelectionListeners;
+    }
+
+    /**
+     * Returns the list view selection detail listener list.
+     */
+    public ListenerList<ListViewSelectionDetailListener> getListViewSelectionDetailListeners() {
+        return listViewSelectionDetailListeners;
+    }
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListView.png
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListView.png?rev=754936&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListView.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewItemListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewItemListener.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewItemListener.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewItemListener.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,68 @@
+/*
+ * 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;
+
+/**
+ * List view item listener interface.
+ *
+ * @author gbrown
+ */
+public interface ListViewItemListener {
+    /**
+     * Called when an item has been inserted into the list view.
+     *
+     * @param listView
+     * The source of the event.
+     *
+     * @param index
+     * The index of the item that was inserted.
+     */
+    public void itemInserted(ListView listView, int index);
+
+    /**
+     * Called when items have been removed from the list view.
+     *
+     * @param listView
+     * The source of the event.
+     *
+     * @param index
+     * The first index affected by the event.
+     *
+     * @param count
+     * The number of items that were removed, or <tt>-1</tt> if all items
+     * were removed.
+     */
+    public void itemsRemoved(ListView listView, int index, int count);
+
+    /**
+     * Called when an item in the list view has been updated.
+     *
+     * @param listView
+     * The source of the event.
+     *
+     * @param index
+     * The first index affected by the event.
+     */
+    public void itemUpdated(ListView listView, int index);
+
+    /**
+     * Called when the items in a list have been sorted.
+     *
+     * @param listView
+     * The source of the event.
+     */
+    public void itemsSorted(ListView listView);
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewItemStateListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewItemStateListener.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewItemStateListener.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewItemStateListener.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,51 @@
+/*
+ * 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;
+
+/**
+ * List view item state listener interface.
+ *
+ * @author gbrown
+ * @author tvolkert
+ */
+public interface ListViewItemStateListener {
+    /**
+     * Called to preview an item disabled state change.
+     *
+     * @param listView
+     * @param index
+     */
+    public Vote previewItemDisabledChange(ListView listView, int index);
+
+    /**
+     * Called when an item disabled change event has been cancelled.
+     *
+     * @param listView
+     * @param index
+     * @param reason
+     */
+    public void itemDisabledChangeVetoed(ListView listView, int index, Vote reason);
+
+    /**
+     * Called when an item's disabled state has changed.
+     *
+     * @param listView
+     * @param index
+     */
+    public void itemDisabledChanged(ListView listView, int index);
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewListener.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewListener.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewListener.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,65 @@
+/*
+ * 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.collections.List;
+
+/**
+ * List view listener interface.
+ *
+ * @author gbrown
+ */
+public interface ListViewListener {
+    /**
+     * Called when a list view's list data has changed.
+     *
+     * @param listView
+     * @param previousListData
+     */
+    public void listDataChanged(ListView listView, List<?> previousListData);
+
+    /**
+     * Called when a list view's item renderer has changed.
+     *
+     * @param listView
+     * @param previousItemRenderer
+     */
+    public void itemRendererChanged(ListView listView, ListView.ItemRenderer previousItemRenderer);
+
+    /**
+     * Called when a list view's select mode has changed.
+     *
+     * @param listView
+     * @param previousSelectMode
+     */
+    public void selectModeChanged(ListView listView, ListView.SelectMode previousSelectMode);
+
+    /**
+     * Called when a list view's selected value key has changed.
+     *
+     * @param listView
+     * @param previousSelectedIndexKey
+     */
+    public void selectedValueKeyChanged(ListView listView, String previousSelectedIndexKey);
+
+    /**
+     * Called when a list view's selected values key has changed.
+     *
+     * @param listView
+     * @param previousSelectedValuesKey
+     */
+    public void selectedValuesKeyChanged(ListView listView, String previousSelectedValuesKey);
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewSelectionDetailListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewSelectionDetailListener.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewSelectionDetailListener.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewSelectionDetailListener.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,51 @@
+/*
+ * 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.collections.Sequence;
+
+/**
+ * List view selection detail listener interface.
+ *
+ * @author gbrown
+ */
+public interface ListViewSelectionDetailListener {
+    /**
+     * Called when a range has been added to a list view's selection.
+     *
+     * @param listView
+     * @param rangeStart
+     * @param rangeEnd
+     */
+    public void selectedRangeAdded(ListView listView, int rangeStart, int rangeEnd);
+
+    /**
+     * Called when a range has been removed from a list view's selection.
+     *
+     * @param listView
+     * @param rangeStart
+     * @param rangeEnd
+     */
+    public void selectedRangeRemoved(ListView listView, int rangeStart, int rangeEnd);
+
+    /**
+     * Called when a list view's selection state has been reset.
+     *
+     * @param listView
+     * @param previousSelectedRanges
+     */
+    public void selectionReset(ListView listView, Sequence<Span> previousSelectedRanges);
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewSelectionListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewSelectionListener.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewSelectionListener.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/ListViewSelectionListener.java Mon Mar 16 16:36:10 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;
+
+/**
+ * List view selection listener interface.
+ *
+ * @author gbrown
+ */
+public interface ListViewSelectionListener {
+    /**
+     * Called when a list view's selection state has changed.
+     *
+     * @param listView
+     */
+    public void selectionChanged(ListView listView);
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/Menu.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/Menu.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/Menu.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/Menu.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,398 @@
+/*
+ * 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.util.Iterator;
+
+import pivot.collections.ArrayList;
+import pivot.collections.Sequence;
+import pivot.util.ImmutableIterator;
+import pivot.util.ListenerList;
+import pivot.wtk.content.MenuItemDataRenderer;
+
+/**
+ * Component that presents a cascading menu.
+ *
+ * @author gbrown
+ */
+@ComponentInfo(icon="Menu.png")
+public class Menu extends Container {
+    /**
+     * Component representing a menu item.
+     *
+     * @author gbrown
+     */
+    public static class Item extends Button {
+        private class ItemListenerList extends ListenerList<ItemListener>
+            implements ItemListener {
+            public void menuChanged(Item item, Menu previousMenu) {
+                for (ItemListener listener : this) {
+                    listener.menuChanged(item, previousMenu);
+                }
+            }
+        }
+
+        private Section section = null;
+        private Menu menu = null;
+
+        private ItemListenerList itemListeners = new ItemListenerList();
+
+        private static final Button.DataRenderer DEFAULT_DATA_RENDERER = new MenuItemDataRenderer();
+
+        public Item() {
+            this(null);
+        }
+
+        public Item(Object buttonData) {
+            super(buttonData);
+
+            setDataRenderer(DEFAULT_DATA_RENDERER);
+            installSkin(Item.class);
+        }
+
+        @Override
+        protected void setParent(Container parent) {
+            if (parent != null
+                && !(parent instanceof Menu)) {
+                throw new IllegalArgumentException("Parent must be an instance of "
+                    + Menu.class.getName());
+            }
+
+            super.setParent(parent);
+        }
+
+        public Section getSection() {
+            return section;
+        }
+
+        private void setSection(Section section) {
+            this.section = section;
+        }
+
+        public Menu getMenu() {
+            return menu;
+        }
+
+        public void setMenu(Menu menu) {
+            if (menu != null
+                && menu.getItem() != null) {
+                throw new IllegalArgumentException("menu already belongs to an item.");
+            }
+
+            Menu previousMenu = this.menu;
+
+            if (previousMenu != menu) {
+                if (previousMenu != null) {
+                    previousMenu.setItem(null);
+                }
+
+                if (menu != null) {
+                    menu.setItem(this);
+                }
+
+                this.menu = menu;
+
+                itemListeners.menuChanged(this, previousMenu);
+            }
+        }
+
+        @Override
+        public void setTriState(boolean triState) {
+            throw new UnsupportedOperationException("Menu items can't be tri-state.");
+        }
+
+        @Override
+        public void press() {
+            if (isToggleButton()) {
+                setSelected(getGroup() == null ? !isSelected() : true);
+            }
+
+            super.press();
+
+            if (menu == null) {
+                Item item = this;
+                while (item != null
+                    && item.section != null
+                    && item.section.menu != null) {
+                    item.section.menu.menuItemSelectionListeners.itemSelected(this);
+                    item = item.section.menu.item;
+                }
+            }
+        }
+
+        public ListenerList<ItemListener> getItemListeners() {
+            return itemListeners;
+        }
+    }
+
+    /**
+     * Item listener interface.
+     *
+     * @author gbrown
+     */
+    public interface ItemListener {
+        public void menuChanged(Item item, Menu previousMenu);
+    }
+
+    /**
+     * Class representing a menu section. A section is a grouping of menu
+     * items within a menu.
+     *
+     * @author gbrown
+     */
+    public static class Section implements Sequence<Item>, Iterable<Item> {
+        private Menu menu = null;
+        private ArrayList<Item> items = new ArrayList<Item>();
+
+        public Menu getMenu() {
+            return menu;
+        }
+
+        private void setMenu(Menu menu) {
+            this.menu = menu;
+        }
+
+        public int add(Item item) {
+            int index = getLength();
+            insert(item, index);
+
+            return index;
+        }
+
+        public void insert(Item item, int index) {
+            if (item.getSection() != null) {
+                throw new IllegalArgumentException("item already has a section.");
+            }
+
+            items.insert(item, index);
+            item.setSection(this);
+
+            if (menu != null) {
+                menu.add(item);
+                menu.menuListeners.itemInserted(this, index);
+            }
+        }
+
+        public Item update(int index, Item item) {
+            throw new UnsupportedOperationException();
+        }
+
+        public int remove(Item item) {
+            int index = items.indexOf(item);
+            if (index != -1) {
+                remove(index, 1);
+            }
+
+            return index;
+        }
+
+        public Sequence<Item> remove(int index, int count) {
+            Sequence<Item> removed = items.remove(index, count);
+
+            for (int i = 0, n = removed.getLength(); i < n; i++) {
+                Item item = removed.get(i);
+
+                item.setSection(null);
+
+                if (menu != null) {
+                    menu.remove(item);
+                }
+            }
+
+            if (menu != null) {
+                menu.menuListeners.itemsRemoved(this, index, removed);
+            }
+
+            return removed;
+        }
+
+        public Item get(int index) {
+            return items.get(index);
+        }
+
+        public int indexOf(Item item) {
+            return items.indexOf(item);
+        }
+
+        public int getLength() {
+            return items.getLength();
+        }
+
+        public Iterator<Item> iterator() {
+            return new ImmutableIterator<Item>(items.iterator());
+        }
+    }
+
+    /**
+     * Section sequence implementation.
+     *
+     * @author gbrown
+     */
+    public final class SectionSequence implements Sequence<Section>, Iterable<Section> {
+        private SectionSequence() {
+        }
+
+        public int add(Section section) {
+            int index = getLength();
+            insert(section, index);
+
+            return index;
+        }
+
+        public void insert(Section section, int index) {
+            if (section.getMenu() != null) {
+                throw new IllegalArgumentException("section already has a menu.");
+            }
+
+            sections.insert(section, index);
+            section.setMenu(Menu.this);
+
+            for (int i = 0, n = section.getLength(); i < n; i++) {
+                Menu.this.add(section.get(i));
+            }
+
+            menuListeners.sectionInserted(Menu.this, index);
+        }
+
+        public Section update(int index, Section section) {
+            throw new UnsupportedOperationException();
+        }
+
+        public int remove(Section section) {
+            int index = sections.indexOf(section);
+            if (index != -1) {
+                remove(index, 1);
+            }
+
+            return index;
+        }
+
+        public Sequence<Section> remove(int index, int count) {
+            Sequence<Section> removed = sections.remove(index, count);
+
+            for (int i = 0, n = removed.getLength(); i < n; i++) {
+                Section section = removed.get(i);
+
+                section.setMenu(null);
+
+                for (Item item : section) {
+                    Menu.this.remove(item);
+                }
+            }
+
+            menuListeners.sectionsRemoved(Menu.this, index, removed);
+
+            return removed;
+        }
+
+        public Section get(int index) {
+            return sections.get(index);
+        }
+
+        public int indexOf(Section item) {
+            return sections.indexOf(item);
+        }
+
+        public int getLength() {
+            return sections.getLength();
+        }
+
+        public Iterator<Section> iterator() {
+            return new ImmutableIterator<Section>(sections.iterator());
+        }
+    }
+
+    private static class MenuListenerList extends ListenerList<MenuListener>
+        implements MenuListener {
+        public void sectionInserted(Menu menu, int index) {
+            for (MenuListener listener : this) {
+                listener.sectionInserted(menu, index);
+            }
+        }
+
+        public void sectionsRemoved(Menu menu, int index, Sequence<Section> removed) {
+            for (MenuListener listener : this) {
+                listener.sectionsRemoved(menu, index, removed);
+            }
+        }
+
+        public void itemInserted(Menu.Section section, int index) {
+            for (MenuListener listener : this) {
+                listener.itemInserted(section, index);
+            }
+        }
+
+        public void itemsRemoved(Menu.Section section, int index, Sequence<Item> removed) {
+            for (MenuListener listener : this) {
+                listener.itemsRemoved(section, index, removed);
+            }
+        }
+    }
+
+    private static class MenuItemSelectionListenerList extends ListenerList<MenuItemSelectionListener>
+        implements MenuItemSelectionListener {
+        public void itemSelected(Menu.Item menuItem) {
+            for (MenuItemSelectionListener listener : this) {
+                listener.itemSelected(menuItem);
+            }
+        }
+    }
+
+    private Item item = null;
+    private ArrayList<Section> sections = new ArrayList<Section>();
+    private SectionSequence sectionSequence = new SectionSequence();
+
+    private MenuListenerList menuListeners = new MenuListenerList();
+    private MenuItemSelectionListenerList menuItemSelectionListeners = new MenuItemSelectionListenerList();
+
+    public Menu() {
+        installSkin(Menu.class);
+    }
+
+    public Item getItem() {
+        return item;
+    }
+
+    private void setItem(Item item) {
+        this.item = item;
+    }
+
+    public SectionSequence getSections() {
+        return sectionSequence;
+    }
+
+    @Override
+    public Sequence<Component> remove(int index, int count) {
+        for (int i = index, n = index + count; i < n; i++) {
+            Item item = (Item)get(i);
+
+            if (item.getSection() != null) {
+                throw new UnsupportedOperationException();
+            }
+        }
+
+        // Call the base method to remove the components
+        return super.remove(index, count);
+    }
+
+    public ListenerList<MenuListener> getMenuListeners() {
+        return menuListeners;
+    }
+
+    public ListenerList<MenuItemSelectionListener> getMenuItemSelectionListeners() {
+        return menuItemSelectionListeners;
+    }
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/Menu.png
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/Menu.png?rev=754936&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/Menu.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuBar.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuBar.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuBar.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuBar.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,259 @@
+/*
+ * 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.util.Iterator;
+
+import pivot.collections.ArrayList;
+import pivot.collections.Sequence;
+import pivot.util.ImmutableIterator;
+import pivot.util.ListenerList;
+import pivot.wtk.content.MenuBarItemDataRenderer;
+
+/**
+ * Component representing a horizontal menu bar.
+ *
+ * @author gbrown
+ */
+@ComponentInfo(icon="MenuBar.png")
+public class MenuBar extends Container {
+    /**
+     * Component representing a menu bar item.
+     *
+     * @author gbrown
+     */
+    public static class Item extends Button {
+        private class ItemListenerList extends ListenerList<ItemListener>
+            implements ItemListener {
+            public void menuChanged(Item item, Menu previousMenu) {
+                for (ItemListener listener : this) {
+                    listener.menuChanged(item, previousMenu);
+                }
+            }
+        }
+
+        private MenuBar menuBar = null;
+        private Menu menu = null;
+
+        private ItemListenerList itemListeners = new ItemListenerList();
+
+        private static final Button.DataRenderer DEFAULT_DATA_RENDERER = new MenuBarItemDataRenderer();
+
+        public Item() {
+            this(null);
+        }
+
+        public Item(Object buttonData) {
+            super(buttonData);
+
+            setDataRenderer(DEFAULT_DATA_RENDERER);
+            installSkin(Item.class);
+        }
+
+        @Override
+        protected void setParent(Container parent) {
+            if (!(parent instanceof MenuBar)) {
+                throw new IllegalArgumentException("Parent must be an instance of "
+                    + MenuBar.class.getName());
+            }
+
+            super.setParent(parent);
+        }
+
+        public MenuBar getMenuBar() {
+            return menuBar;
+        }
+
+        private void setMenuBar(MenuBar menuBar) {
+            this.menuBar = menuBar;
+        }
+
+        public Menu getMenu() {
+            return menu;
+        }
+
+        public void setMenu(Menu menu) {
+            if (menu != null
+                && menu.getItem() != null) {
+                throw new IllegalArgumentException("menu already belongs to an item.");
+            }
+
+            Menu previousMenu = this.menu;
+
+            if (previousMenu != menu) {
+                this.menu = menu;
+                itemListeners.menuChanged(this, previousMenu);
+            }
+        }
+
+        @Override
+        public void setToggleButton(boolean toggleButton) {
+            throw new UnsupportedOperationException("Menu bar items cannot be toggle buttons.");
+        }
+
+        public ListenerList<ItemListener> getItemListeners() {
+            return itemListeners;
+        }
+    }
+
+    /**
+     * Item listener interface.
+     *
+     * @author gbrown
+     */
+    public interface ItemListener {
+        public void menuChanged(Item item, Menu previousMenu);
+    }
+
+    /**
+     * Item sequence implementation.
+     *
+     * @author gbrown
+     */
+    public final class ItemSequence implements Sequence<Item>, Iterable<Item> {
+        public int add(Item item) {
+            int index = getLength();
+            insert(item, index);
+
+            return index;
+        }
+
+        public void insert(Item item, int index) {
+            if (item.getMenuBar() != null) {
+                throw new IllegalArgumentException("item already has a menu bar.");
+            }
+
+            MenuBar.this.add(item);
+            items.insert(item, index);
+            item.setMenuBar(MenuBar.this);
+
+            menuBarListeners.itemInserted(MenuBar.this, index);
+        }
+
+        public Item update(int index, Item item) {
+            throw new UnsupportedOperationException();
+        }
+
+        public int remove(Item item) {
+            int index = items.indexOf(item);
+            if (index != -1) {
+                remove(index, 1);
+            }
+
+            return index;
+        }
+
+        public Sequence<Item> remove(int index, int count) {
+            Sequence<Item> removed = items.remove(index, count);
+
+            for (int i = 0, n = removed.getLength(); i < n; i++) {
+                Item item = removed.get(i);
+                item.setMenuBar(null);
+                MenuBar.this.remove(item);
+            }
+
+            menuBarListeners.itemsRemoved(MenuBar.this, index, removed);
+
+            return removed;
+        }
+
+        public Item get(int index) {
+            return items.get(index);
+        }
+
+        public int indexOf(Item item) {
+            return items.indexOf(item);
+        }
+
+        public int getLength() {
+            return items.getLength();
+        }
+
+        public Iterator<Item> iterator() {
+            return new ImmutableIterator<Item>(items.iterator());
+        }
+    }
+
+    private class MenuBarListenerList extends ListenerList<MenuBarListener>
+        implements MenuBarListener {
+        public void itemInserted(MenuBar menuBar, int index) {
+            for (MenuBarListener listener : this) {
+                listener.itemInserted(menuBar, index);
+            }
+        }
+
+        public void itemsRemoved(MenuBar menuBar, int index, Sequence<MenuBar.Item> removed) {
+            for (MenuBarListener listener : this) {
+                listener.itemsRemoved(menuBar, index, removed);
+            }
+        }
+
+        public void activeChanged(MenuBar menuBar) {
+            for (MenuBarListener listener : this) {
+                listener.activeChanged(menuBar);
+            }
+        }
+    }
+
+    private ArrayList<Item> items = new ArrayList<Item>();
+    private ItemSequence itemSequence = new ItemSequence();
+
+    private boolean active = false;
+
+    private MenuBarListenerList menuBarListeners = new MenuBarListenerList();
+
+    public MenuBar() {
+        installSkin(MenuBar.class);
+    }
+
+    public ItemSequence getItems() {
+        return itemSequence;
+    }
+
+    public boolean isActive() {
+        return active;
+    }
+
+    public void setActive(boolean active) {
+        if (this.active != active) {
+            this.active = active;
+
+            if (!active) {
+                getWindow().setActiveDescendant(null);
+            }
+
+            menuBarListeners.activeChanged(this);
+        }
+    }
+
+    @Override
+    public Sequence<Component> remove(int index, int count) {
+        for (int i = index, n = index + count; i < n; i++) {
+            Item item = (Item)get(i);
+
+            if (item.getMenuBar() != null) {
+                throw new UnsupportedOperationException();
+            }
+        }
+
+        // Call the base method to remove the components
+        return super.remove(index, count);
+    }
+
+    public ListenerList<MenuBarListener> getMenuBarListeners() {
+        return menuBarListeners;
+    }
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuBar.png
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuBar.png?rev=754936&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuBar.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuBarListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuBarListener.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuBarListener.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuBarListener.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,49 @@
+/*
+ * 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.collections.Sequence;
+
+/**
+ * Menu bar listener interface.
+ *
+ * @author gbrown
+ */
+public interface MenuBarListener {
+    /**
+     * Called when a menu bar item has been inserted.
+     *
+     * @param menuBar
+     * @param index
+     */
+    public void itemInserted(MenuBar menuBar, int index);
+
+    /**
+     * Called when menu bar items have been removed.
+     *
+     * @param menuBar
+     * @param index
+     * @param removed
+     */
+    public void itemsRemoved(MenuBar menuBar, int index, Sequence<MenuBar.Item> removed);
+
+    /**
+     * Called when the menu bar's active state has changed.
+     *
+     * @param menuBar
+     */
+    public void activeChanged(MenuBar menuBar);
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuButton.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuButton.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuButton.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuButton.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,99 @@
+/*
+ * 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.ListenerList;
+import pivot.wtk.content.ButtonDataRenderer;
+
+/**
+ * Component that allows a user to select one of several menu options. The
+ * options are hidden until the user pushes the button.
+ * <p>
+ * The repeatable flag is used to trigger "split button" behavior.
+ * When true, the button reflects the selected value and allows a user to
+ * repeatedly press the left half of the button, firing additional menu
+ * selection events for the selected item. Pressing the right half of the
+ * button continues to fire button press events and display the menu.
+ *
+ * @author gbrown
+ */
+@ComponentInfo(icon="MenuButton.png")
+public class MenuButton extends Button {
+    private class MenuButtonListenerList extends ListenerList<MenuButtonListener>
+        implements MenuButtonListener {
+        public void menuChanged(MenuButton menuButton, Menu previousMenu) {
+            for (MenuButtonListener listener : this) {
+                listener.menuChanged(menuButton, previousMenu);
+            }
+        }
+
+        public void repeatableChanged(MenuButton menuButton) {
+            for (MenuButtonListener listener : this) {
+                listener.repeatableChanged(menuButton);
+            }
+        }
+    }
+
+    private Menu menu = null;
+    private boolean repeatable = false;
+
+    private MenuButtonListenerList menuButtonListeners = new MenuButtonListenerList();
+
+    private static final Button.DataRenderer DEFAULT_DATA_RENDERER = new ButtonDataRenderer();
+
+    public MenuButton() {
+        setDataRenderer(DEFAULT_DATA_RENDERER);
+        installSkin(MenuButton.class);
+    }
+
+    @Override
+    public void setToggleButton(boolean toggleButton) {
+        throw new UnsupportedOperationException("Menu buttons cannot be toggle buttons.");
+    }
+
+    public Menu getMenu() {
+        return menu;
+    }
+
+    public void setMenu(Menu menu) {
+        if (menu != null
+            && menu.getItem() != null) {
+            throw new IllegalArgumentException("menu already belongs to an item.");
+        }
+
+        Menu previousMenu = this.menu;
+
+        if (previousMenu != menu) {
+            this.menu = menu;
+            menuButtonListeners.menuChanged(this, previousMenu);
+        }
+    }
+
+    public boolean isRepeatable() {
+        return repeatable;
+    }
+
+    public void setRepeatable(boolean repeatable) {
+        if (this.repeatable != repeatable) {
+            this.repeatable = repeatable;
+            menuButtonListeners.repeatableChanged(this);
+        }
+    }
+
+    public ListenerList<MenuButtonListener> getMenuButtonListeners() {
+        return menuButtonListeners;
+    }
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuButton.png
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuButton.png?rev=754936&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuButton.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuButtonListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuButtonListener.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuButtonListener.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuButtonListener.java Mon Mar 16 16:36:10 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;
+
+/**
+ * Menu button listener interface.
+ *
+ * @author gbrown
+ */
+public interface MenuButtonListener {
+    /**
+     * Called when a menu button's menu has changed.
+     *
+     * @param menuButton
+     * @param previousMenu
+     */
+    public void menuChanged(MenuButton menuButton, Menu previousMenu);
+
+    /**
+     * Called when a menu button's repeatable flag has changed.
+     *
+     * @param menuButton
+     */
+    public void repeatableChanged(MenuButton menuButton);
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuItemSelectionListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuItemSelectionListener.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuItemSelectionListener.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuItemSelectionListener.java Mon Mar 16 16:36:10 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;
+
+/**
+ * Menu item selection listener interface.
+ *
+ * @author gbrown
+ */
+public interface MenuItemSelectionListener {
+    /**
+     * Called when a descendant item of this menu has been selected.
+     *
+     * @param menuItem
+     */
+    public void itemSelected(Menu.Item menuItem);
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuListener.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuListener.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuListener.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,59 @@
+/*
+ * 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.collections.Sequence;
+
+/**
+ * Menu listener interface.
+ *
+ * @author gbrown
+ */
+public interface MenuListener {
+    /**
+     * Called when a menu section has been inserted.
+     *
+     * @param menu
+     * @param index
+     */
+    public void sectionInserted(Menu menu, int index);
+
+    /**
+     * Called when menu sections have been removed.
+     *
+     * @param menu
+     * @param index
+     * @param removed
+     */
+    public void sectionsRemoved(Menu menu, int index, Sequence<Menu.Section> removed);
+
+    /**
+     * Called when a menu item has been inserted.
+     *
+     * @param section
+     * @param index
+     */
+    public void itemInserted(Menu.Section section, int index);
+
+    /**
+     * Called when menu items have been removed.
+     *
+     * @param section
+     * @param index
+     * @param removed
+     */
+    public void itemsRemoved(Menu.Section section, int index, Sequence<Menu.Item> removed);
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuPopup.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuPopup.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuPopup.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuPopup.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,94 @@
+/*
+ * 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.ListenerList;
+
+/**
+ * Popup class that displays a cascading menu.
+ *
+ * @author gbrown
+ */
+@ComponentInfo(icon="MenuPopup.png")
+public class MenuPopup extends Popup {
+    private class MenuPopupListenerList extends ListenerList<MenuPopupListener>
+        implements MenuPopupListener {
+        public void menuChanged(MenuPopup menuPopup, Menu previousMenu) {
+            for (MenuPopupListener listener : this) {
+                listener.menuChanged(menuPopup, previousMenu);
+            }
+        }
+    }
+
+    private Menu menu;
+
+    private MenuPopupListenerList menuPopupListeners = new MenuPopupListenerList();
+
+    public MenuPopup() {
+        this(null);
+    }
+
+    public MenuPopup(Menu menu) {
+        setMenu(menu);
+
+        installSkin(MenuPopup.class);
+    }
+
+    public Menu getMenu() {
+        return menu;
+    }
+
+    public void setMenu(Menu menu) {
+        Menu previousMenu = this.menu;
+
+        if (previousMenu != menu) {
+            this.menu = menu;
+            menuPopupListeners.menuChanged(this, previousMenu);
+        }
+    }
+
+    public void open(Display display, int x, int y) {
+        // TODO Determine x, y and width, height
+        setLocation(x, y);
+        super.open(display);
+    }
+
+    public void open(Display display, Point location) {
+        if (location == null) {
+            throw new IllegalArgumentException("location is null.");
+        }
+
+        open(display, location.x, location.y);
+    }
+
+    public void open(Window owner, int x, int y) {
+        // TODO Determine x, y and width, height
+        setLocation(x, y);
+        super.open(owner);
+    }
+
+    public void open(Window owner, Point location) {
+        if (location == null) {
+            throw new IllegalArgumentException("location is null.");
+        }
+
+        open(owner, location.x, location.y);
+    }
+
+    public ListenerList<MenuPopupListener> getMenuPopupListeners() {
+        return menuPopupListeners;
+    }
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuPopup.png
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuPopup.png?rev=754936&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuPopup.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuPopupListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuPopupListener.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuPopupListener.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MenuPopupListener.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,31 @@
+/*
+ * 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;
+
+/**
+ * Menu popup listener interface.
+ *
+ * @author gbrown
+ */
+public interface MenuPopupListener {
+    /**
+     * Called when a menu popup's menu has changed.
+     *
+     * @param menuPopup
+     * @param previousMenu
+     */
+    public void menuChanged(MenuPopup menuPopup, Menu previousMenu);
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MessageType.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MessageType.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MessageType.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MessageType.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+/**
+ * Enumeration defining a message's type.
+ *
+ * @author gbrown
+ */
+public enum MessageType {
+    ERROR,
+    WARNING,
+    QUESTION,
+    INFO,
+    APPLICATION;
+
+    public static MessageType decode(String value) {
+        return valueOf(value.toUpperCase());
+    }
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/Meter.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/Meter.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/Meter.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/Meter.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,81 @@
+/*
+ * 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.ListenerList;
+
+/**
+ * Component that displays progress information.
+ *
+ * @author tvolkert
+ */
+@ComponentInfo(icon="Meter.png")
+public class Meter extends Component {
+    private static class MeterListenerList extends ListenerList<MeterListener>
+    implements MeterListener {
+        public void percentageChanged(Meter meter, double oldPercentage) {
+            for (MeterListener listener : this) {
+                listener.percentageChanged(meter, oldPercentage);
+            }
+        }
+
+        public void textChanged(Meter meter, String oldText) {
+            for (MeterListener listener : this) {
+                listener.textChanged(meter, oldText);
+            }
+        }
+    }
+
+    private double percentage = 0.0;
+    private String text = null;
+    private MeterListenerList meterListeners = new MeterListenerList();
+
+    public Meter() {
+        installSkin(Meter.class);
+    }
+
+    public double getPercentage() {
+        return percentage;
+    }
+
+    public void setPercentage(double percentage) {
+        if (percentage < 0.0 || percentage > 1.0) {
+            throw new IllegalArgumentException
+                ("Percentage must be a number between 0 and 1");
+        }
+
+        double previousPercentage = this.percentage;
+
+        if (previousPercentage != percentage) {
+            this.percentage = percentage;
+            meterListeners.percentageChanged(this, previousPercentage);
+        }
+    }
+
+    public String getText() {
+        return text;
+    }
+
+    public void setText(String text) {
+        String previousText = this.text;
+        this.text = text;
+        meterListeners.textChanged(this, previousText);
+    }
+
+    public ListenerList<MeterListener> getMeterListeners() {
+        return meterListeners;
+    }
+}

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/Meter.png
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/Meter.png?rev=754936&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/Meter.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MeterListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MeterListener.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MeterListener.java (added)
+++ incubator/pivot/tags/v1.0.1/wtk/src/pivot/wtk/MeterListener.java Mon Mar 16 16:36:10 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;
+
+/**
+ * Meter listener interface.
+ *
+ * @author tvolkert
+ */
+public interface MeterListener {
+    /**
+     * Called when a meter's percentage value has changed.
+     *
+     * @param meter
+     * @param previousPercentage
+     */
+    public void percentageChanged(Meter meter, double previousPercentage);
+
+    /**
+     * Called when a meter's text has changed.
+     *
+     * @param meter
+     * @param previousText
+     */
+    public void textChanged(Meter meter, String previousText);
+}