summaryrefslogtreecommitdiffstats
path: root/source/client/swing
diff options
context:
space:
mode:
Diffstat (limited to 'source/client/swing')
-rw-r--r--source/client/swing/Player.java59
-rw-r--r--source/client/swing/SongTableModel.java103
-rw-r--r--source/client/swing/SwingWorker.java133
-rw-r--r--source/client/swing/TableMap.java70
-rw-r--r--source/client/swing/TableSorter.java432
-rw-r--r--source/client/swing/ZApplet.java46
-rw-r--r--source/client/swing/ZPlayer.java506
7 files changed, 1349 insertions, 0 deletions
diff --git a/source/client/swing/Player.java b/source/client/swing/Player.java
new file mode 100644
index 0000000..7783544
--- /dev/null
+++ b/source/client/swing/Player.java
@@ -0,0 +1,59 @@
+package itunes.client.swing;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import javax.swing.JOptionPane;
+
+import itunes.client.swing.ZPlayer;
+public class Player extends Thread {
+ private javazoom.jl.player.Player p;
+ private ZPlayer prog;
+
+ public Player(ZPlayer prog, String fileName) {
+ this.prog = prog;
+ FileInputStream f=null;
+ try {
+ f = new FileInputStream(fileName);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ newPlayer(f);
+ }
+
+ private void newPlayer(InputStream f) {
+ if (p != null)
+ this.stopMusic();
+ try{
+ p = new javazoom.jl.player.Player(f);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public Player(ZPlayer prog, InputStream f) {
+ this.prog = prog;
+ newPlayer(f);
+ }
+
+ public void run() {
+ try {
+ p.play();
+ } catch (javazoom.jl.decoder.BitstreamException be) {
+ JOptionPane.showMessageDialog(prog.frame,"Unsupported file format!\n"+be.getLocalizedMessage());
+ prog.stopPlaying();
+ }catch (Exception e) {
+ e.printStackTrace();
+ prog.stopPlaying();
+ }
+ if (p.isComplete()) {
+ prog.playNext(1);
+ }
+ }
+
+ public int getPosition() {
+ return p.getPosition();
+ }
+
+ public void stopMusic() {
+ p.close();
+ }
+}
diff --git a/source/client/swing/SongTableModel.java b/source/client/swing/SongTableModel.java
new file mode 100644
index 0000000..d4bc78c
--- /dev/null
+++ b/source/client/swing/SongTableModel.java
@@ -0,0 +1,103 @@
+/*
+ * Created on May 8, 2003
+ *
+ * To change the template for this generated file go to
+ * Window>Preferences>Java>Code Generation>Code and Comments
+ *
+ * Copyright 2003 Joseph Barnett
+
+This File is part of "one 2 oh my god"
+
+"one 2 oh my god" is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+Free Software Foundation; either version 2 of the License, or
+your option) any later version.
+
+"one 2 oh my god" 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 "one 2 oh my god"; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ */
+package itunes.client.swing;
+
+import itunes.client.*;
+
+import javax.swing.table.AbstractTableModel;
+import java.util.ArrayList;
+
+/**
+ * @author jbarnett
+ *
+ * To change the template for this generated type comment go to
+ * Window>Preferences>Java>Code Generation>Code and Comments
+ */
+public class SongTableModel extends AbstractTableModel {
+ public final String[] columnNames = {"Artist","Album","Title","Track","Time"};
+ public ArrayList data;
+
+ public SongTableModel() {
+ super();
+ data = new ArrayList();
+ }
+
+ public String getColumnName(int col) {
+ return columnNames[col];
+ }
+
+ public void clear() {
+ data = new ArrayList();
+ }
+
+ public void AddRow(Song s) {
+ Object[] a = new Object[7];
+ a[0] = s.artist;
+ a[1] = s.album;
+ a[2] = s.name;
+ a[3] = new Integer(s.track);
+ int totalseconds = s.getTime();
+ if(totalseconds > 999)
+ {
+ totalseconds /= 1000;
+ }
+ int minutes = totalseconds / 60;
+ int seconds = totalseconds - minutes*60;
+ a[4] = (minutes == 0 && seconds == 0) ? "???" : ((minutes<10)?"0":"") + minutes + ":" + ((seconds<10)?"0":"") + seconds;
+ a[5] = Integer.toString(s.id);
+ a[6] = s.format;
+ data.add(a);
+ }
+
+ public int getRowCount() {
+ return data.size();
+ }
+
+ public int getColumnCount() {
+ return columnNames.length;
+ }
+
+ public Class getColumnClass(int c) {
+ return getValueAt(0, c).getClass();
+ }
+
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ return ((Object[])data.get(rowIndex))[columnIndex];
+ }
+ public String getArtistAt(int row) {
+ return (String)getValueAt(row,0);
+ }
+
+ public String getAlbumAt(int row) {
+ return (String)getValueAt(row,1);
+ }
+
+ public String getSongNameAt(int row) {
+ return (String)getValueAt(row,2);
+ }
+
+}
+
diff --git a/source/client/swing/SwingWorker.java b/source/client/swing/SwingWorker.java
new file mode 100644
index 0000000..d52a0e5
--- /dev/null
+++ b/source/client/swing/SwingWorker.java
@@ -0,0 +1,133 @@
+package itunes.client.swing;
+
+import javax.swing.SwingUtilities;
+
+/**
+ * This is the 3rd version of SwingWorker (also known as
+ * SwingWorker 3), an abstract class that you subclass to
+ * perform GUI-related work in a dedicated thread. For
+ * instructions on using this class, see:
+ *
+ * http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html
+ *
+ * Note that the API changed slightly in the 3rd version:
+ * You must now invoke start() on the SwingWorker after
+ * creating it.
+ */
+public abstract class SwingWorker {
+ private Object value; // see getValue(), setValue()
+ private Thread thread;
+
+ /**
+ * Class to maintain reference to current worker thread
+ * under separate synchronization control.
+ */
+ private static class ThreadVar {
+ private Thread thread;
+ ThreadVar(Thread t) { thread = t; }
+ synchronized Thread get() { return thread; }
+ synchronized void clear() { thread = null; }
+ }
+
+ private ThreadVar threadVar;
+
+ /**
+ * Get the value produced by the worker thread, or null if it
+ * hasn't been constructed yet.
+ */
+ protected synchronized Object getValue() {
+ return value;
+ }
+
+ /**
+ * Set the value produced by worker thread
+ */
+ private synchronized void setValue(Object x) {
+ value = x;
+ }
+
+ /**
+ * Compute the value to be returned by the <code>get</code> method.
+ */
+ public abstract Object construct();
+
+ /**
+ * Called on the event dispatching thread (not on the worker thread)
+ * after the <code>construct</code> method has returned.
+ */
+ public void finished() {
+ }
+
+ /**
+ * A new method that interrupts the worker thread. Call this method
+ * to force the worker to stop what it's doing.
+ */
+ public void interrupt() {
+ Thread t = threadVar.get();
+ if (t != null) {
+ t.interrupt();
+ }
+ threadVar.clear();
+ }
+
+ /**
+ * Return the value created by the <code>construct</code> method.
+ * Returns null if either the constructing thread or the current
+ * thread was interrupted before a value was produced.
+ *
+ * @return the value created by the <code>construct</code> method
+ */
+ public Object get() {
+ while (true) {
+ Thread t = threadVar.get();
+ if (t == null) {
+ return getValue();
+ }
+ try {
+ t.join();
+ }
+ catch (InterruptedException e) {
+ Thread.currentThread().interrupt(); // propagate
+ return null;
+ }
+ }
+ }
+
+
+ /**
+ * Start a thread that will call the <code>construct</code> method
+ * and then exit.
+ */
+ public SwingWorker() {
+ final Runnable doFinished = new Runnable() {
+ public void run() { finished(); }
+ };
+
+ Runnable doConstruct = new Runnable() {
+ public void run() {
+ try {
+ setValue(construct());
+ }
+ finally {
+ threadVar.clear();
+ }
+
+ SwingUtilities.invokeLater(doFinished);
+ }
+ };
+
+ Thread t = new Thread(doConstruct);
+ threadVar = new ThreadVar(t);
+ }
+
+ /**
+ * Start the worker thread.
+ */
+ public void start() {
+ Thread t = threadVar.get();
+ if (t != null) {
+ t.start();
+ }
+ }
+}
+
diff --git a/source/client/swing/TableMap.java b/source/client/swing/TableMap.java
new file mode 100644
index 0000000..fa85aa9
--- /dev/null
+++ b/source/client/swing/TableMap.java
@@ -0,0 +1,70 @@
+package itunes.client.swing;
+
+/**
+ * In a chain of data manipulators some behaviour is common. TableMap
+ * provides most of this behavour and can be subclassed by filters
+ * that only need to override a handful of specific methods. TableMap
+ * implements TableModel by routing all requests to its model, and
+ * TableModelListener by routing all events to its listeners. Inserting
+ * a TableMap which has not been subclassed into a chain of table filters
+ * should have no effect.
+ *
+ * @version 1.4 12/17/97
+ * @author Philip Milne */
+
+import javax.swing.table.*;
+import javax.swing.event.TableModelListener;
+import javax.swing.event.TableModelEvent;
+
+public class TableMap extends AbstractTableModel
+ implements TableModelListener {
+ protected TableModel model;
+
+ public TableModel getModel() {
+ return model;
+ }
+
+ public void setModel(TableModel model) {
+ this.model = model;
+ model.addTableModelListener(this);
+ }
+
+ // By default, implement TableModel by forwarding all messages
+ // to the model.
+
+ public Object getValueAt(int aRow, int aColumn) {
+ return model.getValueAt(aRow, aColumn);
+ }
+
+ public void setValueAt(Object aValue, int aRow, int aColumn) {
+ model.setValueAt(aValue, aRow, aColumn);
+ }
+
+ public int getRowCount() {
+ return (model == null) ? 0 : model.getRowCount();
+ }
+
+ public int getColumnCount() {
+ return (model == null) ? 0 : model.getColumnCount();
+ }
+
+ public String getColumnName(int aColumn) {
+ return model.getColumnName(aColumn);
+ }
+
+ public Class getColumnClass(int aColumn) {
+ return model.getColumnClass(aColumn);
+ }
+
+ public boolean isCellEditable(int row, int column) {
+ return model.isCellEditable(row, column);
+ }
+//
+// Implementation of the TableModelListener interface,
+//
+ // By default forward all events to all the listeners.
+ public void tableChanged(TableModelEvent e) {
+ fireTableChanged(e);
+ }
+}
+
diff --git a/source/client/swing/TableSorter.java b/source/client/swing/TableSorter.java
new file mode 100644
index 0000000..f8b2a40
--- /dev/null
+++ b/source/client/swing/TableSorter.java
@@ -0,0 +1,432 @@
+package itunes.client.swing;
+
+/**
+ * A sorter for TableModels. The sorter has a model (conforming to TableModel)
+ * and itself implements TableModel. TableSorter does not store or copy
+ * the data in the TableModel, instead it maintains an array of
+ * integers which it keeps the same size as the number of rows in its
+ * model. When the model changes it notifies the sorter that something
+ * has changed eg. "rowsAdded" so that its internal array of integers
+ * can be reallocated. As requests are made of the sorter (like
+ * getValueAt(row, col) it redirects them to its model via the mapping
+ * array. That way the TableSorter appears to hold another copy of the table
+ * with the rows in a different order. The sorting algorthm used is stable
+ * which means that it does not move around rows when its comparison
+ * function returns 0 to denote that they are equivalent.
+ *
+ * @version 1.5 12/17/97
+ * @author Philip Milne
+ */
+
+import java.util.*;
+
+import javax.swing.table.TableModel;
+import javax.swing.event.TableModelEvent;
+
+// Imports for picking up mouse events from the JTable.
+
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.InputEvent;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.table.JTableHeader;
+import javax.swing.table.TableColumnModel;
+
+public class TableSorter extends TableMap {
+ int indexes[];
+
+ protected int numElements;
+
+ int fullIndex[];
+ Vector sortingColumns = new Vector();
+ boolean ascending = true;
+ int compares;
+ protected int currColumn;
+ protected boolean currAscending;
+ protected String filter;
+
+ public TableSorter() {
+
+ indexes = new int[0]; // for consistency
+ currColumn = -1;
+ currAscending = true;
+ checkModel();
+ }
+
+ public TableSorter(TableModel model) {
+ setModel(model);
+ currColumn = -1;
+ currAscending = true;
+ }
+
+ public void setModel(TableModel model) {
+ super.setModel(model);
+ filter = "";
+ reallocateIndexes();
+ loadFullIndex();
+ }
+ public void reFilter(JTextField field) {
+ filter = field.getText().toLowerCase();
+ decideFiltering();
+ super.tableChanged(new TableModelEvent(this));
+ }
+ protected void decideFiltering()
+ {
+ if (filter.equals(""))
+ loadFullIndex();
+ else
+ findFilter();
+ }
+ protected boolean presentInRow(String[] tokens, int row)
+ {
+ String artist = getArtistAt(row).toLowerCase();
+ String album = getAlbumAt(row).toLowerCase();
+ String song = getSongAt(row).toLowerCase();
+ for (int i = 0; i < tokens.length; i++){
+ if (artist.indexOf(tokens[i])==-1 && album.indexOf(tokens[i])==-1 && song.indexOf(tokens[i])==-1){
+ return false;
+ }
+ }
+ return true;
+ }
+ public int getFullIndexRow(int filteredRow)
+ {
+ if(filteredRow < 0 || filteredRow >= numElements)
+ {
+ return -1;
+ }
+ return indexes[filteredRow];
+ }
+ public int getFilteredIndexRow(int fullRow)
+ {
+ for(int i = 0; i < numElements; i++)
+ {
+ if(indexes[i] == fullRow)
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+ public String getArtistAt(int row) {
+ checkModel();
+ return ((SongTableModel)model).getArtistAt(indexes[row]);
+ }
+
+ public String getAlbumAt(int row) {
+ checkModel();
+ return ((SongTableModel)model).getAlbumAt(indexes[row]);
+ }
+
+ public String getSongAt(int row) {
+ checkModel();
+ return ((SongTableModel)model).getSongNameAt(indexes[row]);
+ }
+ protected void findFilter()
+ {
+ indexes = fullIndex;
+ int temp[] = new int[indexes.length];
+ int j = 0;
+
+ String[] tokens = filter.split(" +");
+ for (int i = 0; i < indexes.length; i++){
+ if (presentInRow(tokens, i)){
+ temp[j] = indexes[i];
+ ++j;
+ }
+ }
+ indexes = temp;
+ numElements = j;
+ }
+ public void loadFullIndex()
+ {
+ indexes = fullIndex;
+ numElements = indexes.length;
+ }
+ public int compareRowsByColumn(int row1, int row2, int column) {
+ Class type = model.getColumnClass(column);
+ TableModel data = model;
+
+ // Check for nulls.
+
+ Object o1 = data.getValueAt(row1, column);
+ Object o2 = data.getValueAt(row2, column);
+
+ // If both values are null, return 0.
+ if (o1 == null && o2 == null) {
+ return 0;
+ } else if (o1 == null) { // Define null less than everything.
+ return -1;
+ } else if (o2 == null) {
+ return 1;
+ }
+
+ /*
+ * We copy all returned values from the getValue call in case
+ * an optimised model is reusing one object to return many
+ * values. The Number subclasses in the JDK are immutable and
+ * so will not be used in this way but other subclasses of
+ * Number might want to do this to save space and avoid
+ * unnecessary heap allocation.
+ */
+
+ if (type.getSuperclass() == java.lang.Number.class) {
+ Number n1 = (Number)data.getValueAt(row1, column);
+ double d1 = n1.doubleValue();
+ Number n2 = (Number)data.getValueAt(row2, column);
+ double d2 = n2.doubleValue();
+
+ if (d1 < d2) {
+ return -1;
+ } else if (d1 > d2) {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else if (type == java.util.Date.class) {
+ Date d1 = (Date)data.getValueAt(row1, column);
+ long n1 = d1.getTime();
+ Date d2 = (Date)data.getValueAt(row2, column);
+ long n2 = d2.getTime();
+
+ if (n1 < n2) {
+ return -1;
+ } else if (n1 > n2) {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else if (type == String.class) {
+ String s1 = (String)data.getValueAt(row1, column);
+ String s2 = (String)data.getValueAt(row2, column);
+ int result = s1.compareTo(s2);
+ if (column == 4) { //rating
+ if (s1.length() > s2.length()) {
+ return 1;
+ } else if (s1.length() < s2.length()) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ if (s1.matches(" *") && !s2.matches(" *")) {
+ return 1;
+ } else if (s2.matches(" *") && !s1.matches(" *")) {
+ return -1;
+ }
+ if (result < 0) {
+ return -1;
+ } else if (result > 0) {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else if (type == Boolean.class) {
+ Boolean bool1 = (Boolean)data.getValueAt(row1, column);
+ boolean b1 = bool1.booleanValue();
+ Boolean bool2 = (Boolean)data.getValueAt(row2, column);
+ boolean b2 = bool2.booleanValue();
+
+ if (b1 == b2) {
+ return 0;
+ } else if (b1) { // Define false < true
+ return 1;
+ } else {
+ return -1;
+ }
+ } else {
+ Object v1 = data.getValueAt(row1, column);
+ String s1 = v1.toString();
+ Object v2 = data.getValueAt(row2, column);
+ String s2 = v2.toString();
+ int result = s1.compareTo(s2);
+
+ if (result < 0) {
+ return -1;
+ } else if (result > 0) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+ public int compare(int row1, int row2) {
+ compares++;
+ for (int level = 0; level < sortingColumns.size(); level++) {
+ Integer column = (Integer)sortingColumns.elementAt(level);
+ int result = compareRowsByColumn(row1, row2, column.intValue());
+ if (result != 0) {
+ return ascending ? result : -result;
+ }
+ }
+ return 0;
+ }
+public int getRowCount()
+ {
+ return numElements;
+ }
+ public void reallocateIndexes() {
+
+ numElements = model.getRowCount();
+
+ // Set up a new array of indexes with the right number of elements
+ // for the new data model.
+ fullIndex = new int[numElements];
+
+ // Initialise with the identity mapping.
+ for (int row = 0; row < numElements; row++) {
+ fullIndex[row] = row;
+ }
+
+ }
+
+ public void tableChanged(TableModelEvent e) {
+ //System.out.println("Sorter: tableChanged");
+ reallocateIndexes();
+ decideFiltering();
+ super.tableChanged(e);
+ }
+
+ public void checkModel() {
+ if (indexes.length != model.getRowCount()) {
+ System.out.println("Sorter not informed of a change in model.");
+ }
+ }
+
+ public void sort(Object sender) {
+ checkModel();
+
+ compares = 0;
+ // n2sort();
+ // qsort(0, indexes.length-1);
+ shuttlesort((int[])indexes.clone(), indexes, 0, numElements);
+ //System.out.println("Compares: "+compares);
+ }
+
+ public void n2sort() {
+ for (int i = 0; i < getRowCount(); i++) {
+ for (int j = i+1; j < getRowCount(); j++) {
+ if (compare(indexes[i], indexes[j]) == -1) {
+ swap(i, j);
+ }
+ }
+ }
+ }
+
+ // This is a home-grown implementation which we have not had time
+ // to research - it may perform poorly in some circumstances. It
+ // requires twice the space of an in-place algorithm and makes
+ // NlogN assigments shuttling the values between the two
+ // arrays. The number of compares appears to vary between N-1 and
+ // NlogN depending on the initial order but the main reason for
+ // using it here is that, unlike qsort, it is stable.
+ public void shuttlesort(int from[], int to[], int low, int high) {
+ if (high - low < 2) {
+ return;
+ }
+ int middle = (low + high)/2;
+ shuttlesort(to, from, low, middle);
+ shuttlesort(to, from, middle, high);
+
+ int p = low;
+ int q = middle;
+
+ /* This is an optional short-cut; at each recursive call,
+ check to see if the elements in this subset are already
+ ordered. If so, no further comparisons are needed; the
+ sub-array can just be copied. The array must be copied rather
+ than assigned otherwise sister calls in the recursion might
+ get out of sinc. When the number of elements is three they
+ are partitioned so that the first set, [low, mid), has one
+ element and and the second, [mid, high), has two. We skip the
+ optimisation when the number of elements is three or less as
+ the first compare in the normal merge will produce the same
+ sequence of steps. This optimisation seems to be worthwhile
+ for partially ordered lists but some analysis is needed to
+ find out how the performance drops to Nlog(N) as the initial
+ order diminishes - it may drop very quickly. */
+
+ if (high - low >= 4 && compare(from[middle-1], from[middle]) <= 0) {
+ for (int i = low; i < high; i++) {
+ to[i] = from[i];
+ }
+ return;
+ }
+
+ // A normal merge.
+
+ for (int i = low; i < high; i++) {
+ if (q >= high || (p < middle && compare(from[p], from[q]) <= 0)) {
+ to[i] = from[p++];
+ }
+ else {
+ to[i] = from[q++];
+ }
+ }
+ }
+
+ public void swap(int i, int j) {
+ int tmp = indexes[i];
+ indexes[i] = indexes[j];
+ indexes[j] = tmp;
+ }
+
+ // The mapping only affects the contents of the data rows.
+ // Pass all requests to these rows through the mapping array: "indexes".
+
+ public Object getValueAt(int aRow, int aColumn) {
+ checkModel();
+ return model.getValueAt(indexes[aRow], aColumn);
+ }
+
+ public void setValueAt(Object aValue, int aRow, int aColumn) {
+ checkModel();
+ model.setValueAt(aValue, indexes[aRow], aColumn);
+ }
+
+ public void sortByColumn(int column) {
+ sortByColumn(column, true);
+ }
+
+ public void sortByColumn(int column, boolean ascending) {
+ this.ascending = ascending;
+ sortingColumns.removeAllElements();
+ sortingColumns.addElement(new Integer(column));
+ sort(this);super.tableChanged(new TableModelEvent(this, 0, numElements - 1, TableModelEvent.ALL_COLUMNS,
+ TableModelEvent.UPDATE));
+ }
+
+ // There is no-where else to put this.
+ // Add a mouse listener to the Table to trigger a table sort
+ // when a column heading is clicked in the JTable.
+ public void addMouseListenerToHeaderInTable(JTable table) {
+ final TableSorter sorter = this;
+ final JTable tableView = table;
+ tableView.setColumnSelectionAllowed(false);
+ MouseAdapter listMouseListener = new MouseAdapter() {
+ public void mouseClicked(MouseEvent e) {
+ TableColumnModel columnModel = tableView.getColumnModel();
+ int viewColumn = columnModel.getColumnIndexAtX(e.getX());
+ int column = tableView.convertColumnIndexToModel(viewColumn);
+ if (e.getClickCount() == 1 && column != -1) {
+ //System.out.println("Sorting ...");
+ // int shiftPressed = e.getModifiers()&InputEvent.SHIFT_MASK;
+ //boolean ascending = (shiftPressed == 0);
+ if (column == sorter.currColumn) {
+ sorter.currAscending = !sorter.currAscending;
+ } else {
+ sorter.currAscending = true;
+ }
+ sorter.currColumn = column;
+ sorter.sortByColumn(column, sorter.currAscending);
+ }
+ }
+ };
+ JTableHeader th = tableView.getTableHeader();
+ th.addMouseListener(listMouseListener);
+ }
+}
+
diff --git a/source/client/swing/ZApplet.java b/source/client/swing/ZApplet.java
new file mode 100644
index 0000000..0ecd92a
--- /dev/null
+++ b/source/client/swing/ZApplet.java
@@ -0,0 +1,46 @@
+package itunes.client.swing;
+import javax.swing.*;
+import java.awt.event.*;
+public class ZApplet extends JApplet {
+ private ZPlayer app;
+ public void init()
+ {
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ JButton jb = new JButton("Launch ZPlayer");
+ jb.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) { createNewPlayer(); }
+ });
+ this.getContentPane().add(jb);
+ }
+ public void createNewPlayer()
+ {
+ if(app == null)
+ {
+ app = new ZPlayer(true);
+ app.frame.addWindowListener(new WindowListener() {
+ public void windowClosed(WindowEvent e) {}
+ public void windowClosing(WindowEvent e) {
+ app = null;
+ }
+ public void windowDeiconified(WindowEvent e) {}
+ public void windowIconified(WindowEvent e) {}
+ public void windowActivated(WindowEvent e) {}
+ public void windowDeactivated(WindowEvent e) {}
+ public void windowOpened(WindowEvent e) {}
+ });
+ app.connectToHost(getParameter("host"));
+ }
+ else
+ {
+ app.frame.toFront();
+ }
+ }
+ public void stop()
+ {
+ app.logOut();
+ }
+} \ No newline at end of file
diff --git a/source/client/swing/ZPlayer.java b/source/client/swing/ZPlayer.java
new file mode 100644
index 0000000..b265641
--- /dev/null
+++ b/source/client/swing/ZPlayer.java
@@ -0,0 +1,506 @@
+/*
+ * Created on May 7, 2003
+ *
+ * To change the template for this generated file go to
+ * Window>Preferences>Java>Code Generation>Code and Comments
+ *
+Copyright 2003 Joseph Barnett
+
+This File is part of "ZPlayer"
+
+"ZPlayer" is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+Free Software Foundation; either version 2 of the License, or
+your option) any later version.
+
+"ZPlayer" 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 "ZPlayer"; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+ */
+package itunes.client.swing;
+import itunes.client.*;
+import itunes.client.request.*;
+import com.strangeberry.rendezvous.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.table.TableColumn;
+import java.awt.*;
+import java.awt.event.*;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+public class ZPlayer implements ServiceListener {
+ protected String host;
+ protected String connectedHost;
+ public JFrame frame;
+ protected JPanel serverLibrary;
+ protected JPanel pane;
+ protected JComboBox servicesCB;
+ protected JComboBox cb;
+ protected JButton browseLib;
+ protected JButton songDl;
+ protected ArrayList knownIPs;
+ protected int sessionId;
+ protected SongTableModel songModel;
+ protected JTable songTable;
+ protected JScrollPane scroller;
+ protected TableSorter sorter;
+ protected boolean hostConnected;
+ protected boolean librariesQueried;
+ protected boolean browsingLibrary;
+ protected static String iTunesService = "_daap._tcp.local.";
+ protected Rendezvous r;
+ private int fullRowPlaying;
+ protected ProgressMonitor progressMonitor;
+ protected Player p;
+ protected boolean playstop;
+ protected int playingRow;
+ protected int playdb;
+ protected String playhost;
+ protected JTextField searchQuery;
+
+ public void stopPlaying() {
+ if (p != null)
+ p.stopMusic();
+ p = null;
+ frame.setTitle("ZPlayer");
+ if (songDl != null) {
+ songDl.setText("Play Selected");
+ songDl.validate();
+ }
+ playhost = "";
+ playstop = !playstop;
+ }
+
+ public void playNext(int dir) {
+ if (p != null) {
+ p.stopMusic();
+ p = null;
+ }
+ if (playingRow >= 0 && playingRow < songTable.getRowCount()-1 && ((MusicProvider)servicesCB.getSelectedItem()).address.equals(playhost)){
+ playingRow+=dir;
+ fullRowPlaying = sorter.getFullIndexRow(playingRow);
+ SongRequest sr=null;
+ try {
+ sr = new SongRequest(host, Request.ITUNES_PORT,playdb,Integer.parseInt((String)sorter.getValueAt(playingRow,5)),(String)sorter.getValueAt(playingRow,6),sessionId);
+ } catch (NoServerPermissionException e) {
+ e.printStackTrace();
+ }
+ frame.setTitle("ZPlayer: Now Playing \""+(String)sorter.getValueAt(playingRow,2)+"\" by "+(String)sorter.getValueAt(playingRow,0));
+ p = new Player(this,sr.getStream());
+ p.start();
+ songDl.setText("Stop playing");
+ songDl.validate();
+ } else {
+ stopPlaying();
+ }
+ }
+
+ protected void playSong() {
+ final int selection = songTable.getSelectedRow();
+ final int fdb = playdb;
+ if (playstop) {
+ SongRequest sr=null;
+ try {
+ sr = new SongRequest(host, Request.ITUNES_PORT,fdb,Integer.parseInt((String)sorter.getValueAt(selection,5)),(String)sorter.getValueAt(selection,6),sessionId);
+ } catch (NoServerPermissionException e) {
+ e.printStackTrace();
+ JOptionPane.showMessageDialog(frame,"Error streaming song!");
+ stopPlaying();
+ return;
+ }
+ p = new Player(this,sr.getStream());
+ p.start();
+ System.out.println("Selected: "+selection+"\n"+(String)sorter.getValueAt(selection,5)+"\n"+(String)sorter.getValueAt(selection,2));
+ playhost = ((MusicProvider)servicesCB.getSelectedItem()).address;
+ frame.setTitle("ZPlayer: Now Playing \""+(String)sorter.getValueAt(selection,2)+"\" by "+(String)sorter.getValueAt(selection,0));
+ songDl.setText("Stop playing");
+ songDl.validate();
+ playingRow = selection;
+ fullRowPlaying = sorter.getFullIndexRow(playingRow);
+ playstop = !playstop;
+ } else {
+ stopPlaying();
+ playingRow = -1;
+ fullRowPlaying = sorter.getFullIndexRow(playingRow);
+ }
+ }
+
+ public void resolveService(Rendezvous r, String type, String name, ServiceInfo info) {
+ if (info == null) {
+ System.out.println("Service not found");
+ } else {
+ if (name.endsWith("." + type)) {
+ name = name.substring(0, name.length() - (type.length() + 1));
+ }
+ if (!knownIPs.contains(info.getAddress())) {
+ ServerInfoRequest rq=null;
+ try {
+ rq = new ServerInfoRequest(info.getAddress(), Request.ITUNES_PORT);
+ } catch (NoServerPermissionException e) {
+ return;
+ }
+ // if (rq.getServerVersion() >=2) {
+ // System.out.println("Ignoring "+name+ " at address "+ info.getAddress() + ": Unsupported iTunes Version 4.0.1 ("+rq.getServerVersion()+")");
+ // return;
+ // }
+ System.out.println("Discovered "+name+" at address "+info.getAddress() + ": (Server Version "+rq.getServerVersion()+")");
+ knownIPs.add(info.getAddress());
+ MusicProvider m = new MusicProvider(info.getAddress(),name);
+ servicesCB.addItem(m);
+ servicesCB.validate();
+ } else {
+ System.out.println("Ignoring new service " + name + " from already known IP "+info.getAddress());
+ }
+ }
+ }
+
+ public void addService(Rendezvous r, String type, String name) {
+ if (name.endsWith("." + type)) {
+ name = name.substring(0, name.length() - (type.length() + 1));
+ }
+ r.requestServiceInfo(iTunesService,name);
+ }
+
+ public void removeService(Rendezvous r, String type, String name) {
+ if (name.endsWith("." + type)) {
+ name = name.substring(0, name.length() - (type.length() + 1));
+ }
+ knownIPs.remove(r.getServiceInfo(type,name).getAddress());
+ System.out.println(name + " went offline");
+ servicesCB.removeItem(new MusicProvider("",name));
+ servicesCB.validate();
+ }
+ private class MyDocumentListner implements DocumentListener {
+ public void insertUpdate(DocumentEvent e){
+ changeFilter();
+ }
+ public void removeUpdate(DocumentEvent e) {
+ changeFilter();
+ }
+ public void changedUpdate(DocumentEvent e) {
+ }
+ }
+ private void changeFilter()
+ {
+ sorter.reFilter(searchQuery);
+ playingRow = sorter.getFilteredIndexRow(fullRowPlaying);
+ }
+ private JPanel buildSearch()
+ {
+ JPanel row1 = new JPanel();
+
+ searchQuery = new JTextField(20);
+ searchQuery.getDocument().addDocumentListener(new MyDocumentListner());
+ JButton clearSearch = new JButton("Clear Search");
+
+ clearSearch.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e){
+ searchQuery.setText("");
+ }
+ });
+
+ JLabel searchLabel = new JLabel("Search:");
+
+ row1.add(searchLabel);
+ row1.add(searchQuery);
+ row1.add(clearSearch);
+ row1.add(Box.createRigidArea(new Dimension(5,0)));
+ JButton select = new JButton("Select Playing");
+ select.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if(playingRow == -1 && fullRowPlaying != -1)
+ {
+ searchQuery.setText("");
+ }
+ if(playingRow>-1)
+ {
+ songTable.setRowSelectionInterval(playingRow,playingRow);
+ songTable.scrollRectToVisible(songTable.getCellRect(playingRow, 0, true));
+ }
+ }
+ });
+ row1.add(select);
+ return row1;
+ }
+ protected void createSongChooser() throws NoServerPermissionException {
+ SingleDatabaseRequest sr = new SingleDatabaseRequest(host,Request.ITUNES_PORT,sessionId, playdb);
+ ArrayList songs = sr.getSongs();
+ Collections.sort(songs);
+ if (songModel == null) {
+ songModel = new SongTableModel();
+ } else {
+ songModel.clear();
+ }
+ for (int i = 0; i < songs.size();i++) {
+ Song s = (Song)songs.get(i);
+ songModel.AddRow(s);
+ }
+ if (songTable == null) {
+ pane.remove(pane.getComponentCount()-1);
+ sorter = new TableSorter(songModel);
+ songTable = new JTable(sorter);
+ songTable.addMouseListener(new MouseAdapter() {
+ public void mouseClicked(MouseEvent e) {
+ if (e.getClickCount() == 2) {
+ stopPlaying();
+ playstop = true;
+ playSong();
+ }
+ }
+ });
+ sorter.addMouseListenerToHeaderInTable(songTable);
+ songTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ songTable.setPreferredScrollableViewportSize(new
+ Dimension(1500, 1600));
+ for (int i = 0; i < 5; i++) {
+ TableColumn column = songTable.getColumnModel().getColumn(i);
+ if (i == 2) {
+ column.setPreferredWidth(300);
+ } else if (i==0||i==1){
+ column.setPreferredWidth(150);
+ } else {
+ column.setPreferredWidth(10);
+ }
+ }
+ scroller = new JScrollPane(songTable);
+ pane.add(buildSearch());
+ pane.add(scroller);
+
+ } else {
+ songTable.validate();
+ songTable.repaint();
+ }
+ if (songDl == null) {
+ JPanel buttonRow = new JPanel();
+ buttonRow.setAlignmentX(Component.CENTER_ALIGNMENT);
+ JButton back = new JButton("Back Track");
+ back.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ playNext(-1);
+ }
+ });
+ buttonRow.add(back);
+ buttonRow.add(Box.createRigidArea(new Dimension(5,0)));
+ songDl = new JButton("Play Selected");
+ back.setAlignmentX(Component.CENTER_ALIGNMENT);
+ songDl.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ playSong();
+ }
+ });
+ buttonRow.add(songDl);
+ buttonRow.add(Box.createRigidArea(new Dimension(5,0)));
+ JButton next = new JButton("Forward Track");
+ next.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ playNext(1);
+ }
+ });
+ buttonRow.add(next);
+ pane.add(buttonRow);
+ }
+ songModel.fireTableDataChanged();
+ pane.validate();
+ scroller.validate();
+ frame.validate();
+ }
+
+ protected void connectToHost() throws NoServerPermissionException{
+ if (sessionId != -1) {
+ try {
+ LogoutRequest lr = new LogoutRequest(connectedHost,Request.ITUNES_PORT, sessionId);
+ } catch (NoServerPermissionException e) {}
+ sessionId = -1;
+ }
+ LoginRequest l=null;
+ l = new LoginRequest(host,Request.ITUNES_PORT);
+ connectedHost = host;
+ sessionId = l.getSessionId();
+ if (sessionId == -1) {
+ JOptionPane.showMessageDialog(frame, "Error connecting to "+host);
+ return;
+ }
+ System.out.println("logged in: session "+ sessionId);
+ JPanel col1 = new JPanel();
+ col1.setLayout(new BoxLayout(col1,BoxLayout.Y_AXIS));
+ DatabasesRequest db = new DatabasesRequest(host, Request.ITUNES_PORT, sessionId);
+ int dbCount = db.getLibraryCount();
+ ArrayList dbs = db.getDbs();
+ if (cb == null) {
+ cb = new JComboBox();
+ cb.setAlignmentX(Component.CENTER_ALIGNMENT);
+ col1.add(cb);
+ } else {
+ cb.removeAllItems();
+ }
+ for (int i = 0; i < dbCount; i++) {
+ cb.addItem(dbs.get(i));
+ }
+ if (browseLib == null) {
+ browseLib = new JButton("Browse Selected Library");
+ browseLib.setAlignmentX(Component.CENTER_ALIGNMENT);
+ browseLib.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ try {
+ playdb = ((Database)cb.getSelectedItem()).id;
+ createSongChooser();
+ } catch (NoServerPermissionException ex) {
+ }
+ }
+ });
+ col1.add(browseLib);
+ }
+ if(col1.getComponentCount() > 0)
+ {
+ serverLibrary.add(Box.createRigidArea(new Dimension(5,0)));
+ serverLibrary.add(col1);
+ serverLibrary.validate();
+ }
+ }
+
+ public Component createComponents() {
+ JButton button = new JButton("Browse Host");
+ button.setAlignmentX(Component.CENTER_ALIGNMENT);
+ button.setMnemonic(KeyEvent.VK_I);
+ button.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ try {
+ MusicProvider m = ((MusicProvider)servicesCB.getSelectedItem());
+ if (m.address.equals("")) {
+ host = JOptionPane.showInputDialog("Enter hostname: ","");
+ } else {
+ host = m.address;
+ }
+
+ try {
+ connectToHost();
+ } catch (NoServerPermissionException e1) {
+ JOptionPane.showMessageDialog(frame,"Need Authorization! (not yet implemented)");
+ return;
+ }
+ playdb = ((Database)cb.getItemAt(0)).id;
+ createSongChooser();
+ } catch (NoServerPermissionException ex) {
+ }
+ }
+ });
+ serverLibrary = new JPanel();
+ serverLibrary.setLayout(new BoxLayout(serverLibrary,BoxLayout.X_AXIS));
+ pane = new JPanel();
+ pane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+ pane.setLayout(new BoxLayout(pane,BoxLayout.Y_AXIS));
+ MusicProvider m = new MusicProvider("","Other...");
+ servicesCB.addItem(m);
+ JPanel col1 = new JPanel();
+ col1.setLayout(new BoxLayout(col1,BoxLayout.Y_AXIS));
+ col1.add(servicesCB);
+ col1.add(button);
+ serverLibrary.add(col1);
+ pane.add(serverLibrary);
+ pane.add(Box.createRigidArea(new Dimension(0,20)));
+ pane.add(Box.createRigidArea(new Dimension(0,400)));
+ return pane;
+ }
+ public void logOut()
+ {
+ if (sessionId != -1) {
+ LogoutRequest lr;
+ try {
+ lr = new LogoutRequest(connectedHost, Request.ITUNES_PORT, sessionId);
+ System.out.println("disconnected from session: " + sessionId);
+ } catch (NoServerPermissionException ex) { }
+ sessionId = -1;
+ }
+ stopPlaying();
+ }
+ public void connectToHost(String initialHost)
+ {
+ if(initialHost != null && !initialHost.equals(""))
+ try
+ {
+ host = initialHost;
+ connectToHost();
+ playdb = ((Database)cb.getItemAt(0)).id;
+ createSongChooser();
+ }
+ catch (NoServerPermissionException ex) {}
+ }
+ public ZPlayer(boolean applet) {
+ playhost = "";
+ playingRow = -1;
+ fullRowPlaying = -1;
+ playstop = true;
+ p = null;
+ servicesCB = null;
+ cb = null;
+ browseLib = null;
+ songDl = null;
+ sessionId = -1;
+ songModel = null;
+ sorter = null;
+ songTable = null;
+ scroller = null;
+ hostConnected = false;
+ librariesQueried = false;
+ browsingLibrary = false;
+ knownIPs = new ArrayList();
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ servicesCB = new JComboBox();
+ servicesCB.setAlignmentX(Component.CENTER_ALIGNMENT);
+
+ try {
+ r = new Rendezvous();
+ r.addServiceListener(iTunesService,this);
+ } catch (Exception e) {
+ System.out.println("Died while establishing rendezvous:\n");
+ e.printStackTrace();
+ }
+ //Create the top-level container and add contents to it.
+ frame = new JFrame("ZPlayer");
+ Component contents = createComponents();
+ frame.getContentPane().add(contents, BorderLayout.CENTER);
+
+ if(!applet)
+ {
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ }
+ frame.pack();
+
+ frame.setLocation(100,100);
+ frame.setSize(800,400);
+ frame.addWindowListener(new WindowListener() {
+ public void windowClosed(WindowEvent e) {
+ logOut();
+ }
+ public void windowClosing(WindowEvent e) {
+ logOut();
+ }
+ public void windowDeiconified(WindowEvent e) {}
+ public void windowIconified(WindowEvent e) {}
+ public void windowActivated(WindowEvent e) {}
+ public void windowDeactivated(WindowEvent e) {}
+ public void windowOpened(WindowEvent e) {}
+ });
+ frame.setVisible(true);
+ }
+ public static void main(String[] args) {
+ ZPlayer app = new ZPlayer(false);
+ if(args.length > 0)
+ {
+ app.connectToHost(args[0]);
+ }
+ }
+}