package org.marketcetera.photon.ui; 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.TableColumn; import org.marketcetera.photon.Messages; 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.gui.TableFormat; import ca.odell.glazedlists.swt.EventTableViewer; /** * Copy of {@link ca.odell.glazedlists.swt.TableComparatorChooser} that does not require a {@link EventTableViewer}. * * Upgrade note: {@link Messages#TABLE_COMPARATOR_CHOOSER_INVALID_LISTENER} was used to externalize a string. * * @see ca.odell.glazedlists.swt.TableComparatorChooser * * @author <a href="mailto:will@marketcetera.com">Will Horn</a> * @version $Id: TableComparatorChooser.java 16841 2014-02-20 19:59:04Z colin $ * @since 1.5.0 */ @SuppressWarnings("deprecation") @ClassVersion("$Id: TableComparatorChooser.java 16841 2014-02-20 19:59:04Z colin $") public final class TableComparatorChooser<E> extends AbstractTableComparatorChooser<E> { private final Object sortingStrategy; /** the table being sorted */ private Table table; /** 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 TableComparatorChooser. */ private TableComparatorChooser(Table table, TableFormat<E> format, SortedList<E> sortedList, boolean multipleColumnSort) { super(sortedList, format); // save the SWT-specific state this.table = table; // listen for events on the specified table for(int c = 0; c < table.getColumnCount(); c++) { table.getColumn(c).addSelectionListener(columnListener); } // sort using the specified approach sortingStrategy = SINGLE_COLUMN; } /** * Installs a new TableComparatorChooser that responds to clicks * on the specified table and uses them to sort the specified list. * * @param table the table to be sorted * @param format the table format to be used * @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> TableComparatorChooser<E> install(Table table, TableFormat<E> format, SortedList<E> sortedList, boolean multipleColumnSort) { return new TableComparatorChooser<E>(table, format, sortedList, multipleColumnSort); } /** * Registers the specified {@link Listener} to receive notification whenever * the {@link Table} is sorted by this {@link TableComparatorChooser}. */ 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(Messages.TABLE_COMPARATOR_CHOOSER_INVALID_LISTENER.getText(sortListener)); } /** * Handles column clicks. */ class ColumnListener implements org.eclipse.swt.events.SelectionListener { public void widgetSelected(SelectionEvent e) { TableColumn column = (TableColumn)e.widget; Table table = column.getParent(); int columnIndex = table.indexOf(column); // Using reflection to access internal code try { sortingStrategy.getClass().getMethods()[0].invoke(sortingStrategy, sortingState, columnIndex, 1, false, false); } catch (Exception ex) { throw new RuntimeException(ex); } } 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 table.setSortColumn(null); table.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; table.setSortColumn(table.getColumn(primaryColumnIndex)); table.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 = table; for(Iterator<Listener> i = sortListeners.iterator(); i.hasNext(); ) { i.next().handleEvent(sortEvent); } } /** * Releases the resources consumed by this {@link TableComparatorChooser} so that it * may eventually be garbage collected. * * <p>A {@link TableComparatorChooser} 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 TableComparatorChooser} * to be garbage collected before its source {@link EventList}. This is * necessary for situations where an {@link TableComparatorChooser} 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 TableComparatorChooser} after it has been disposed. */ public void dispose() { // stop listening for events on the specified table for(int c = 0; c < table.getColumnCount(); c++) { table.getColumn(c).removeSelectionListener(columnListener); } } }