All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ca.odell.glazedlists.swt.TableCheckFilterList Maven / Gradle / Ivy

/* Glazed Lists                                                 (c) 2003-2006 */
/*                                                     O'Dell Engineering Ltd.*/
/* http://publicobject.com/glazedlists/                      publicobject.com,*/
package ca.odell.glazedlists.swt;

import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.FilterList;
import ca.odell.glazedlists.TransformedList;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventListener;
import ca.odell.glazedlists.gui.CheckableTableFormat;
import ca.odell.glazedlists.gui.TableFormat;
import ca.odell.glazedlists.matchers.AbstractMatcherEditor;
import ca.odell.glazedlists.matchers.Matcher;
import ca.odell.glazedlists.matchers.Matchers;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;

/**
 * A FilterList for elements that are checked in the EventTableViewer.
 *
 * 

The TableCheckFilterList must be used as the source list to * the EventTableViewer. This is because the TableCheckFilterList uses methods on * the Table that depend on table indices. * *

This class is not thread safe. It must be used exclusively with the SWT * event handler thread. * * @author Jesse Wilson */ final class TableCheckFilterList extends TransformedList implements SelectionListener { /** filter out unchecked elements */ private final CheckMatcherEditor checkMatcherEditor = new CheckMatcherEditor(); /** the table that checkboxes are displayed in */ private final Table table; /** for checking table items */ private final CheckableTableFormat checkableTableFormat; /** Retain a reference to the CheckableWrapperList, if we create one, so it can be disposed */ private CheckableWrapperList checkableWrapperList; /** Retain a reference to the FilterList so it can be disposed */ private FilterList filterList; /** Retain a reference to the TableChecker, if we create one, so it can be disposed */ private TableChecker tableChecker; /** * Creates a new filter that filters elements depending on whether they are * checked in the table. * * @param source the items to decorate with checkability * @param table the checkable table * @param tableFormat if this class implements {@link CheckableTableFormat} * it will be used to store check state. Otherwise check state will be * stored transiently within this class' state. */ public TableCheckFilterList(EventList source, Table table, TableFormat tableFormat) { this(table, tableFormat, tableFormat instanceof CheckableTableFormat ? source : new CheckableWrapperList(source)); } /** * A hidden constructor that allows us to store a reference to the source of * the FilterList, which we may have created and must dispose later. * * @param table the checkable table * @param tableFormat the format of the checkable table * @param filterListSource the source of the FilterList that backs this TableCheckFilterList */ private TableCheckFilterList(Table table, TableFormat tableFormat, EventList filterListSource) { this(new FilterList(filterListSource, Matchers.trueMatcher()), table, tableFormat); // if a CheckableWrapperList was created, store a reference so it can be disposed later if (filterListSource instanceof CheckableWrapperList) checkableWrapperList = (CheckableWrapperList) filterListSource; } /** * A hidden constructor that allows us to store a reference to the * FilterList, which we created and must dispose later. * * @param filterList the FilterList that backs this TableCheckFilterList * @param table the checkable table * @param tableFormat the format of the checkable table */ private TableCheckFilterList(FilterList filterList, Table table, TableFormat tableFormat) { super(filterList); this.filterList = filterList; this.table = table; this.checkableTableFormat = tableFormat instanceof CheckableTableFormat ? (CheckableTableFormat) tableFormat : null; // listen for changes in checkedness table.addSelectionListener(this); // prepare the filter filterList.setMatcherEditor(checkMatcherEditor); // handle changes filterList.addListEventListener(this); } /** * @return false; TableCheckFilterList is readonly */ @Override protected boolean isWritable() { return false; } /** * Set the specified list element in the source list as checked. */ private void setChecked(Object element, boolean checked) { if(checkableTableFormat != null) { checkableTableFormat.setChecked(element, checked); } else { ((CheckWrapped)element).setChecked(checked); } } /** * Set the specified index in the filtered list as checked. */ private void setChecked(int index, boolean checked) { setChecked(source.get(getSourceIndex(index)), checked); } /** * Get whether the specified element in the source list is checked. */ private boolean getChecked(Object element) { if(checkableTableFormat != null) { return checkableTableFormat.getChecked(element); } else { return ((CheckWrapped)element).getChecked(); } } /** * Get whether the specified index in the filtered list is checked. */ private boolean getChecked(int index) { return getChecked(source.get(getSourceIndex(index))); } /** * Match checked elements. */ private class CheckMatcherEditor extends AbstractMatcherEditor { private boolean checkedOnly = false; private void setCheckedOnly(boolean checkedOnly) { if(checkedOnly == this.checkedOnly) return; if(checkedOnly) fireConstrained(new CheckMatcher()); else fireMatchAll(); } private boolean getCheckedOnly() { return checkedOnly; } private class CheckMatcher implements Matcher { public boolean matches(V element) { return getChecked(element); } } } /** * Gets a static snapshot of the checked Objects in this list. */ public List getAllChecked() { final List result = new ArrayList(); for(int i = 0, n = size(); i < n; i++) { if(getChecked(i)) { result.add(get(i)); } } return result; } /** * Set whether this filter list displays all elements, or only checked elements. */ public void setCheckedOnly(boolean checkedOnly) { checkMatcherEditor.setCheckedOnly(checkedOnly); } /** * Get whether this filter list displays all elements, or only checked elements. */ public boolean getCheckedOnly() { return checkMatcherEditor.getCheckedOnly(); } /** * Returns the element at the specified position in this list. This unwraps * a {@link CheckWrapped} object from the source if necessary. */ @Override public E get(int index) { if(checkableTableFormat != null) { return super.get(index); } else { CheckWrapped checkWrapped = (CheckWrapped)super.get(index); return checkWrapped.getWrapped(); } } /** {@inheritDoc} */ @Override public void listChanged(ListEvent listChanges) { updates.forwardEvent(listChanges); } /** * Sent when selection occurs in the control. */ public void widgetSelected(SelectionEvent e) { if(e.detail == SWT.CHECK) { getReadWriteLock().writeLock().lock(); try { updateItemChecked((TableItem)e.item); } finally { getReadWriteLock().writeLock().unlock(); } } } public void widgetDefaultSelected(SelectionEvent e) { if(e.detail == SWT.CHECK) { getReadWriteLock().writeLock().lock(); try { updateItemChecked((TableItem)e.item); } finally { getReadWriteLock().writeLock().unlock(); } } } /** * When the check status of a table item is changed, this changes the * checked property of the corresponding CheckWrapped object. * *

This uses a very lame hack to get the index of the specified TableItem. * It first uses the Table.indexOf(TableItem) method, and then attempts to get * the unfiltered index of that. This is only guaranteed to work in the case * where the TableCheckFilterList is the source of the EventTableViewer. * Otherwise everything may blow up because the list indices cannot be looked up. */ private void updateItemChecked(TableItem updated) { if(updated == null) return; // set the checked property on the proper element int index = table.indexOf(updated); boolean checked = updated.getChecked(); setChecked(index, checked); // force an update event int sourceIndex = getSourceIndex(index); source.set(sourceIndex, source.get(sourceIndex)); } @Override public void addListEventListener(ListEventListener listChangeListener) { super.addListEventListener(listChangeListener); // also adjust the table's checked rows if(listChangeListener instanceof DefaultEventTableViewer) { tableChecker = new TableChecker(); super.addListEventListener(tableChecker); } } /** {@inheritDoc} */ @Override public void dispose() { if (checkableWrapperList != null) checkableWrapperList.dispose(); if (tableChecker != null) removeListEventListener(tableChecker); filterList.dispose(); super.dispose(); } /** * The TableChecker checks the table rows after they have been updated. */ private class TableChecker implements ListEventListener { public TableChecker() { for(int i = 0; i < size(); i++) { boolean checked = getChecked(i); table.getItem(i).setChecked(checked); } } public void listChanged(ListEvent listChanges) { while(listChanges.next()) { int changeIndex = listChanges.getIndex(); int changeType = listChanges.getType(); if(changeType == ListEvent.INSERT || changeType == ListEvent.UPDATE) { boolean checked = getChecked(changeIndex); table.getItem(changeIndex).setChecked(checked); } } } } } /** * A simple wrapper list that makes all elements Checkable. * *

This maintains a separate collection that mirrors the source collection. * This collection contains only CheckWrapped objects. For each element in the * source list at index i, that element is wrapped in the mirror list at index i. * *

The mirror collection is maintained in the listChanged() method only. * All get() calls return from the mirror collection rather than from the source * list. */ class CheckableWrapperList extends TransformedList> { /** wrapped list contains CheckWrapped elements only */ private List> wrappedSource = new ArrayList>(); public CheckableWrapperList(EventList source) { super(source); prepareElements(); source.addListEventListener(this); } /** * @return false; CheckableWrapperList is readonly */ @Override protected boolean isWritable() { return false; } /** * The CheckableWrapperList supports only one write operation, which is to * force an update on a specified value. This requires that the parameter * value is an instance of CheckWrapped, which is the only value that this * list supports. */ @Override public CheckWrapped set(int index, CheckWrapped value) { source.set(index, value.getWrapped()); return value; } private void prepareElements() { for(int i = 0, n = source.size(); i < n; i++) { wrappedSource.add(i, new CheckWrapped(source.get(i))); } } @Override public CheckWrapped get(int index) { return wrappedSource.get(index); } @Override public void listChanged(ListEvent listChanges) { updates.beginEvent(); while (listChanges.next()) { final int changeIndex = listChanges.getIndex(); final int changeType = listChanges.getType(); switch (changeType) { case ListEvent.INSERT: wrappedSource.add(changeIndex, new CheckWrapped(source.get(changeIndex))); break; case ListEvent.UPDATE: wrappedSource.get(changeIndex).setWrapped(source.get(changeIndex)); break; case ListEvent.DELETE: wrappedSource.remove(changeIndex); break; } updates.addChange(changeType, changeIndex); } updates.commitEvent(); } } /** * A simple wrapper that adds a checked property to an Object. */ class CheckWrapped { private boolean checked = false; private E wrapped = null; public CheckWrapped(E wrapped) { this.wrapped = wrapped; } public E getWrapped() { return wrapped; } public void setWrapped(E wrapped) { this.wrapped = wrapped; } public boolean getChecked() { return checked; } public void setChecked(boolean checked) { this.checked = checked; } @Override public String toString() { if(checked) return "[*] " + wrapped; else return "[ ] " + wrapped; } }