You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by gb...@apache.org on 2009/09/17 19:43:44 UTC

svn commit: r816300 - in /incubator/pivot/trunk: core/src/org/apache/pivot/collections/ core/src/org/apache/pivot/collections/adapter/ core/src/org/apache/pivot/collections/concurrent/ demos/src/org/apache/pivot/demos/million/ demos/src/org/apache/pivo...

Author: gbrown
Date: Thu Sep 17 17:43:41 2009
New Revision: 816300

URL: http://svn.apache.org/viewvc?rev=816300&view=rev
Log:
Initial work towards streamlining TableView sorting API; ensure that all collections apply comparator changes whenever setComparator() is called (not only when the reference changes).

Added:
    incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableViewSortListener.java
Removed:
    incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedCollection.java
Modified:
    incubator/pivot/trunk/core/src/org/apache/pivot/collections/ArrayList.java
    incubator/pivot/trunk/core/src/org/apache/pivot/collections/FilteredList.java
    incubator/pivot/trunk/core/src/org/apache/pivot/collections/HashMap.java
    incubator/pivot/trunk/core/src/org/apache/pivot/collections/HashSet.java
    incubator/pivot/trunk/core/src/org/apache/pivot/collections/LinkedList.java
    incubator/pivot/trunk/core/src/org/apache/pivot/collections/MapList.java
    incubator/pivot/trunk/core/src/org/apache/pivot/collections/adapter/MapAdapter.java
    incubator/pivot/trunk/core/src/org/apache/pivot/collections/adapter/SetAdapter.java
    incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedList.java
    incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedMap.java
    incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedQueue.java
    incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedSet.java
    incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedStack.java
    incubator/pivot/trunk/demos/src/org/apache/pivot/demos/million/LargeData.java
    incubator/pivot/trunk/demos/src/org/apache/pivot/demos/million/large_data.wtkx
    incubator/pivot/trunk/demos/src/org/apache/pivot/demos/tables/FixedColumnTable.java
    incubator/pivot/trunk/demos/src/org/apache/pivot/demos/tables/fixed_column_table.wtkx
    incubator/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/KitchenSink.java
    incubator/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/tables.wtkx
    incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/BoxPane.java
    incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/Dialog.java
    incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/Sheet.java
    incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableView.java
    incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableViewHeader.java
    incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableViewHeaderListener.java
    incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/terra/TerraTableViewHeaderSkin.java

