package org.marketcetera.photon.internal.positions.ui.glazed;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.marketcetera.util.misc.ClassVersion;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.SortedList;
import ca.odell.glazedlists.gui.AbstractTableComparatorChooser;
import ca.odell.glazedlists.impl.gui.MouseOnlySortingStrategy;
import ca.odell.glazedlists.impl.gui.SortingStrategy;
import ca.odell.glazedlists.swt.TableComparatorChooser;
/* $License$ */
/**
* A TreeComparatorChooser is a tool that allows the user to sort a ListTable by clicking
* on the table's headers. It requires that the ListTable has a SortedList as
* a source as the sorting on that list is used.
*
* Modeled after the Glazed Lists {@link TableComparatorChooser}.
*
* @see TableComparatorChooser
*
* @author <a href="mailto:will@marketcetera.com">Will Horn</a>
* @version $Id: TreeComparatorChooser.java 16154 2012-07-14 16:34:05Z colin $
* @since 1.5.0
*/
@ClassVersion("$Id: TreeComparatorChooser.java 16154 2012-07-14 16:34:05Z colin $")
public final class TreeComparatorChooser<E> extends AbstractTableComparatorChooser<E> {
private final SortingStrategy sortingStrategy;
/** the tree being sorted */
private Tree tree;
/** listeners to sort change events */
private List<Listener> sortListeners = new ArrayList<Listener>();
/** listeners for column headers */
private ColumnListener columnListener = new ColumnListener();
/**
* Creates and installs a TreeComparatorChooser.
*/
private TreeComparatorChooser(EventTreeViewer<E> eventTreeViewer, SortedList<E> sortedList, boolean multipleColumnSort) {
super(sortedList, eventTreeViewer.getTableFormat());
// save the SWT-specific state
this.tree = eventTreeViewer.getTree();
// listen for events on the specified table
for(int c = 0; c < tree.getColumnCount(); c++) {
tree.getColumn(c).addSelectionListener(columnListener);
}
// sort using the specified approach
sortingStrategy = new MouseOnlySortingStrategy(multipleColumnSort);
}
/**
* Installs a new TreeComparatorChooser that responds to clicks
* on the specified tree and uses them to sort the specified list.
*
* @param eventTreeViewer the tree viewer for the tree to be sorted
* @param sortedList the sorted list to update.
* @param multipleColumnSort <code>true</code> to sort by multiple columns
* at a time, or <code>false</code> to sort by a single column. Although
* sorting by multiple columns is more powerful, the user interface is
* not as simple and this strategy should only be used where necessary.
*/
public static <E> TreeComparatorChooser<E> install(EventTreeViewer<E> eventTreeViewer, SortedList<E> sortedList, boolean multipleColumnSort) {
return new TreeComparatorChooser<E>(eventTreeViewer, sortedList, multipleColumnSort);
}
/**
* Registers the specified {@link Listener} to receive notification whenever
* the {@link Table} is sorted by this {@link TreeComparatorChooser}.
*/
public void addSortListener(final Listener sortListener) {
sortListeners.add(sortListener);
}
/**
* Deregisters the specified {@link Listener} to no longer receive events.
*/
public void removeSortActionListener(final Listener sortListener) {
for(Iterator<Listener> i = sortListeners.iterator(); i.hasNext(); ) {
if(sortListener == i.next()) {
i.remove();
return;
}
}
throw new IllegalArgumentException("Cannot remove nonexistent listener " + sortListener); //$NON-NLS-1$
}
/**
* Handles column clicks.
*/
class ColumnListener implements org.eclipse.swt.events.SelectionListener {
public void widgetSelected(SelectionEvent e) {
TreeColumn column = (TreeColumn)e.widget;
int columnIndex = tree.indexOf(column);
sortingStrategy.columnClicked(sortingState, columnIndex, 1, false, false);
}
public void widgetDefaultSelected(SelectionEvent e) {
// Do Nothing
}
}
/**
* Updates the SWT table to indicate sorting icon on the primary sort column.
*/
protected final void updateTableSortColumn() {
final List<Integer> sortedColumns = getSortingColumns();
if (sortedColumns.isEmpty()) {
// no columns sorted
tree.setSortColumn(null);
tree.setSortDirection(SWT.NONE);
} else {
// make GL primary sort column the SWT table sort column
final int primaryColumnIndex = sortedColumns.get(0).intValue();
final int sortDirection = isColumnReverse(primaryColumnIndex) ? SWT.DOWN : SWT.UP;
tree.setSortColumn(tree.getColumn(primaryColumnIndex));
tree.setSortDirection(sortDirection);
}
}
/**
* Updates the comparator in use and applies it to the table.
*
* <p>This method is called when the sorting state changed.</p>
*/
protected final void rebuildComparator() {
super.rebuildComparator();
// update sorting icon in SWT table
updateTableSortColumn();
// notify interested listeners that the sorting has changed
Event sortEvent = new Event();
sortEvent.widget = tree;
for(Iterator<Listener> i = sortListeners.iterator(); i.hasNext(); ) {
i.next().handleEvent(sortEvent);
}
}
/**
* Releases the resources consumed by this {@link TreeComparatorChooser} so that it
* may eventually be garbage collected.
*
* <p>A {@link TreeComparatorChooser} will be garbage collected without a call to
* {@link #dispose()}, but not before its source {@link EventList} is garbage
* collected. By calling {@link #dispose()}, you allow the {@link TreeComparatorChooser}
* to be garbage collected before its source {@link EventList}. This is
* necessary for situations where an {@link TreeComparatorChooser} is short-lived but
* its source {@link EventList} is long-lived.
*
* <p><strong><font color="#FF0000">Warning:</font></strong> It is an error
* to call any method on a {@link TreeComparatorChooser} after it has been disposed.
*/
public void dispose() {
// stop listening for events on the specified table
for(int c = 0; c < tree.getColumnCount(); c++) {
tree.getColumn(c).removeSelectionListener(columnListener);
}
}
}