diff options
Diffstat (limited to 'source/client/swing')
| -rw-r--r-- | source/client/swing/Player.java | 59 | ||||
| -rw-r--r-- | source/client/swing/SongTableModel.java | 103 | ||||
| -rw-r--r-- | source/client/swing/SwingWorker.java | 133 | ||||
| -rw-r--r-- | source/client/swing/TableMap.java | 70 | ||||
| -rw-r--r-- | source/client/swing/TableSorter.java | 432 | ||||
| -rw-r--r-- | source/client/swing/ZApplet.java | 46 | ||||
| -rw-r--r-- | source/client/swing/ZPlayer.java | 506 |
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]); + } + } +} |
