package org.limewire.ui.swing.player; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.limewire.core.api.library.LocalFileItem; import org.limewire.listener.SwingEDTEvent; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.event.ListEvent; import ca.odell.glazedlists.event.ListEventListener; /** * Contains a reference to an EventList that is used for playing * a list of audio file automatically. Since the backing of the * playlist is an EventList, any changes on the list will be reflected * within the playlist automatically. */ class Playlist { /** File item for the last opened song. */ private LocalFileItem currentFileItem = null; /** Indicator for shuffle mode. */ private boolean shuffle = false; /** Current list of songs. Null if no list is selected. */ private EventList<LocalFileItem> playlist = null; private List<LocalFileItem> shuffleList = Collections.emptyList(); private ListChanges listChanges = null; /** * Sets the current FileItem that is playing. If no FileItem exists for * the item being played, can be set to null. */ public void setCurrentItem(LocalFileItem fileItem) { this.currentFileItem = fileItem; } /** * Sets the current EventList that is being used as the playlist. If * no playlist exists, this can be set to null. */ public void setActivePlaylist(EventList<LocalFileItem> fileList) { if(playlist != null) { removeListener(); playlist.dispose(); } this.playlist = fileList; if(shuffle) { createShuffleList(); addListener(); } } /** * Returns true if shuffle moad is set, false otherwise. */ public boolean isShuffle() { return shuffle; } /** * Returns the next file item in the current playlist. If no next FileItem * exists, returns null. */ public LocalFileItem getNextFileItem() { if(!isValidPlaylist()) return null; if(shuffle) { currentFileItem = getNextItem(shuffleList, true); } else { currentFileItem = getNextItem(playlist, true); } return currentFileItem; } /** * Returns the previous file item in the current playlist. If no previous item * exists, returns null. */ public LocalFileItem getPrevFileItem() { if(!isValidPlaylist()) return null; if(shuffle) { currentFileItem = getNextItem(shuffleList, false); } else { currentFileItem = getNextItem(playlist, false); } return currentFileItem; } private boolean isValidPlaylist() { return playlist != null && currentFileItem != null && playlist.size() > 0; } /** * Returns the size of the current playlist. If no playlist * is set returns 0. */ public int size() { if(playlist == null) return 0; else return playlist.size(); } /** * Returns the next item in the given list. If isForward is true, will * return the next item that is incremented. If isForward is false, will * return the next item decremented by the counter. */ private LocalFileItem getNextItem(List<LocalFileItem> list, boolean isForward) { if(list == null) return null; int index = list.indexOf(currentFileItem); if(isForward) { if (index >= 0 && index < (list.size() - 1)) { return list.get(index + 1); } else { return null; } } else { if (index > 0 && index < (list.size())) { return list.get(index - 1); } else { return null; } } } /** * Updates the shuffle state. */ public void setShuffle(boolean shuffle) { this.shuffle = shuffle; // Update shuffle list. if (shuffle) { createShuffleList(); addListener(); } else { shuffleList = Collections.emptyList(); removeListener(); } } /** * Creates and adds a listener to the playlist if it doesn't * already exist. */ private void addListener() { if(playlist != null && listChanges == null) { listChanges = new ListChanges(); playlist.addListEventListener(listChanges); } } /** * Removes the listener from the playlist if one exists. */ private void removeListener() { if(playlist != null && listChanges != null) { playlist.removeListEventListener(listChanges); listChanges = null; } } /** * Creates a ShuffleList based on the current files within the * playlist. If the playlist is changed, this list must be recreated. */ private void createShuffleList() { shuffleList = new ArrayList<LocalFileItem>(); if(playlist != null && playlist.size() > 0 && currentFileItem != null) { playlist.getReadWriteLock().readLock().lock(); try { for (Iterator<LocalFileItem> iter = playlist.iterator(); iter.hasNext();) { shuffleList.add(iter.next()); } } finally { playlist.getReadWriteLock().readLock().unlock(); } Collections.shuffle(shuffleList); int index = shuffleList.indexOf(currentFileItem); if (index > 0) { shuffleList.remove(index); shuffleList.add(0, currentFileItem); } } } /** * Listens to changes in the playlist. Recreates the shuffleList * if this list changes. */ private class ListChanges implements ListEventListener<LocalFileItem> { @Override @SwingEDTEvent public void listChanged(ListEvent<LocalFileItem> listChanges) { createShuffleList(); } } }