Modified: incubator/pivot/trunk/core/src/org/apache/pivot/collections/ArrayList.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/core/src/org/apache/pivot/collections/ArrayList.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/core/src/org/apache/pivot/collections/ArrayList.java (original)
+++ incubator/pivot/trunk/core/src/org/apache/pivot/collections/ArrayList.java Thu Sep 17 17:43:41 2009
@@ -436,17 +436,14 @@
     public void setComparator(Comparator<T> comparator) {
         Comparator<T> previousComparator = this.comparator;
 
-        if (previousComparator != comparator) {
-            if (comparator != null) {
-                sort(this, comparator);
-            }
+        if (comparator != null) {
+            sort(this, comparator);
+        }
 
-            // Set the new comparator
-            this.comparator = comparator;
+        this.comparator = comparator;
 
-            if (listListeners != null) {
-                listListeners.comparatorChanged(this, previousComparator);
-            }
+        if (listListeners != null) {
+            listListeners.comparatorChanged(this, previousComparator);
         }
     }
 

Modified: incubator/pivot/trunk/core/src/org/apache/pivot/collections/FilteredList.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/core/src/org/apache/pivot/collections/FilteredList.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/core/src/org/apache/pivot/collections/FilteredList.java (original)
+++ incubator/pivot/trunk/core/src/org/apache/pivot/collections/FilteredList.java Thu Sep 17 17:43:41 2009
@@ -480,16 +480,13 @@
     @Override
     public void setComparator(Comparator<T> comparator) {
         Comparator<T> previousComparator = this.comparator;
+        this.comparator = comparator;
 
-        if (previousComparator != comparator) {
-            this.comparator = comparator;
-
-            if (view != null) {
-                view.setComparator(comparator);
-            }
-
-            listListeners.comparatorChanged(this, previousComparator);
+        if (view != null) {
+            view.setComparator(comparator);
         }
+
+        listListeners.comparatorChanged(this, previousComparator);
     }
 
     @Override

Modified: incubator/pivot/trunk/core/src/org/apache/pivot/collections/HashMap.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/core/src/org/apache/pivot/collections/HashMap.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/core/src/org/apache/pivot/collections/HashMap.java (original)
+++ incubator/pivot/trunk/core/src/org/apache/pivot/collections/HashMap.java Thu Sep 17 17:43:41 2009
@@ -335,26 +335,24 @@
     public void setComparator(Comparator<K> comparator) {
         Comparator<K> previousComparator = getComparator();
 
-        if (comparator != previousComparator) {
-            if (comparator == null) {
-                keys = null;
-            } else {
-                if (keys == null) {
-                    // Populate key list
-                    ArrayList<K> keys = new ArrayList<K>((int)((float)getCapacity() * loadFactor));
-                    for (K key : this) {
-                        keys.add(key);
-                    }
-
-                    this.keys = keys;
+        if (comparator == null) {
+            keys = null;
+        } else {
+            if (keys == null) {
+                // Populate key list
+                ArrayList<K> keys = new ArrayList<K>((int)((float)getCapacity() * loadFactor));
+                for (K key : this) {
+                    keys.add(key);
                 }
 
-                keys.setComparator(comparator);
+                this.keys = keys;
             }
 
-            if (mapListeners != null) {
-                mapListeners.comparatorChanged(this, previousComparator);
-            }
+            keys.setComparator(comparator);
+        }
+
+        if (mapListeners != null) {
+            mapListeners.comparatorChanged(this, previousComparator);
         }
     }
 

Modified: incubator/pivot/trunk/core/src/org/apache/pivot/collections/HashSet.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/core/src/org/apache/pivot/collections/HashSet.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/core/src/org/apache/pivot/collections/HashSet.java (original)
+++ incubator/pivot/trunk/core/src/org/apache/pivot/collections/HashSet.java Thu Sep 17 17:43:41 2009
@@ -118,7 +118,13 @@
 
     @Override
     public void setComparator(Comparator<E> comparator) {
+        Comparator<E> previousComparator = getComparator();
+
         hashMap.setComparator(comparator);
+
+        if (setListeners != null) {
+            setListeners.comparatorChanged(this, previousComparator);
+        }
     }
 
     @Override

Modified: incubator/pivot/trunk/core/src/org/apache/pivot/collections/LinkedList.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/core/src/org/apache/pivot/collections/LinkedList.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/core/src/org/apache/pivot/collections/LinkedList.java (original)
+++ incubator/pivot/trunk/core/src/org/apache/pivot/collections/LinkedList.java Thu Sep 17 17:43:41 2009
@@ -552,44 +552,42 @@
     public void setComparator(Comparator<T> comparator) {
         Comparator<T> previousComparator = this.comparator;
 
-        if (previousComparator != comparator) {
-            if (comparator != null) {
-                // Copy the nodes into an array and sort
-                T[] array = (T[])new Object[length];
-
-                int i = 0;
-                for (T item : this) {
-                    array[i++] = item;
-                }
+        if (comparator != null) {
+            // Copy the nodes into an array and sort
+            T[] array = (T[])new Object[length];
+
+            int i = 0;
+            for (T item : this) {
+                array[i++] = item;
+            }
 
-                Arrays.sort(array, comparator);
+            Arrays.sort(array, comparator);
 
-                // Rebuild the node list
-                first = null;
+            // Rebuild the node list
+            first = null;
 
-                Node<T> node = null;
-                for (i = 0; i < length; i++) {
-                    Node<T> previousNode = node;
-                    node = new Node<T>(previousNode, null, array[i]);
-
-                    if (previousNode == null) {
-                        first = node;
-                    } else {
-                        previousNode.next = node;
-                    }
+            Node<T> node = null;
+            for (i = 0; i < length; i++) {
+                Node<T> previousNode = node;
+                node = new Node<T>(previousNode, null, array[i]);
+
+                if (previousNode == null) {
+                    first = node;
+                } else {
+                    previousNode.next = node;
                 }
+            }
 
-                last = node;
+            last = node;
 
-                modificationCount++;
-            }
+            modificationCount++;
+        }
 
-            // Set the new comparator
-            this.comparator = comparator;
+        // Set the new comparator
+        this.comparator = comparator;
 
-            if (listListeners != null) {
-                listListeners.comparatorChanged(this, previousComparator);
-            }
+        if (listListeners != null) {
+            listListeners.comparatorChanged(this, previousComparator);
         }
     }
 

Modified: incubator/pivot/trunk/core/src/org/apache/pivot/collections/MapList.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/core/src/org/apache/pivot/collections/MapList.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/core/src/org/apache/pivot/collections/MapList.java (original)
+++ incubator/pivot/trunk/core/src/org/apache/pivot/collections/MapList.java Thu Sep 17 17:43:41 2009
@@ -386,10 +386,8 @@
     public void setComparator(Comparator<Pair<K, V>> comparator) {
         Comparator<Pair<K, V>> previousComparator = view.getComparator();
 
-        if (previousComparator != comparator) {
-            view.setComparator(comparator);
-            listListeners.comparatorChanged(this, previousComparator);
-        }
+        view.setComparator(comparator);
+        listListeners.comparatorChanged(this, previousComparator);
     }
 
     /**

Modified: incubator/pivot/trunk/core/src/org/apache/pivot/collections/adapter/MapAdapter.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/core/src/org/apache/pivot/collections/adapter/MapAdapter.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/core/src/org/apache/pivot/collections/adapter/MapAdapter.java (original)
+++ incubator/pivot/trunk/core/src/org/apache/pivot/collections/adapter/MapAdapter.java Thu Sep 17 17:43:41 2009
@@ -116,13 +116,14 @@
     @SuppressWarnings("unchecked")
     @Override
     public void setComparator(Comparator<K> comparator) {
-        // If the adapted map supports it, implement setComparator by
-        // constructing a new map
+        Comparator<K> previousComparator = getComparator();
+
+        // If the adapted map supports it, construct a new sorted map
         if (this.map instanceof SortedMap) {
             try {
                 Constructor constructor = this.map.getClass().getConstructor(Comparator.class);
                 if (constructor != null) {
-                    java.util.Map<K, V> map = (java.util.Map) constructor.newInstance(comparator);
+                    java.util.Map<K, V> map = (java.util.Map)constructor.newInstance(comparator);
                     map.putAll(this.map);
                     this.map = map;
                 }
@@ -140,7 +141,8 @@
                 throw new RuntimeException(exception);
             }
         }
-        throw new UnsupportedOperationException();
+
+        mapListeners.comparatorChanged(this, previousComparator);
     }
 
     @Override

Modified: incubator/pivot/trunk/core/src/org/apache/pivot/collections/adapter/SetAdapter.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/core/src/org/apache/pivot/collections/adapter/SetAdapter.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/core/src/org/apache/pivot/collections/adapter/SetAdapter.java (original)
+++ incubator/pivot/trunk/core/src/org/apache/pivot/collections/adapter/SetAdapter.java Thu Sep 17 17:43:41 2009
@@ -112,8 +112,9 @@
     @SuppressWarnings("unchecked")
     @Override
     public void setComparator(Comparator<E> comparator) {
-        // If the adapted set supports it, implement setComparator by
-        // constructing a new set
+        Comparator<E> previousComparator = getComparator();
+
+        // If the adapted set supports it, construct a new sorted set
         if (this.set instanceof java.util.SortedSet) {
             try {
                 Constructor constructor = this.set.getClass().getConstructor(Comparator.class);
@@ -136,7 +137,8 @@
                 throw new RuntimeException(exception);
             }
         }
-        throw new UnsupportedOperationException();
+
+        setListeners.comparatorChanged(this, previousComparator);
     }
 
     @Override

Modified: incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedList.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedList.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedList.java (original)
+++ incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedList.java Thu Sep 17 17:43:41 2009
@@ -17,18 +17,19 @@
 package org.apache.pivot.collections.concurrent;
 
 import java.util.Comparator;
+import java.util.Iterator;
 
 import org.apache.pivot.collections.List;
 import org.apache.pivot.collections.ListListener;
 import org.apache.pivot.collections.Sequence;
+import org.apache.pivot.util.ImmutableIterator;
 import org.apache.pivot.util.ListenerList;
 
 
 /**
  * Synchronized implementation of the {@link List} interface.
  */
-public class SynchronizedList<T> extends SynchronizedCollection<T>
-    implements List<T> {
+public class SynchronizedList<T> implements List<T> {
     private static class SynchronizedListListenerList<T>
         extends ListListenerList<T> {
         @Override
@@ -67,15 +68,20 @@
         }
     }
 
+    private List<T> list;
     private SynchronizedListListenerList<T> listListeners = new SynchronizedListListenerList<T>();
 
     public SynchronizedList(List<T> list) {
-        super(list);
+        if (list == null) {
+            throw new IllegalArgumentException();
+        }
+
+        this.list = list;
     }
 
     @Override
     public synchronized int add(T item) {
-        int index = ((List<T>)collection).add(item);
+        int index = list.add(item);
         listListeners.itemInserted(this, index);
 
         return index;
@@ -83,13 +89,13 @@
 
     @Override
     public synchronized void insert(T item, int index) {
-        ((List<T>)collection).insert(item, index);
+        list.insert(item, index);
         listListeners.itemInserted(this, index);
     }
 
     @Override
     public synchronized T update(int index, T item) {
-        T previousItem = ((List<T>)collection).update(index, item);
+        T previousItem = list.update(index, item);
         if (previousItem != item) {
             listListeners.itemUpdated(this, index, previousItem);
         }
@@ -111,7 +117,7 @@
 
     @Override
     public synchronized Sequence<T> remove(int index, int count) {
-        Sequence<T> removed = ((List<T>)collection).remove(index, count);
+        Sequence<T> removed = list.remove(index, count);
         if (count > 0) {
             listListeners.itemsRemoved(this, index, removed);
         }
@@ -120,18 +126,47 @@
     }
 
     @Override
+    public synchronized void clear() {
+        if (list.getLength() > 0) {
+            list.clear();
+            listListeners.listCleared(this);
+        }
+    }
+
+    @Override
     public synchronized T get(int index) {
-        return ((List<T>)collection).get(index);
+        return list.get(index);
     }
 
     @Override
     public synchronized int indexOf(T item) {
-        return ((List<T>)collection).indexOf(item);
+        return list.indexOf(item);
     }
 
     @Override
     public synchronized int getLength() {
-        return ((List<T>)collection).getLength();
+        return list.getLength();
+    }
+
+    @Override
+    public synchronized Comparator<T> getComparator() {
+        return list.getComparator();
+    }
+
+    @Override
+    public synchronized void setComparator(Comparator<T> comparator) {
+        Comparator<T> previousComparator = getComparator();
+        list.setComparator(comparator);
+        listListeners.comparatorChanged(this, previousComparator);
+    }
+
+    /**
+     * NOTE Callers must manually synchronize on the SynchronizedList
+     * instance to ensure thread safety during iteration.
+     */
+    @Override
+    public Iterator<T> iterator() {
+        return new ImmutableIterator<T>(list.iterator());
     }
 
     @Override

Modified: incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedMap.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedMap.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedMap.java (original)
+++ incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedMap.java Thu Sep 17 17:43:41 2009
@@ -17,17 +17,17 @@
 package org.apache.pivot.collections.concurrent;
 
 import java.util.Comparator;
+import java.util.Iterator;
 
 import org.apache.pivot.collections.Map;
 import org.apache.pivot.collections.MapListener;
+import org.apache.pivot.util.ImmutableIterator;
 import org.apache.pivot.util.ListenerList;
 
-
 /**
  * Synchronized implementation of the {@link Map} interface.
  */
-public class SynchronizedMap<K, V> extends SynchronizedCollection<K>
-    implements Map<K, V> {
+public class SynchronizedMap<K, V> implements Map<K, V> {
     private static class SynchronizedMapListenerList<K, V>
         extends MapListenerList<K, V> {
         @Override
@@ -66,23 +66,26 @@
         }
     }
 
+    private Map<K, V> map;
     private SynchronizedMapListenerList<K, V> mapListeners = new SynchronizedMapListenerList<K, V>();
 
     public SynchronizedMap(Map<K, V> map) {
-        super(map);
+        if (map == null) {
+            throw new IllegalArgumentException();
+        }
+
+        this.map = map;
     }
 
-    @SuppressWarnings("unchecked")
     @Override
     public synchronized V get(K key) {
-        return ((Map<K, V>)collection).get(key);
+        return map.get(key);
     }
 
-    @SuppressWarnings("unchecked")
     @Override
     public synchronized V put(K key, V value) {
         boolean update = containsKey(key);
-        V previousValue = ((Map<K, V>)collection).put(key, value);
+        V previousValue = map.put(key, value);
 
         if (update) {
             mapListeners.valueUpdated(this, key, previousValue);
@@ -94,35 +97,60 @@
         return previousValue;
     }
 
-    @SuppressWarnings("unchecked")
     @Override
     public synchronized V remove(K key) {
         V value = null;
 
         if (containsKey(key)) {
-            value = ((Map<K, V>)collection).remove(key);
+            value = map.remove(key);
             mapListeners.valueRemoved(this, key, value);
         }
 
         return value;
     }
 
-    @SuppressWarnings("unchecked")
     @Override
     public synchronized boolean isEmpty() {
-        return ((Map<K, V>)collection).isEmpty();
+        return map.isEmpty();
+    }
+
+    @Override
+    public synchronized void clear() {
+        if (!map.isEmpty()) {
+            map.clear();
+            mapListeners.mapCleared(this);
+        }
     }
 
-    @SuppressWarnings("unchecked")
     @Override
     public synchronized boolean containsKey(K key) {
-        return ((Map<K, V>)collection).containsKey(key);
+        return map.containsKey(key);
     }
 
-    @SuppressWarnings("unchecked")
     @Override
     public synchronized int getCount() {
-        return ((Map<K, V>)collection).getCount();
+        return map.getCount();
+    }
+
+    @Override
+    public synchronized Comparator<K> getComparator() {
+        return map.getComparator();
+    }
+
+    @Override
+    public synchronized void setComparator(Comparator<K> comparator) {
+        Comparator<K> previousComparator = getComparator();
+        map.setComparator(comparator);
+        mapListeners.comparatorChanged(this, previousComparator);
+    }
+
+    /**
+     * NOTE Callers must manually synchronize on the SynchronizedMap
+     * instance to ensure thread safety during iteration.
+     */
+    @Override
+    public Iterator<K> iterator() {
+        return new ImmutableIterator<K>(map.iterator());
     }
 
     @Override

Modified: incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedQueue.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedQueue.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedQueue.java (original)
+++ incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedQueue.java Thu Sep 17 17:43:41 2009
@@ -16,16 +16,19 @@
  */
 package org.apache.pivot.collections.concurrent;
 
+import java.util.Comparator;
+import java.util.Iterator;
+
 import org.apache.pivot.collections.Queue;
 import org.apache.pivot.collections.QueueListener;
+import org.apache.pivot.util.ImmutableIterator;
 import org.apache.pivot.util.ListenerList;
 
 
 /**
  * Synchronized implementation of the {@link Queue} interface.
  */
-public class SynchronizedQueue<T> extends SynchronizedCollection<T>
-    implements Queue<T> {
+public class SynchronizedQueue<T> implements Queue<T> {
     private static class SynchronizedQueueListenerList<T>
         extends QueueListenerList<T> {
         @Override
@@ -49,15 +52,20 @@
         }
     }
 
+    private Queue<T> queue;
     private SynchronizedQueueListenerList<T> queueListeners = new SynchronizedQueueListenerList<T>();
 
     public SynchronizedQueue(Queue<T> queue) {
-        super(queue);
+        if (queue == null) {
+            throw new IllegalArgumentException();
+        }
+
+        this.queue = queue;
     }
 
     @Override
     public synchronized void enqueue(T item) {
-        ((Queue<T>)collection).enqueue(item);
+        queue.enqueue(item);
         queueListeners.itemEnqueued(this, item);
 
         notify();
@@ -71,7 +79,7 @@
                 wait();
             }
 
-            item = ((Queue<T>)collection).dequeue();
+            item = queue.dequeue();
             queueListeners.itemDequeued(this, item);
         } catch(InterruptedException exception) {
         }
@@ -81,12 +89,38 @@
 
     @Override
     public synchronized T peek() {
-        return ((Queue<T>)collection).peek();
+        return queue.peek();
+    }
+
+    @Override
+    public synchronized void clear() {
+        // TODO Fire event
+        queue.clear();
     }
 
     @Override
     public synchronized boolean isEmpty() {
-        return ((Queue<T>)collection).isEmpty();
+        return queue.isEmpty();
+    }
+
+    @Override
+    public synchronized Comparator<T> getComparator() {
+        return queue.getComparator();
+    }
+
+    @Override
+    public synchronized void setComparator(Comparator<T> comparator) {
+        // TODO Fire event
+        queue.setComparator(comparator);
+    }
+
+    /**
+     * NOTE Callers must manually synchronize on the SynchronizedQueue
+     * instance to ensure thread safety during iteration.
+     */
+    @Override
+    public Iterator<T> iterator() {
+        return new ImmutableIterator<T>(queue.iterator());
     }
 
     @Override

Modified: incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedSet.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedSet.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedSet.java (original)
+++ incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedSet.java Thu Sep 17 17:43:41 2009
@@ -17,16 +17,17 @@
 package org.apache.pivot.collections.concurrent;
 
 import java.util.Comparator;
+import java.util.Iterator;
 
 import org.apache.pivot.collections.Set;
 import org.apache.pivot.collections.SetListener;
+import org.apache.pivot.util.ImmutableIterator;
 import org.apache.pivot.util.ListenerList;
 
 /**
  * Synchronized implementation of the {@link Set} interface.
  */
-public class SynchronizedSet<E> extends SynchronizedCollection<E>
-    implements Set<E> {
+public class SynchronizedSet<E> implements Set<E> {
     private static class SynchronizedSetListenerList<E>
         extends SetListenerList<E> {
         @Override
@@ -60,10 +61,15 @@
         }
     }
 
+    private Set<E> set;
     private SynchronizedSetListenerList<E> setListeners = new SynchronizedSetListenerList<E>();
 
     public SynchronizedSet(Set<E> set) {
-        super(set);
+        if (set == null) {
+            throw new IllegalArgumentException();
+        }
+
+        this.set = set;
     }
 
     @Override
@@ -71,7 +77,7 @@
         boolean added = false;
 
         if (!contains(element)) {
-            ((Set<E>)collection).add(element);
+            set.add(element);
             added = true;
 
             setListeners.elementAdded(this, element);
@@ -85,7 +91,7 @@
         boolean removed = false;
 
         if (contains(element)) {
-            ((Set<E>)collection).remove(element);
+            set.remove(element);
             removed = true;
 
             setListeners.elementRemoved(this, element);
@@ -96,17 +102,46 @@
 
     @Override
     public synchronized boolean contains(E element) {
-        return ((Set<E>)collection).contains(element);
+        return set.contains(element);
     }
 
     @Override
     public synchronized boolean isEmpty() {
-        return ((Set<E>)collection).isEmpty();
+        return set.isEmpty();
+    }
+
+    @Override
+    public synchronized void clear() {
+        if (!set.isEmpty()) {
+            set.clear();
+            setListeners.setCleared(this);
+        }
     }
 
     @Override
     public synchronized int getCount() {
-        return ((Set<E>)collection).getCount();
+        return set.getCount();
+    }
+
+    @Override
+    public synchronized Comparator<E> getComparator() {
+        return set.getComparator();
+    }
+
+    @Override
+    public synchronized void setComparator(Comparator<E> comparator) {
+        Comparator<E> previousComparator = getComparator();
+        set.setComparator(comparator);
+        setListeners.comparatorChanged(this, previousComparator);
+    }
+
+    /**
+     * NOTE Callers must manually synchronize on the SynchronizedSet
+     * instance to ensure thread safety during iteration.
+     */
+    @Override
+    public Iterator<E> iterator() {
+        return new ImmutableIterator<E>(set.iterator());
     }
 
     @Override

Modified: incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedStack.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedStack.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedStack.java (original)
+++ incubator/pivot/trunk/core/src/org/apache/pivot/collections/concurrent/SynchronizedStack.java Thu Sep 17 17:43:41 2009
@@ -16,16 +16,19 @@
  */
 package org.apache.pivot.collections.concurrent;
 
+import java.util.Comparator;
+import java.util.Iterator;
+
 import org.apache.pivot.collections.Stack;
 import org.apache.pivot.collections.StackListener;
+import org.apache.pivot.util.ImmutableIterator;
 import org.apache.pivot.util.ListenerList;
 
 
 /**
  * Synchronized implementation of the {@link Stack} interface.
  */
-public class SynchronizedStack<T> extends SynchronizedCollection<T>
-    implements Stack<T> {
+public class SynchronizedStack<T> implements Stack<T> {
     private static class SynchronizedStackListenerList<T>
         extends StackListenerList<T> {
         @Override
@@ -49,15 +52,20 @@
         }
     }
 
+    private Stack<T> stack;
     private SynchronizedStackListenerList<T> stackListeners = new SynchronizedStackListenerList<T>();
 
     public SynchronizedStack(Stack<T> stack) {
-        super(stack);
+        if (stack == null) {
+            throw new IllegalArgumentException();
+        }
+
+        this.stack = stack;
     }
 
     @Override
     public synchronized void push(T item) {
-        ((Stack<T>)collection).push(item);
+        stack.push(item);
         stackListeners.itemPushed(this, item);
 
         notify();
@@ -71,7 +79,7 @@
                 wait();
             }
 
-            item = ((Stack<T>)collection).pop();
+            item = stack.pop();
             stackListeners.itemPopped(this, item);
         }
         catch(InterruptedException exception) {
@@ -82,12 +90,38 @@
 
     @Override
     public synchronized T peek() {
-        return ((Stack<T>)collection).peek();
+        return stack.peek();
+    }
+
+    @Override
+    public synchronized void clear() {
+        // TODO Fire event
+        stack.clear();
     }
 
     @Override
     public synchronized boolean isEmpty() {
-        return ((Stack<T>)collection).isEmpty();
+        return stack.isEmpty();
+    }
+
+    @Override
+    public synchronized Comparator<T> getComparator() {
+        return stack.getComparator();
+    }
+
+    @Override
+    public synchronized void setComparator(Comparator<T> comparator) {
+        // TODO Fire event
+        stack.setComparator(comparator);
+    }
+
+    /**
+     * NOTE Callers must manually synchronize on the SynchronizedStack
+     * instance to ensure thread safety during iteration.
+     */
+    @Override
+    public Iterator<T> iterator() {
+        return new ImmutableIterator<T>(stack.iterator());
     }
 
     @Override

Modified: incubator/pivot/trunk/demos/src/org/apache/pivot/demos/million/LargeData.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/demos/src/org/apache/pivot/demos/million/LargeData.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/demos/src/org/apache/pivot/demos/million/LargeData.java (original)
+++ incubator/pivot/trunk/demos/src/org/apache/pivot/demos/million/LargeData.java Thu Sep 17 17:43:41 2009
@@ -36,7 +36,6 @@
 import org.apache.pivot.wtk.ListButton;
 import org.apache.pivot.wtk.PushButton;
 import org.apache.pivot.wtk.TableView;
-import org.apache.pivot.wtk.TableViewHeader;
 import org.apache.pivot.wtk.Window;
 import org.apache.pivot.wtkx.WTKXSerializer;
 
@@ -142,7 +141,6 @@
     private PushButton cancelButton = null;
     private Label statusLabel = null;
     private TableView tableView = null;
-    private TableViewHeader tableViewHeader = null;
 
     private CSVSerializer csvSerializer;
     private int pageSize = 0;
@@ -174,7 +172,6 @@
         cancelButton = (PushButton)wtkxSerializer.get("cancelButton");
         statusLabel = (Label)wtkxSerializer.get("statusLabel");
         tableView = (TableView)wtkxSerializer.get("tableView");
-        tableViewHeader = (TableViewHeader)wtkxSerializer.get("tableViewHeader");
 
         loadDataButton.getButtonPressListeners().add(new ButtonPressListener() {
             @Override
@@ -196,6 +193,8 @@
             }
         });
 
+        // TODO
+        /*
         tableViewHeader.getTableViewHeaderPressListeners().add(new TableView.SortHandler() {
             @Override
             public void headerPressed(TableViewHeader tableViewHeader, int index) {
@@ -206,6 +205,7 @@
                 statusLabel.setText("Data sorted in " + (endTime - startTime) + " ms.");
             }
         });
+        */
 
         window.open(display);
     }

Modified: incubator/pivot/trunk/demos/src/org/apache/pivot/demos/million/large_data.wtkx
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/demos/src/org/apache/pivot/demos/million/large_data.wtkx?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
Binary files - no diff available.

Modified: incubator/pivot/trunk/demos/src/org/apache/pivot/demos/tables/FixedColumnTable.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/demos/src/org/apache/pivot/demos/tables/FixedColumnTable.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/demos/src/org/apache/pivot/demos/tables/FixedColumnTable.java (original)
+++ incubator/pivot/trunk/demos/src/org/apache/pivot/demos/tables/FixedColumnTable.java Thu Sep 17 17:43:41 2009
@@ -21,10 +21,8 @@
 import org.apache.pivot.wtk.Application;
 import org.apache.pivot.wtk.DesktopApplicationContext;
 import org.apache.pivot.wtk.Display;
-import org.apache.pivot.wtk.SortDirection;
 import org.apache.pivot.wtk.Span;
 import org.apache.pivot.wtk.TableView;
-import org.apache.pivot.wtk.TableViewHeader;
 import org.apache.pivot.wtk.TableViewSelectionListener;
 import org.apache.pivot.wtk.Window;
 import org.apache.pivot.wtkx.WTKXSerializer;
@@ -32,9 +30,7 @@
 public class FixedColumnTable implements Application {
     private Window window = null;
     private TableView primaryTableView = null;
-    private TableViewHeader primaryTableViewHeader = null;
     private TableView fixedTableView = null;
-    private TableViewHeader fixedTableViewHeader = null;
 
     private boolean synchronizingSelection = false;
 
@@ -44,9 +40,7 @@
         WTKXSerializer wtkxSerializer = new WTKXSerializer();
         window = (Window)wtkxSerializer.readObject(this, "fixed_column_table.wtkx");
         primaryTableView = (TableView)wtkxSerializer.get("primaryTableView");
-        primaryTableViewHeader = (TableViewHeader)wtkxSerializer.get("primaryTableViewHeader");
         fixedTableView = (TableView)wtkxSerializer.get("fixedTableView");
-        fixedTableViewHeader = (TableViewHeader)wtkxSerializer.get("fixedTableViewHeader");
 
         // Keep selection state in sync
         primaryTableView.getTableViewSelectionListeners().add(new TableViewSelectionListener() {
@@ -108,6 +102,8 @@
         });
 
         // Keep header state in sync
+        // TODO Add sort listeners to both table views
+        /*
         primaryTableViewHeader.getTableViewHeaderPressListeners().add(new TableView.SortHandler() {
             @Override
             public void headerPressed(TableViewHeader tableViewHeader, int index) {
@@ -133,6 +129,7 @@
                 }
             }
         });
+        */
 
         window.open(display);
     }

Modified: incubator/pivot/trunk/demos/src/org/apache/pivot/demos/tables/fixed_column_table.wtkx
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/demos/src/org/apache/pivot/demos/tables/fixed_column_table.wtkx?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
Binary files - no diff available.

Modified: incubator/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/KitchenSink.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/KitchenSink.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/KitchenSink.java (original)
+++ incubator/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/KitchenSink.java Thu Sep 17 17:43:41 2009
@@ -69,7 +69,6 @@
 import org.apache.pivot.wtk.SliderValueListener;
 import org.apache.pivot.wtk.Spinner;
 import org.apache.pivot.wtk.TableView;
-import org.apache.pivot.wtk.TableViewHeader;
 import org.apache.pivot.wtk.TextArea;
 import org.apache.pivot.wtk.Theme;
 import org.apache.pivot.wtk.TreeView;
@@ -502,7 +501,6 @@
         private Component component = null;
         private TableView sortableTableView = null;
         private TableView customTableView = null;
-        private TableViewHeader sortableTableViewHeader = null;
 
         @Override
         public Vote previewExpandedChange(Rollup rollup) {
@@ -518,7 +516,6 @@
 
                 sortableTableView = (TableView)wtkxSerializer.get("sortableTableView");
                 customTableView = (TableView)wtkxSerializer.get("customTableView");
-                sortableTableViewHeader = (TableViewHeader)wtkxSerializer.get("sortableTableViewHeader");
 
                 rollup.setContent(component);
 
@@ -547,8 +544,11 @@
 
                 sortableTableView.setTableData(tableData);
 
+                // TODO
+                /*
                 // Install header press listener
                 sortableTableViewHeader.getTableViewHeaderPressListeners().add(new TableView.SortHandler());
+                */
 
                 customTableView.getComponentMouseButtonListeners().add(new ComponentMouseButtonListener.Adapter() {
                     @Override

Modified: incubator/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/tables.wtkx
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/tutorials/src/org/apache/pivot/tutorials/tables.wtkx?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
Binary files - no diff available.

Modified: incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/BoxPane.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/BoxPane.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/BoxPane.java (original)
+++ incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/BoxPane.java Thu Sep 17 17:43:41 2009
@@ -41,7 +41,7 @@
     }
 
     public BoxPane(Orientation orientation) {
-        this.orientation = orientation;
+        setOrientation(orientation);
 
         installSkin(BoxPane.class);
     }
@@ -51,6 +51,10 @@
     }
 
     public void setOrientation(Orientation orientation) {
+        if (orientation == null) {
+            throw new IllegalArgumentException();
+        }
+
         if (this.orientation != orientation) {
             this.orientation = orientation;
             boxPaneListeners.orientationChanged(this);

Modified: incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/Dialog.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/Dialog.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/Dialog.java (original)
+++ incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/Dialog.java Thu Sep 17 17:43:41 2009
@@ -175,12 +175,12 @@
                     }
 
                     // Notify listeners
+                    dialogStateListeners.dialogClosed(this);
+
                     if (dialogCloseListener != null) {
                         dialogCloseListener.dialogClosed(this);
                         dialogCloseListener = null;
                     }
-
-                    dialogStateListeners.dialogClosed(this);
                 }
             } else if (vote == Vote.DENY) {
                 closing = false;

Modified: incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/Sheet.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/Sheet.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/Sheet.java (original)
+++ incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/Sheet.java Thu Sep 17 17:43:41 2009
@@ -136,12 +136,12 @@
                     }
 
                     // Notify listeners
+                    sheetStateListeners.sheetClosed(this);
+
                     if (sheetCloseListener != null) {
                         sheetCloseListener.sheetClosed(this);
                         sheetCloseListener = null;
                     }
-
-                    sheetStateListeners.sheetClosed(this);
                 }
             } else if (vote == Vote.DENY) {
                 closing = false;

Modified: incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableView.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableView.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableView.java (original)
+++ incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableView.java Thu Sep 17 17:43:41 2009
@@ -19,9 +19,9 @@
 import java.util.Comparator;
 import java.util.Iterator;
 
-import org.apache.pivot.beans.BeanDictionary;
 import org.apache.pivot.collections.ArrayList;
 import org.apache.pivot.collections.Dictionary;
+import org.apache.pivot.collections.HashMap;
 import org.apache.pivot.collections.List;
 import org.apache.pivot.collections.ListListener;
 import org.apache.pivot.collections.Map;
@@ -713,87 +713,6 @@
     }
 
     /**
-     * Compares two rows. The dictionary values must implement
-     * {@link Comparable}.
-     * <p>
-     * TODO Allow a caller to sort on multiple columns.
-     */
-    public static class RowComparator implements Comparator<Object> {
-        private String columnName = null;
-        private SortDirection sortDirection = null;
-
-        public RowComparator(String columnName, SortDirection sortDirection) {
-            this.columnName = columnName;
-            this.sortDirection = sortDirection;
-        }
-
-        @Override
-        @SuppressWarnings("unchecked")
-        public int compare(Object o1, Object o2) {
-            Dictionary<String, ?> row1;
-            if (o1 instanceof Dictionary<?, ?>) {
-                row1 = (Dictionary<String, ?>)o1;
-            } else {
-                row1 = new BeanDictionary(o1);
-            }
-
-            Dictionary<String, ?> row2;
-            if (o2 instanceof Dictionary<?, ?>) {
-                row2 = (Dictionary<String, ?>)o2;
-            } else {
-                row2 = new BeanDictionary(o2);
-            }
-
-            Comparable<Object> comparable = (Comparable<Object>)row1.get(columnName);
-            Object value = row2.get(columnName);
-
-            int result;
-            if (comparable == null
-                && value == null) {
-                result = 0;
-            } else if (comparable == null) {
-                result = 1;
-            } else if (value == null) {
-                result = -1;
-            } else {
-                result = (comparable.compareTo(value)) * (sortDirection == SortDirection.ASCENDING ? 1 : -1);
-            }
-
-            return result;
-        }
-    }
-
-    /**
-     * Default sort handler class. Sorts rows using {@link RowComparator}.
-     */
-    public static class SortHandler implements TableViewHeaderPressListener {
-        @Override
-        @SuppressWarnings("unchecked")
-        public void headerPressed(TableViewHeader tableViewHeader, int index) {
-            TableView tableView = tableViewHeader.getTableView();
-            TableView.ColumnSequence columns = tableView.getColumns();
-            TableView.Column column = columns.get(index);
-
-            SortDirection sortDirection = column.getSortDirection();
-
-            if (sortDirection == null
-                || sortDirection == SortDirection.DESCENDING) {
-                sortDirection = SortDirection.ASCENDING;
-            } else {
-                sortDirection = SortDirection.DESCENDING;
-            }
-
-            List<Object> tableData = (List<Object>)tableView.getTableData();
-            tableData.setComparator(new TableView.RowComparator(column.getName(), sortDirection));
-
-            for (int i = 0, n = columns.getLength(); i < n; i++) {
-                column = columns.get(i);
-                column.setSortDirection(i == index ? sortDirection : null);
-            }
-        }
-    }
-
-    /**
      * Column sequence implementation.
      */
     public final class ColumnSequence implements Sequence<Column>, Iterable<Column> {
@@ -908,6 +827,69 @@
     }
 
     /**
+     * Sort dictionary implementation.
+     */
+    public final class SortDictionary implements Dictionary<String, SortDirection>, Iterable<String> {
+        public SortDirection get(String columnName) {
+            return sortMap.get(columnName);
+        }
+
+        public SortDirection put(String columnName, SortDirection sortDirection) {
+            SortDirection previousSortDirection;
+
+            if (sortDirection == null) {
+                previousSortDirection = remove(columnName);
+            } else {
+                boolean update = containsKey(columnName);
+                previousSortDirection = sortMap.put(columnName, sortDirection);
+
+                if (update) {
+                    tableViewSortListeners.sortUpdated(TableView.this, columnName, previousSortDirection);
+                } else {
+                    tableViewSortListeners.sortAdded(TableView.this, columnName);
+                }
+            }
+
+            return previousSortDirection;
+        }
+
+        public SortDirection remove(String columnName) {
+            SortDirection sortDirection = null;
+
+            if (containsKey(columnName)) {
+                sortDirection = sortMap.remove(columnName);
+                sortList.remove(columnName);
+                tableViewSortListeners.sortRemoved(TableView.this, columnName, sortDirection);
+            }
+
+            return sortDirection;
+        }
+
+        public boolean containsKey(String columnName) {
+            return sortMap.containsKey(columnName);
+        }
+
+        public boolean isEmpty() {
+            return sortMap.isEmpty();
+        }
+
+        public Dictionary.Pair<String, SortDirection> get(int index) {
+            String columnName = sortList.get(index);
+            SortDirection sortDirection = sortMap.get(columnName);
+
+            return new Dictionary.Pair<String, SortDirection>(columnName, sortDirection);
+        }
+
+        public int getLength() {
+            return sortList.getLength();
+        }
+
+        public Iterator<String> iterator() {
+            return sortList.iterator();
+        }
+    }
+
+    /**
      * Table view column listener list.
      */
     private static class TableViewColumnListenerList extends ListenerList<TableViewColumnListener>
@@ -1037,6 +1019,36 @@
         }
     }
 
+    private static class TableViewSortListenerList extends ListenerList<TableViewSortListener>
+        implements TableViewSortListener {
+        public void sortAdded(TableView tableView, String columnName) {
+            for (TableViewSortListener listener : this) {
+                listener.sortAdded(tableView, columnName);
+            }
+        }
+
+        public void sortUpdated(TableView tableView, String columnName,
+            SortDirection previousSortDirection) {
+            for (TableViewSortListener listener : this) {
+                listener.sortUpdated(tableView, columnName, previousSortDirection);
+            }
+        }
+
+        public void sortRemoved(TableView tableView, String columnName,
+            SortDirection sortDirection) {
+            for (TableViewSortListener listener : this) {
+                listener.sortRemoved(tableView, columnName, sortDirection);
+            }
+        }
+
+        public void sortChanged(TableView tableView,
+            Sequence<Dictionary.Pair<String, SortDirection>> previousSort) {
+            for (TableViewSortListener listener : this) {
+                listener.sortChanged(tableView, previousSort);
+            }
+        }
+    }
+
     private ArrayList<Column> columns = new ArrayList<Column>();
     private ColumnSequence columnSequence = new ColumnSequence();
 
@@ -1090,6 +1102,9 @@
     private ListSelection selectedRanges = new ListSelection();
     private SelectMode selectMode = SelectMode.SINGLE;
 
+    private HashMap<String, SortDirection> sortMap = new HashMap<String, SortDirection>();
+    private ArrayList<String> sortList = new ArrayList<String>();
+
     private Filter<?> disabledRowFilter = null;
 
     private RowEditor rowEditor = null;
@@ -1098,6 +1113,7 @@
     private TableViewColumnListenerList tableViewColumnListeners = new TableViewColumnListenerList();
     private TableViewRowListenerList tableViewRowListeners = new TableViewRowListenerList();
     private TableViewSelectionListenerList tableViewSelectionListeners = new TableViewSelectionListenerList();
+    private TableViewSortListenerList tableViewSortListeners = new TableViewSortListenerList();
 
     /**
      * Creates a new table view populated with an empty array list.
@@ -1756,4 +1772,8 @@
     public ListenerList<TableViewSelectionListener> getTableViewSelectionListeners() {
         return tableViewSelectionListeners;
     }
+
+    public ListenerList<TableViewSortListener> getTableViewSortListeners() {
+        return tableViewSortListeners;
+    }
 }

Modified: incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableViewHeader.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableViewHeader.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableViewHeader.java (original)
+++ incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableViewHeader.java Thu Sep 17 17:43:41 2009
@@ -25,6 +25,15 @@
  */
 public class TableViewHeader extends Component {
     /**
+     * Enumeration representing a sort mode.
+     */
+    public enum SortMode {
+        NONE,
+        SINGLE_COLUMN,
+        MULTI_COLUMN
+    }
+
+    /**
      * Table view header data renderer interface.
      */
     public interface DataRenderer extends Renderer {
@@ -57,6 +66,13 @@
                 listener.dataRendererChanged(tableViewHeader, previousDataRenderer);
             }
         }
+
+        @Override
+        public void sortModeChanged(TableViewHeader tableViewHeader, SortMode previousSortMode) {
+            for (TableViewHeaderListener listener : this) {
+                listener.sortModeChanged(tableViewHeader, previousSortMode);
+            }
+        }
     }
 
     private static class TableViewHeaderPressListenerList extends ListenerList<TableViewHeaderPressListener>
@@ -69,8 +85,9 @@
         }
     }
 
-    private TableView tableView = null;
-    private DataRenderer dataRenderer = null;
+    private TableView tableView;
+    private DataRenderer dataRenderer;
+    private SortMode sortMode = SortMode.NONE;
 
     private TableViewHeaderListenerList tableViewHeaderListeners = new TableViewHeaderListenerList();
     private TableViewHeaderPressListenerList tableViewHeaderPressListeners = new TableViewHeaderPressListenerList();
@@ -125,6 +142,30 @@
         }
     }
 
+    public SortMode getSortMode() {
+        return sortMode;
+    }
+
+    public void setSortMode(SortMode sortMode) {
+        if (sortMode == null) {
+            throw new IllegalArgumentException();
+        }
+
+        SortMode previousSortMode = this.sortMode;
+        if (previousSortMode != sortMode) {
+            this.sortMode = sortMode;
+            tableViewHeaderListeners.sortModeChanged(this, previousSortMode);
+        }
+    }
+
+    public void setSortMode(String sortMode) {
+        if (sortMode == null) {
+            throw new IllegalArgumentException();
+        }
+
+        setSortMode(SortMode.valueOf(sortMode.toUpperCase()));
+    }
+
     public void pressHeader(int index) {
         tableViewHeaderPressListeners.headerPressed(this, index);
     }

Modified: incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableViewHeaderListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableViewHeaderListener.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableViewHeaderListener.java (original)
+++ incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableViewHeaderListener.java Thu Sep 17 17:43:41 2009
@@ -16,6 +16,8 @@
  */
 package org.apache.pivot.wtk;
 
+import org.apache.pivot.wtk.TableViewHeader.SortMode;
+
 /**
  * Table view header listener interface.
  */
@@ -26,8 +28,7 @@
      * @param tableViewHeader
      * @param previousTableView
      */
-    public void tableViewChanged(TableViewHeader tableViewHeader,
-        TableView previousTableView);
+    public void tableViewChanged(TableViewHeader tableViewHeader, TableView previousTableView);
 
     /**
      * Called when a table view header's data renderer has changed.
@@ -37,4 +38,12 @@
      */
     public void dataRendererChanged(TableViewHeader tableViewHeader,
         TableViewHeader.DataRenderer previousDataRenderer);
+
+    /**
+     * Called when a table view header's sort mode has changed.
+     *
+     * @param tableViewHeader
+     * @param previousSortMode
+     */
+    public void sortModeChanged(TableViewHeader tableViewHeader, SortMode previousSortMode);
 }

Added: incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableViewSortListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableViewSortListener.java?rev=816300&view=auto
==============================================================================
--- incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableViewSortListener.java (added)
+++ incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/TableViewSortListener.java Thu Sep 17 17:43:41 2009
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pivot.wtk;
+
+import org.apache.pivot.collections.Dictionary;
+import org.apache.pivot.collections.Sequence;
+
+/**
+ * Table view sort listener interface.
+ */
+public interface TableViewSortListener {
+    /**
+     * Called when a sort has been added to a table view.
+     *
+     * @param tableView
+     * @param columnName
+     */
+    public void sortAdded(TableView tableView, String columnName);
+
+    /**
+     * Called when a sort has been updated in a table view.
+     *
+     * @param tableView
+     * @param columnName
+     * @param previousSortDirection
+     */
+    public void sortUpdated(TableView tableView, String columnName,
+        SortDirection previousSortDirection);
+
+    /**
+     * Called when a sort has been removed from a table view.
+     *
+     * @param tableView
+     * @param columnName
+     * @param sortDirection
+     */
+    public void sortRemoved(TableView tableView, String columnName,
+        SortDirection sortDirection);
+
+    /**
+     * Called when a table view's sort has changed.
+     *
+     * @param tableView
+     * @param previousSort
+     */
+    public void sortChanged(TableView tableView,
+        Sequence<Dictionary.Pair<String, SortDirection>> previousSort);
+}

Modified: incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/terra/TerraTableViewHeaderSkin.java
URL: http://svn.apache.org/viewvc/incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/terra/TerraTableViewHeaderSkin.java?rev=816300&r1=816299&r2=816300&view=diff
==============================================================================
--- incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/terra/TerraTableViewHeaderSkin.java (original)
+++ incubator/pivot/trunk/wtk/src/org/apache/pivot/wtk/skin/terra/TerraTableViewHeaderSkin.java Thu Sep 17 17:43:41 2009
@@ -39,6 +39,7 @@
 import org.apache.pivot.wtk.TableViewHeader;
 import org.apache.pivot.wtk.TableViewHeaderListener;
 import org.apache.pivot.wtk.Theme;
+import org.apache.pivot.wtk.TableViewHeader.SortMode;
 import org.apache.pivot.wtk.media.Image;
 import org.apache.pivot.wtk.skin.ComponentSkin;
 
@@ -777,6 +778,11 @@
         invalidateComponent();
     }
 
+    @Override
+    public void sortModeChanged(TableViewHeader tableViewHeader, SortMode previousSortMode) {
+        // No-op
+    }
+
     // Table view column events
     @Override
     public void columnInserted(TableView tableView, int index) {