// This file is part of Penn TotalRecall <http://memory.psych.upenn.edu/TotalRecall>. // // TotalRecall is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 3 only. // // TotalRecall is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with TotalRecall. If not, see <http://www.gnu.org/licenses/>. package components.audiofiles; import java.util.HashSet; import java.util.List; import javax.swing.ListModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; import util.MyCollection; /** * A very simple <code>ListModel</code> that guarantees elements remain sorted and without repetitions. * * The sorting strategy anticipates that any time many files are added consecutively, they will be added as a batch. * This corresponds to the user opening a directory with <code>OpenAudioLocationAction</code> or dragging many files (or a directory) onto the application. * To encourage fast sorting, no method is provided for adding one element individually. * * @author Yuvi Masory * */ // This class assumes that the ListDataListener (often javax.swing.plaf.basic.BasicListUI$Handler by default), // will repaint the AudioFileList after ListDataEvents>. public class AudioFileListModel implements ListModel, ChangeListener { private MyCollection<AudioFile> collection; private HashSet<ListDataListener> listeners; /** * Creates a new <code>AudioFileListModel</code>. */ protected AudioFileListModel() { listeners = new HashSet<ListDataListener>(); collection = new MyCollection<AudioFile>(); } /** * {@inheritDoc} */ public AudioFile getElementAt(int index) { if(index < 0 || index >= collection.size()) { return null; } return collection.get(index); } /** * Removes the element at the provided index. * * Since removing elements cannot make a sorted list unsorted, no sorting is performed after the removal. * However, all registered listeners are notified of a <code>ListDataEvent.INTERVAL_REMOVED</code> event. * * @param index The index of the element to be removed */ public void removeElementAt(int index) { if(index < 0 || index >= collection.size()) { throw new IllegalArgumentException("index not in file set: " + index); } collection.linearRemoveAt(index).removeAllChangeListeners(); ListDataEvent e = new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, index, index); for(ListDataListener ldl: listeners) { ldl.contentsChanged(e); } } /** * Adds files to the list, skipping <code>AudioFiles</code> already in the list. * * Sets this <code>AudioFileListModel</code> to listen to completion status changes in any files added. * After all files are added, the list is re-sorted using {@link java.util.Collections#sort(List)}. * * @param files An iterable collection of the files to add */ public void addElements(Iterable<AudioFile> files) { for(AudioFile file: files) { file.addChangeListener(this); collection.add(file); } collection.sort(); //we fire a CONTENTS_CHANGED event instead of INTERVAL_ADDED, because after sorting there is no guarantee that a clean interval is all that has been changed ListDataEvent e = new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, 0, collection.size()); for(ListDataListener ldl: listeners) { ldl.contentsChanged(e); } } /** * Finds the number of <code>AudioFiles</code> in the model. * * @return The size of the list containing the <code>AudioFiles</code> */ public int getSize() { return collection.size(); } /** * Handler for changes of completion status in <code>AudioFiles</code>. * * Re-sorts the data using {@link java.util.Collections#sort(List)} and requests a repaint. */ public void stateChanged(ChangeEvent e) { collection.sort(); for(ListDataListener ldl: listeners) { ldl.contentsChanged(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, 0, collection.size())); } } /** * {@inheritDoc} */ public void addListDataListener(ListDataListener l) { listeners.add(l); } /** * {@inheritDoc} */ public void removeListDataListener(ListDataListener l) { listeners.remove(l); } }