Incomplete code is available in a Java archive.
TrackDisplay.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TrackDisplay extends JFrame
{
// these components are referred to by name in the event handlers
// and so need to be declared as fields
private JTextField trackNameField;
private JCheckBox shuffleBox;
private TrackList tracks;
public TrackDisplay()
{
super("MP3 Player");
tracks = new TrackList("file:tracks.txt");
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
// create a text field to show the current track
trackNameField = new JTextField();
trackNameField.setEditable(false);
trackNameField.setText(tracks.getCurrentTrackName());
// make the panel that will go along the bottom edge (SOUTH) of
// the window
JPanel buttonPanel = new JPanel(new GridLayout(1, 3));
// make the checkbox and buttons and register event handlers
// for the events they generate
shuffleBox = new JCheckBox("Shuffle");
shuffleBox.addItemListener(new ShuffleListener());
JButton prevButton = new JButton("Previous");
prevButton.addActionListener(new PreviousListener());
JButton nextButton = new JButton("Next");
nextButton.addActionListener(new NextListener());
// add those three components to the panel
buttonPanel.add(shuffleBox);
buttonPanel.add(prevButton);
buttonPanel.add(nextButton);
// add the text field and the panel with the other 3 components to
// the window
cp.add(trackNameField, BorderLayout.CENTER);
cp.add(buttonPanel, BorderLayout.SOUTH);
setSize(300, 75);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
// by declaring the following three event-handling classes as inner
// classes, we automatically get access to the fields of the outer class
// (and the methods too, although we do not use them here)
/**
* The event handler for the "Previous" button.
*/
private class PreviousListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
tracks.goToPreviousTrack();
trackNameField.setText(tracks.getCurrentTrackName());
}
}
/**
* The event handler for the "Next" button.
*/
private class NextListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
tracks.goToNextTrack();
trackNameField.setText(tracks.getCurrentTrackName());
}
}
/**
* The event handler for the "Shuffle" check box.
*/
private class ShuffleListener implements ItemListener
{
public void itemStateChanged(ItemEvent e)
{
tracks.setShuffle(shuffleBox.isSelected());
trackNameField.setText(tracks.getCurrentTrackName());
}
}
public static void main(String[] args)
{
new TrackDisplay();
}
}
TrackList.java
import java.util.*;
import java.io.*;
/**
* A list of tracks that can be iterated through forwards or backwards.
* For the purposes of iterating through the tracks, "next" either means
* next in sequential order or, if shuffle mode has been selected,
* another randomly selected track (take heed, Toyota!). In shuffle mode,
* "previous" means the previously played track, although this implemention
* puts a cap on the number of previous tracks it remembers.
*
* @author Jim Glenn
* @version 0.1 3/19/2008
*/
public class TrackList
{
/**
* The list of track names.
*/
private List< String > tracks;
/**
* The current shuffle order, given as indices into <CODE>tracks</CODE>.
*/
private List< Integer > shuffleOrder;
/**
* The index of the current track, as an index into <CODE>tracks</CODE>
* or <CODE>shuffleOrder</CODE> depending on whether shuffle mode is
* active or not.
*/
private int currentTrack;
/**
* A flag set to true if shuffle mode is active.
*/
private boolean shuffle;
/**
* Creates a list of tracks from the file with the given name. The
* file should have one track name per line; the tracks will be stored
* in this list in the same order they were listed in the file.
*
* @param fname the name of a file containing track names
*/
public TrackList(String fname)
{
try
{
currentTrack = 0;
shuffle = false;
shuffleOrder = null;
tracks = new LinkedList< String >();
BufferedReader in = new BufferedReader(new FileReader(fname));
String line;
while ((line = in.readLine()) != null)
tracks.add(line);
in.close();
}
catch (IOException e)
{
// Hey! Someone put some error handling in here, please!
}
}
/**
* Returns the index of the current track.
*
* @return the index of the current track
*/
public int getCurrentTrackNumber()
{
if (shuffle)
return shuffleOrder.get(currentTrack);
else
return currentTrack;
}
/**
* Returns the name of the current track.
*
* @return the name of the current track
*/
public String getCurrentTrackName()
{
return tracks.get(getCurrentTrackNumber());
}
/**
* Turns shuffle mode on or off. When shuffle mode is turned on after
* being off, the current track is changed to a randomly selected one.
*
* @param newSetting the new shuffle setting; <CODE>true</CODE> for on
*/
public void setShuffle(boolean newSetting)
{
if (shuffle != newSetting)
{
shuffle = newSetting;
if (shuffle)
{
shuffleOrder = makeRandomTrackList();
currentTrack = 0;
}
else
{
// change from index into shuffleOrder to index
// into tracks
currentTrack = shuffleOrder.get(currentTrack);
}
}
}
/**
* Returns a randomly ordered list of track numbers.
*/
private List< Integer > makeRandomTrackList()
{
// make list of track numbers (indices into tracks list),
// initially 0, ..., length - 1
List< Integer > order = new LinkedList< Integer >();
for (int i = 0; i < tracks.size(); i++)
order.add(i);
// randomize by swapping each position with a randomly chosen position
for (int i = 0; i < tracks.size(); i++)
{
int swapPos = (int)(Math.random() * tracks.size());
int temp = order.get(i);
order.set(i, order.get(swapPos));
order.set(swapPos, temp);
}
return order;
}
/**
* Selects the next track in this list. In shuffle mode this is
* a randomly selected track. With shuffle mode off, this is the
* next track in the original list, wrapping around to the beginning.
*/
public void goToNextTrack()
{
if (shuffle)
{
currentTrack++;
// check if we've run through the shuffled list...
if (currentTrack >= shuffleOrder.size())
{
// ...and make a new list if we have
List< Integer > newOrder = makeRandomTrackList();
// Prevent the last track from being played twice
// in a row by removing it from the new list
// (note that the last tracks.size() entries on
// shuffleOrder are still all the valid indices).
// Note that the next-to-last track may endf up being
// at the front of newOrder, so we might play it
// twice with only one track between. To avoid
// such problems we could extend this to remove
// the last 10% of indices from the new list.
// (To anyone seeking a patent on "A Method for
// Randomizing Track Lists that Avoids Duplications"
// or some such nonsense: this entire piece of
// code took me 30 minutes to write, so any such
// invention clearly fails the non-obviousness test.)
newOrder.remove(new Integer(shuffleOrder.get(shuffleOrder.size() - 1)));
shuffleOrder.addAll(newOrder);
// forget tracks in old shuffles (to save space)
if (shuffleOrder.size() > 2 * tracks.size())
{
for (int i = 0; i < tracks.size() - 1; i++)
shuffleOrder.remove(0);
currentTrack -= (tracks.size() - 1);
}
}
}
else
{
currentTrack = (currentTrack + 1) % tracks.size();
}
}
/**
* Selects the previous track in this list. In shuffle mode this
* is the previously played track, up to the limit of saved
* history. If the limit has been reached, this method does
* nothing. With shuffle mode off, the new track is the previous
* one in the original list, wrapping around to the end.
*/
public void goToPreviousTrack()
{
if (shuffle)
{
currentTrack = Math.max(0, currentTrack - 1);
}
else
{
currentTrack = (currentTrack + tracks.size() - 1) % tracks.size();
}
}
public String toString()
{
StringBuffer result = new StringBuffer();
if (shuffle)
{
for (int i = 0; i < shuffleOrder.size(); i++)
{
if (currentTrack == i)
result.append(">" + tracks.get(shuffleOrder.get(i)) + "<\n");
else
result.append(" " + tracks.get(shuffleOrder.get(i)) + " \n");
}
}
else
{
for (int i = 0; i < tracks.size(); i++)
{
if (currentTrack == i)
result.append(">" + tracks.get(i) + "<\n");
else
result.append(" " + tracks.get(i) + " \n");
}
}
return result.toString();
}
}
tracks.txt
Muse - Exopolitics
Public Enemy - They Call Me Flavor
Oak Ridge Boys - Elvira
Celine Dion - My Heart Will Go On
Radiohead - 4 Minute Warning
Cam'ron - Killa Cam
Usher - Caught Up
Interpol - Rest My Chemistry
Gwen Stefani - Early Winter
The Beatles - I Am the Walrus
This code can also be downloaded from the files
TrackDisplay.java,
TrackList.java,
and tracks.txt.