Revamped preferences handling; added support for voting for songs you
like. Eventually we'll actually make use of the voting information. git-svn-id: https://samskivert.googlecode.com/svn/trunk@1116 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -1,10 +1,9 @@
|
|||||||
//
|
//
|
||||||
// $Id: BrowsePanel.java,v 1.3 2002/11/11 17:04:12 mdb Exp $
|
// $Id: BrowsePanel.java,v 1.4 2003/05/04 18:16:06 mdb Exp $
|
||||||
|
|
||||||
package robodj.chooser;
|
package robodj.chooser;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
import robodj.repository.*;
|
import robodj.repository.*;
|
||||||
|
|
||||||
public class BrowsePanel extends JTabbedPane
|
public class BrowsePanel extends JTabbedPane
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
//
|
//
|
||||||
// $Id: CategoryEntryList.java,v 1.1 2002/02/22 07:06:33 mdb Exp $
|
// $Id: CategoryEntryList.java,v 1.2 2003/05/04 18:16:06 mdb Exp $
|
||||||
|
|
||||||
package robodj.chooser;
|
package robodj.chooser;
|
||||||
|
|
||||||
import com.samskivert.io.PersistenceException;
|
import com.samskivert.io.PersistenceException;
|
||||||
|
import com.samskivert.swing.Controller;
|
||||||
|
|
||||||
import robodj.repository.Entry;
|
import robodj.repository.Entry;
|
||||||
|
|
||||||
@@ -17,14 +18,15 @@ public class CategoryEntryList extends EntryList
|
|||||||
_categoryId = categoryId;
|
_categoryId = categoryId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// documentation inherited
|
||||||
* Reads in the entries for this category.
|
protected Controller createController ()
|
||||||
*/
|
|
||||||
public Entry[] readEntries ()
|
|
||||||
throws PersistenceException
|
|
||||||
{
|
{
|
||||||
|
return new EntryController(this) {
|
||||||
|
public Entry[] readEntries () throws PersistenceException {
|
||||||
return Chooser.model.getEntries(_categoryId);
|
return Chooser.model.getEntries(_categoryId);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
protected String getEmptyString ()
|
protected String getEmptyString ()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: Chooser.java,v 1.10 2002/11/11 17:04:26 mdb Exp $
|
// $Id: Chooser.java,v 1.11 2003/05/04 18:16:06 mdb Exp $
|
||||||
|
|
||||||
package robodj.chooser;
|
package robodj.chooser;
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ import javax.swing.JOptionPane;
|
|||||||
|
|
||||||
import com.samskivert.io.PersistenceException;
|
import com.samskivert.io.PersistenceException;
|
||||||
import com.samskivert.jdbc.StaticConnectionProvider;
|
import com.samskivert.jdbc.StaticConnectionProvider;
|
||||||
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
import com.samskivert.swing.util.SwingUtil;
|
import com.samskivert.swing.util.SwingUtil;
|
||||||
import com.samskivert.util.ConfigUtil;
|
import com.samskivert.util.ConfigUtil;
|
||||||
@@ -20,6 +21,8 @@ import com.samskivert.util.PropertiesUtil;
|
|||||||
import robodj.Log;
|
import robodj.Log;
|
||||||
import robodj.repository.Model;
|
import robodj.repository.Model;
|
||||||
import robodj.repository.Repository;
|
import robodj.repository.Repository;
|
||||||
|
import robodj.util.RDJPrefs;
|
||||||
|
import robodj.util.RDJPrefsPanel;
|
||||||
import robodj.util.ServerControl;
|
import robodj.util.ServerControl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,82 +31,86 @@ import robodj.util.ServerControl;
|
|||||||
*/
|
*/
|
||||||
public class Chooser
|
public class Chooser
|
||||||
{
|
{
|
||||||
public static Properties config;
|
|
||||||
|
|
||||||
public static Repository repository;
|
public static Repository repository;
|
||||||
|
|
||||||
public static Model model;
|
public static Model model;
|
||||||
|
|
||||||
public static ServerControl scontrol;
|
public static ServerControl scontrol;
|
||||||
|
|
||||||
|
public static ChooserFrame frame;
|
||||||
|
|
||||||
public static void main (String[] args)
|
public static void main (String[] args)
|
||||||
{
|
{
|
||||||
// load our main configuration
|
boolean error = false;
|
||||||
String cpath = "conf/chooser.properties";
|
|
||||||
try {
|
// loop until the user provides us with a configuration that works
|
||||||
config = ConfigUtil.loadProperties(cpath);
|
// or requests to exit
|
||||||
} catch (IOException ioe) {
|
for (int ii = 0; ii < 100; ii++) {
|
||||||
String err = "Unable to load configuration " +
|
String repodir = RDJPrefs.getRepositoryDirectory();
|
||||||
"[path=" + cpath + "]:\n" + ioe.getMessage();
|
if (StringUtil.blank(repodir) || ii > 0) {
|
||||||
reportError(err);
|
// display the initial configuration wizard if we are not
|
||||||
System.exit(-1);
|
// yet properly configured
|
||||||
|
RDJPrefsPanel.display(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create an interface to the database repository
|
// create an interface to the database repository
|
||||||
try {
|
try {
|
||||||
StaticConnectionProvider scp =
|
StaticConnectionProvider scp =
|
||||||
new StaticConnectionProvider("conf/repository.properties");
|
new StaticConnectionProvider(RDJPrefs.getJDBCConfig());
|
||||||
repository = new Repository(scp);
|
repository = new Repository(scp);
|
||||||
model = new Model(repository);
|
model = new Model(repository);
|
||||||
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
Log.logStackTrace(ioe);
|
|
||||||
reportError("Error loading repository config:\n" +
|
|
||||||
ioe.getMessage());
|
|
||||||
System.exit(-1);
|
|
||||||
|
|
||||||
} catch (PersistenceException pe) {
|
} catch (PersistenceException pe) {
|
||||||
Log.logStackTrace(pe);
|
if (reportError("Unable to establish communication " +
|
||||||
pe.printStackTrace();
|
"with database:", pe)) {
|
||||||
reportError("Unable to establish communication " +
|
|
||||||
"with database:\n" + pe.getMessage());
|
|
||||||
System.exit(-1);
|
System.exit(-1);
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
String mhost = System.getProperty("musicd_host", "localhost");
|
|
||||||
String mportstr = System.getProperty("musicd_port", "2500");
|
|
||||||
int mport = 2500;
|
|
||||||
try {
|
|
||||||
mport = Integer.parseInt(mportstr);
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
Log.warning("Invalid musicd_port value. Using default " +
|
|
||||||
"(" + mport + ").");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
String host = RDJPrefs.getMusicDaemonHost();
|
||||||
|
if (StringUtil.blank(host)) {
|
||||||
|
throw new IOException(
|
||||||
|
"No music server host specified in configuration.");
|
||||||
|
}
|
||||||
// establish a connection with the music server
|
// establish a connection with the music server
|
||||||
scontrol = new ServerControl(mhost, 2500);
|
scontrol = new ServerControl(
|
||||||
|
host, RDJPrefs.getMusicDaemonPort());
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
Log.logStackTrace(ioe);
|
if (reportError("Unable to establish communication with " +
|
||||||
reportError("Unable to establish communication with music " +
|
"music server on host '" +
|
||||||
"server:\n" + ioe.getMessage());
|
RDJPrefs.getMusicDaemonHost() + "' ", ioe)) {
|
||||||
System.exit(-1);
|
System.exit(-1);
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// create our primary user interface frame, center the frame in
|
// create our primary user interface frame, center the frame in
|
||||||
// the screen and show it
|
// the screen and show it
|
||||||
ChooserFrame frame = new ChooserFrame();
|
frame = new ChooserFrame();
|
||||||
frame.setSize(650, 665);
|
frame.setSize(650, 665);
|
||||||
SwingUtil.centerWindow(frame);
|
SwingUtil.centerWindow(frame);
|
||||||
frame.setVisible(true);
|
frame.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void reportError (String error)
|
protected static boolean reportError (String error, Exception e)
|
||||||
{
|
{
|
||||||
Object[] options = { "OK" };
|
String text = error;
|
||||||
JOptionPane.showOptionDialog(null, error, "Error",
|
if (e != null) {
|
||||||
JOptionPane.DEFAULT_OPTION,
|
text = text + "\n\n" + e.getMessage();
|
||||||
JOptionPane.ERROR_MESSAGE,
|
if (e.getCause() != null) {
|
||||||
null, options, options[0]);
|
text = text + "\n" + e.getCause().getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Object[] options = { "Edit configuration", "Exit" };
|
||||||
|
int choice = JOptionPane.showOptionDialog(
|
||||||
|
null, text, "Error", JOptionPane.DEFAULT_OPTION,
|
||||||
|
JOptionPane.ERROR_MESSAGE, null, options, options[0]);
|
||||||
|
System.out.println("Chose " + choice + ".");
|
||||||
|
return (choice == 1 || choice == JOptionPane.CLOSED_OPTION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
//
|
||||||
|
// $Id: ChooserController.java,v 1.1 2003/05/04 18:16:06 mdb Exp $
|
||||||
|
|
||||||
|
package robodj.chooser;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
|
||||||
|
import com.samskivert.swing.Controller;
|
||||||
|
import com.samskivert.swing.util.TaskMaster;
|
||||||
|
import com.samskivert.swing.util.TaskObserver;
|
||||||
|
|
||||||
|
import robodj.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles top-level chooser UI commands.
|
||||||
|
*/
|
||||||
|
public class ChooserController extends Controller
|
||||||
|
implements TaskObserver
|
||||||
|
{
|
||||||
|
public ChooserController ()
|
||||||
|
{
|
||||||
|
// read our playing state
|
||||||
|
TaskMaster.invokeMethodTask("refreshPlaying", Chooser.scontrol, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited
|
||||||
|
public boolean handleAction (ActionEvent event)
|
||||||
|
{
|
||||||
|
String cmd = event.getActionCommand();
|
||||||
|
if (cmd.equals("exit")) {
|
||||||
|
System.exit(0);
|
||||||
|
|
||||||
|
} else if (cmd.equals("skip")) {
|
||||||
|
TaskMaster.invokeMethodTask("skip", Chooser.scontrol, this);
|
||||||
|
|
||||||
|
} else if (cmd.equals("back")) {
|
||||||
|
TaskMaster.invokeMethodTask("back", Chooser.scontrol, this);
|
||||||
|
|
||||||
|
} else if (cmd.equals("pause")) {
|
||||||
|
TaskMaster.invokeMethodTask("pause", Chooser.scontrol, this);
|
||||||
|
|
||||||
|
} else if (cmd.equals("play")) {
|
||||||
|
TaskMaster.invokeMethodTask("play", Chooser.scontrol, this);
|
||||||
|
|
||||||
|
} else if (cmd.equals("stop")) {
|
||||||
|
TaskMaster.invokeMethodTask("stop", Chooser.scontrol, this);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
System.out.println("Unknown action event: " + cmd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public void taskCompleted (String name, Object result)
|
||||||
|
{
|
||||||
|
// nothing to do here
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public void taskFailed (String name, Throwable exception)
|
||||||
|
{
|
||||||
|
String msg;
|
||||||
|
if (Exception.class.equals(exception.getClass())) {
|
||||||
|
msg = exception.getMessage();
|
||||||
|
} else {
|
||||||
|
msg = exception.toString();
|
||||||
|
}
|
||||||
|
JOptionPane.showMessageDialog(Chooser.frame, msg, "Error",
|
||||||
|
JOptionPane.ERROR_MESSAGE);
|
||||||
|
Log.logStackTrace(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: ChooserFrame.java,v 1.10 2002/11/11 17:04:41 mdb Exp $
|
// $Id: ChooserFrame.java,v 1.11 2003/05/04 18:16:06 mdb Exp $
|
||||||
|
|
||||||
package robodj.chooser;
|
package robodj.chooser;
|
||||||
|
|
||||||
@@ -10,18 +10,18 @@ import java.awt.event.ActionListener;
|
|||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
import com.samskivert.swing.*;
|
import com.samskivert.swing.*;
|
||||||
import com.samskivert.swing.util.TaskAdapter;
|
import com.samskivert.swing.util.*;
|
||||||
import com.samskivert.swing.util.TaskObserver;
|
import com.samskivert.util.StringUtil;
|
||||||
import com.samskivert.swing.util.TaskMaster;
|
|
||||||
|
|
||||||
import robodj.Log;
|
import robodj.Log;
|
||||||
import robodj.Version;
|
import robodj.Version;
|
||||||
import robodj.repository.*;
|
import robodj.repository.*;
|
||||||
import robodj.util.ButtonUtil;
|
import robodj.util.ButtonUtil;
|
||||||
|
import robodj.util.RDJPrefs;
|
||||||
import robodj.util.ServerControl.PlayingListener;
|
import robodj.util.ServerControl.PlayingListener;
|
||||||
|
|
||||||
public class ChooserFrame extends JFrame
|
public class ChooserFrame extends JFrame
|
||||||
implements ActionListener, TaskObserver, PlayingListener
|
implements PlayingListener, ControllerProvider
|
||||||
{
|
{
|
||||||
public ChooserFrame ()
|
public ChooserFrame ()
|
||||||
{
|
{
|
||||||
@@ -59,22 +59,42 @@ public class ChooserFrame extends JFrame
|
|||||||
bgl.setJustification(GroupLayout.RIGHT);
|
bgl.setJustification(GroupLayout.RIGHT);
|
||||||
JPanel cbar = new JPanel(bgl);
|
JPanel cbar = new JPanel(bgl);
|
||||||
|
|
||||||
|
// create a label and text field where the chooser user can
|
||||||
|
// identify themselves
|
||||||
|
cbar.add(new JLabel("Initials:"));
|
||||||
|
cbar.add(_userField = new JTextField());
|
||||||
|
Dimension ups = _userField.getPreferredSize();
|
||||||
|
ups.width = 35;
|
||||||
|
_userField.setPreferredSize(ups);
|
||||||
|
cbar.add(new Spacer(100, 10));
|
||||||
|
|
||||||
|
// enforce a maximum of 3 letters in initials
|
||||||
|
SwingUtil.setDocumentHelpers(
|
||||||
|
_userField, new SwingUtil.DocumentValidator() {
|
||||||
|
public boolean isValid (String text) {
|
||||||
|
return (text.length() <= 3);
|
||||||
|
}
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
// display any previously configured initials
|
||||||
|
_userField.setText(RDJPrefs.getUser());
|
||||||
|
|
||||||
// add some fake control buttons for now
|
// add some fake control buttons for now
|
||||||
cbar.add(ButtonUtil.createControlButton(
|
cbar.add(ButtonUtil.createControlButton(
|
||||||
BACK_TIP, "back", BACK_ICON_PATH, this, true));
|
BACK_TIP, "back", BACK_ICON_PATH, true));
|
||||||
_stop = ButtonUtil.createControlButton(
|
_stop = ButtonUtil.createControlButton(
|
||||||
STOP_TIP, "stop", STOP_ICON_PATH, this, true);
|
STOP_TIP, "stop", STOP_ICON_PATH, true);
|
||||||
cbar.add(_stop);
|
cbar.add(_stop);
|
||||||
_pauseIcon = ButtonUtil.getIcon(PAUSE_ICON_PATH);
|
_pauseIcon = ButtonUtil.getIcon(PAUSE_ICON_PATH);
|
||||||
_playIcon = ButtonUtil.getIcon(PLAY_ICON_PATH);
|
_playIcon = ButtonUtil.getIcon(PLAY_ICON_PATH);
|
||||||
_pause = ButtonUtil.createControlButton(
|
_pause = ButtonUtil.createControlButton(
|
||||||
"", "pause", _pauseIcon, this, true);
|
"", "pause", _pauseIcon, true);
|
||||||
cbar.add(_pause);
|
cbar.add(_pause);
|
||||||
cbar.add(ButtonUtil.createControlButton(
|
cbar.add(ButtonUtil.createControlButton(
|
||||||
SKIP_TIP, "skip", SKIP_ICON_PATH, this, true));
|
SKIP_TIP, "skip", SKIP_ICON_PATH, true));
|
||||||
cbar.add(new Spacer(50, 10));
|
cbar.add(new Spacer(50, 10));
|
||||||
cbar.add(ButtonUtil.createControlButton(
|
cbar.add(ButtonUtil.createControlButton(
|
||||||
EXIT_TIP, "exit", EXIT_ICON_PATH, this, true));
|
EXIT_TIP, "exit", EXIT_ICON_PATH, true));
|
||||||
|
|
||||||
// stick it into the frame
|
// stick it into the frame
|
||||||
top.add(cbar, GroupLayout.FIXED);
|
top.add(cbar, GroupLayout.FIXED);
|
||||||
@@ -86,52 +106,44 @@ public class ChooserFrame extends JFrame
|
|||||||
// add ourselves as a playing listener
|
// add ourselves as a playing listener
|
||||||
Chooser.scontrol.addPlayingListener(this);
|
Chooser.scontrol.addPlayingListener(this);
|
||||||
|
|
||||||
// read our playing state
|
// create our controller
|
||||||
TaskMaster.invokeMethodTask("refreshPlaying", Chooser.scontrol, this);
|
_controller = new ChooserController();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void actionPerformed (ActionEvent e)
|
// documentation inherited from interface
|
||||||
|
public Controller getController ()
|
||||||
{
|
{
|
||||||
String cmd = e.getActionCommand();
|
return _controller;
|
||||||
if (cmd.equals("exit")) {
|
|
||||||
System.exit(0);
|
|
||||||
|
|
||||||
} else if (cmd.equals("skip")) {
|
|
||||||
TaskMaster.invokeMethodTask("skip", Chooser.scontrol, this);
|
|
||||||
|
|
||||||
} else if (cmd.equals("back")) {
|
|
||||||
TaskMaster.invokeMethodTask("back", Chooser.scontrol, this);
|
|
||||||
|
|
||||||
} else if (cmd.equals("pause")) {
|
|
||||||
TaskMaster.invokeMethodTask("pause", Chooser.scontrol, this);
|
|
||||||
|
|
||||||
} else if (cmd.equals("play")) {
|
|
||||||
TaskMaster.invokeMethodTask("play", Chooser.scontrol, this);
|
|
||||||
|
|
||||||
} else if (cmd.equals("stop")) {
|
|
||||||
TaskMaster.invokeMethodTask("stop", Chooser.scontrol, this);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
System.out.println("Unknown action event: " + cmd);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void taskCompleted (String name, Object result)
|
/**
|
||||||
|
* Returns the string currently entered into the "user" field.
|
||||||
|
*/
|
||||||
|
public String getUser (boolean showError)
|
||||||
{
|
{
|
||||||
// nothing to do here
|
String user = _userField.getText();
|
||||||
|
// if they haven't supplied a user, complain
|
||||||
|
if (StringUtil.blank(user)) {
|
||||||
|
if (!showError) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
String errmsg = "The feature you have requested requires " +
|
||||||
public void taskFailed (String name, Throwable exception)
|
"that you identify yourself by entering your initials in " +
|
||||||
{
|
"the 'Initials' box at the bottom of the window.";
|
||||||
String msg;
|
JOptionPane.showMessageDialog(this, errmsg, "Initials required",
|
||||||
if (Exception.class.equals(exception.getClass())) {
|
|
||||||
msg = exception.getMessage();
|
|
||||||
} else {
|
|
||||||
msg = exception.toString();
|
|
||||||
}
|
|
||||||
JOptionPane.showMessageDialog(this, msg, "Error",
|
|
||||||
JOptionPane.ERROR_MESSAGE);
|
JOptionPane.ERROR_MESSAGE);
|
||||||
Log.logStackTrace(exception);
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if they typed in mixed or upper case, lower it for them
|
||||||
|
user = user.toLowerCase();
|
||||||
|
if (!user.equals(_userField.getText())) {
|
||||||
|
_userField.setText(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we've saved these initials values
|
||||||
|
RDJPrefs.config.setValue(RDJPrefs.USER_KEY, user);
|
||||||
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playingUpdated (int songid, boolean paused)
|
public void playingUpdated (int songid, boolean paused)
|
||||||
@@ -150,6 +162,12 @@ public class ChooserFrame extends JFrame
|
|||||||
_stop.setEnabled(songid != -1);
|
_stop.setEnabled(songid != -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Our top-level controller. */
|
||||||
|
protected Controller _controller;
|
||||||
|
|
||||||
|
/** A field where the user can identify themselves. */
|
||||||
|
protected JTextField _userField;
|
||||||
|
|
||||||
/** A reference to the play/pause button. */
|
/** A reference to the play/pause button. */
|
||||||
protected JButton _pause;
|
protected JButton _pause;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,172 @@
|
|||||||
|
//
|
||||||
|
// $Id: EntryController.java,v 1.1 2003/05/04 18:16:06 mdb Exp $
|
||||||
|
|
||||||
|
package robodj.chooser;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JComboBox;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
|
||||||
|
import javax.swing.event.AncestorEvent;
|
||||||
|
import javax.swing.event.AncestorListener;
|
||||||
|
|
||||||
|
import com.samskivert.io.PersistenceException;
|
||||||
|
import com.samskivert.swing.util.SwingUtil;
|
||||||
|
import com.samskivert.swing.util.TaskMaster;
|
||||||
|
|
||||||
|
import robodj.Log;
|
||||||
|
import robodj.repository.Entry;
|
||||||
|
import robodj.repository.Song;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles default entry list behavior.
|
||||||
|
*/
|
||||||
|
public abstract class EntryController extends ItemController
|
||||||
|
implements AncestorListener
|
||||||
|
{
|
||||||
|
public EntryController (EntryList list)
|
||||||
|
{
|
||||||
|
super(list);
|
||||||
|
_list = list;
|
||||||
|
_list.addAncestorListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the entries from the database.
|
||||||
|
*/
|
||||||
|
public void readSongs ()
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
Chooser.repository.populateSongs(_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the entries from the database and plays them all.
|
||||||
|
*/
|
||||||
|
public void readAndPlay ()
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
Chooser.repository.populateSongs(_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the category for the displayed entry.
|
||||||
|
*/
|
||||||
|
public void recategorizeEntry ()
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
Chooser.model.recategorize(_entry, _newcatid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public void taskCompleted (String name, Object result)
|
||||||
|
{
|
||||||
|
if (name.equals("readEntries")) {
|
||||||
|
// the result should be an array of entry objects with which
|
||||||
|
// we can populate our button list
|
||||||
|
_entries = (Entry[])result;
|
||||||
|
_list.populateEntries(_entries);
|
||||||
|
|
||||||
|
} else if (name.equals("readAndPlay")) {
|
||||||
|
for (int i = 0; i < _entry.songs.length; i++) {
|
||||||
|
Chooser.scontrol.append(_entry.songs[i].entryid,
|
||||||
|
_entry.songs[i].songid,
|
||||||
|
_entry.songs[i].location);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (name.equals("readSongs")) {
|
||||||
|
_list.populateSong(_entry);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
super.taskCompleted(name, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the entries from the database. This will be called on a
|
||||||
|
* separate thread and should be prepared to be called repeatedly
|
||||||
|
* without undue overhead. When the user browses into a song and back
|
||||||
|
* up again, this method is called to repopulate the entries.
|
||||||
|
*/
|
||||||
|
public abstract Entry[] readEntries ()
|
||||||
|
throws PersistenceException;
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public boolean handleAction (ActionEvent e)
|
||||||
|
{
|
||||||
|
String cmd = e.getActionCommand();
|
||||||
|
if (cmd.equals(EntryItem.BROWSE)) {
|
||||||
|
_entry = EntryItem.getEntry(e.getSource());
|
||||||
|
// start up the task that reads this CDs songs from the database
|
||||||
|
TaskMaster.invokeMethodTask("readSongs", this, this);
|
||||||
|
|
||||||
|
} else if (cmd.equals("playall")) {
|
||||||
|
_entry = EntryItem.getEntry(e.getSource());
|
||||||
|
// start up the task that reads this CDs songs from the database
|
||||||
|
TaskMaster.invokeMethodTask("readAndPlay", this, this);
|
||||||
|
|
||||||
|
} else if (cmd.equals("play")) {
|
||||||
|
Song song = SongItem.getSong(e.getSource());
|
||||||
|
Chooser.scontrol.append(song.entryid, song.songid, song.location);
|
||||||
|
|
||||||
|
} else if (cmd.equals("up")) {
|
||||||
|
// re-read the category beacuse this entry may have been
|
||||||
|
// recategorized
|
||||||
|
TaskMaster.invokeMethodTask("readEntries", this, this);
|
||||||
|
|
||||||
|
} else if (cmd.equals("edit")) {
|
||||||
|
EditDialog dialog = new EditDialog(_entry);
|
||||||
|
dialog.setSize(400, 400);
|
||||||
|
SwingUtil.centerWindow(dialog);
|
||||||
|
dialog.show();
|
||||||
|
|
||||||
|
} else if (cmd.equals("categorize")) {
|
||||||
|
JComboBox cb = (JComboBox)e.getSource();
|
||||||
|
String catname = (String)cb.getSelectedItem();
|
||||||
|
_newcatid = Chooser.model.getCategoryId(catname);
|
||||||
|
// do the recategorization in a separate task
|
||||||
|
TaskMaster.invokeMethodTask("recategorizeEntry", this, this);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return super.handleAction(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public void ancestorAdded (AncestorEvent e)
|
||||||
|
{
|
||||||
|
// stick a "loading" label in the list to let the user know
|
||||||
|
// what's up
|
||||||
|
_list._bpanel.add(new JLabel("Loading..."));
|
||||||
|
|
||||||
|
// we need to revalidate the component because we added a child
|
||||||
|
_list.revalidate();
|
||||||
|
_list.repaint();
|
||||||
|
|
||||||
|
// start up the task that reads the CD info from the database
|
||||||
|
TaskMaster.invokeMethodTask("readEntries", this, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public void ancestorRemoved (AncestorEvent e)
|
||||||
|
{
|
||||||
|
// clear out our entry ui elements
|
||||||
|
_list._bpanel.removeAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public void ancestorMoved (AncestorEvent e)
|
||||||
|
{
|
||||||
|
// nothing doing
|
||||||
|
}
|
||||||
|
|
||||||
|
protected EntryList _list;
|
||||||
|
protected Entry[] _entries;
|
||||||
|
protected Entry _entry;
|
||||||
|
protected int _newcatid;
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
//
|
||||||
|
// $Id: EntryItem.java,v 1.1 2003/05/04 18:16:06 mdb Exp $
|
||||||
|
|
||||||
|
package robodj.chooser;
|
||||||
|
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
|
||||||
|
import com.samskivert.swing.HGroupLayout;
|
||||||
|
|
||||||
|
import robodj.repository.Entry;
|
||||||
|
import robodj.util.ButtonUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays an entry in the browser.
|
||||||
|
*/
|
||||||
|
public class EntryItem extends Item
|
||||||
|
{
|
||||||
|
public static final String BROWSE = "entry:browse";
|
||||||
|
|
||||||
|
public static final String PLAY = "entry:play";
|
||||||
|
|
||||||
|
public EntryItem (Entry entry)
|
||||||
|
{
|
||||||
|
_entry = entry;
|
||||||
|
|
||||||
|
// set up our layout manager
|
||||||
|
HGroupLayout gl = new HGroupLayout(HGroupLayout.NONE);
|
||||||
|
gl.setOffAxisPolicy(HGroupLayout.STRETCH);
|
||||||
|
gl.setJustification(HGroupLayout.LEFT);
|
||||||
|
setLayout(gl);
|
||||||
|
|
||||||
|
// create a browse and a play button
|
||||||
|
JButton button;
|
||||||
|
|
||||||
|
// add a browse button
|
||||||
|
button = ButtonUtil.createControlButton(
|
||||||
|
BROWSE_ENTRY_TIP, BROWSE, _browseIcon, true);
|
||||||
|
button.putClientProperty(ENTRY_PROP, entry);
|
||||||
|
add(button, HGroupLayout.FIXED);
|
||||||
|
|
||||||
|
// add a play all button
|
||||||
|
button = ButtonUtil.createControlButton(
|
||||||
|
PLAY_ENTRY_TIP, PLAY, _playIcon, true);
|
||||||
|
button.putClientProperty(ENTRY_PROP, entry);
|
||||||
|
add(button, HGroupLayout.FIXED);
|
||||||
|
|
||||||
|
// add the entry title
|
||||||
|
JLabel entryLabel = new JLabel(entry.title);
|
||||||
|
entryLabel.setFont(_nameFont);
|
||||||
|
entryLabel.setToolTipText(entry.title + " (" + entry.entryid + ")");
|
||||||
|
add(entryLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Entry getEntry (Object source)
|
||||||
|
{
|
||||||
|
return (Entry)((JButton)source).getClientProperty(ENTRY_PROP);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Entry _entry;
|
||||||
|
|
||||||
|
protected static final String ENTRY_PROP = "entry";
|
||||||
|
|
||||||
|
protected static final String BROWSE_ENTRY_TIP =
|
||||||
|
"Browse the songs in this album";
|
||||||
|
protected static final String PLAY_ENTRY_TIP =
|
||||||
|
"Append this album to the playlist";
|
||||||
|
|
||||||
|
protected static ImageIcon _browseIcon =
|
||||||
|
ButtonUtil.getIcon(ICON_ROOT + "browse.png");
|
||||||
|
}
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
//
|
//
|
||||||
// $Id: EntryList.java,v 1.12 2002/03/03 20:56:12 mdb Exp $
|
// $Id: EntryList.java,v 1.13 2003/05/04 18:16:06 mdb Exp $
|
||||||
|
|
||||||
package robodj.chooser;
|
package robodj.chooser;
|
||||||
|
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@@ -21,7 +19,7 @@ import robodj.repository.*;
|
|||||||
import robodj.util.ButtonUtil;
|
import robodj.util.ButtonUtil;
|
||||||
|
|
||||||
public abstract class EntryList extends JScrollPane
|
public abstract class EntryList extends JScrollPane
|
||||||
implements TaskObserver, ActionListener, AncestorListener
|
implements ControllerProvider
|
||||||
{
|
{
|
||||||
public EntryList ()
|
public EntryList ()
|
||||||
{
|
{
|
||||||
@@ -46,88 +44,22 @@ public abstract class EntryList extends JScrollPane
|
|||||||
|
|
||||||
// use a special font for our name buttons
|
// use a special font for our name buttons
|
||||||
_titleFont = new Font("Helvetica", Font.BOLD, 14);
|
_titleFont = new Font("Helvetica", Font.BOLD, 14);
|
||||||
_nameFont = new Font("Helvetica", Font.PLAIN, 12);
|
|
||||||
|
|
||||||
// create our icons
|
// create our controller
|
||||||
_browseIcon = ButtonUtil.getIcon(BROWSE_ICON_PATH);
|
_controller = createController();
|
||||||
_playIcon = ButtonUtil.getIcon(PLAY_ICON_PATH);
|
|
||||||
|
|
||||||
// listen to ancestor events
|
|
||||||
addAncestorListener(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void taskCompleted (String name, Object result)
|
public Controller getController ()
|
||||||
{
|
{
|
||||||
if (name.equals("readEntries")) {
|
return _controller;
|
||||||
// the result should be an array of entry objects with which
|
|
||||||
// we can populate our button list
|
|
||||||
_entries = (Entry[])result;
|
|
||||||
populateEntries(_entries);
|
|
||||||
|
|
||||||
} else if (name.equals("readAndPlay")) {
|
|
||||||
for (int i = 0; i < _entry.songs.length; i++) {
|
|
||||||
Chooser.scontrol.append(_entry.songs[i].entryid,
|
|
||||||
_entry.songs[i].songid,
|
|
||||||
_entry.songs[i].location);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (name.equals("readSongs")) {
|
/** Creates the controller for this entry list. */
|
||||||
populateSong(_entry);
|
protected abstract Controller createController ();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void taskFailed (String name, Throwable exception)
|
|
||||||
{
|
|
||||||
String msg;
|
|
||||||
if (Exception.class.equals(exception.getClass())) {
|
|
||||||
msg = exception.getMessage();
|
|
||||||
} else {
|
|
||||||
msg = exception.toString();
|
|
||||||
}
|
|
||||||
JOptionPane.showMessageDialog(this, msg, "Error",
|
|
||||||
JOptionPane.ERROR_MESSAGE);
|
|
||||||
Log.logStackTrace(exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the entries from the database. This will be called on a
|
|
||||||
* separate thread and should be prepared to be called repeatedly
|
|
||||||
* without undue overhead. When the user browses into a song and back
|
|
||||||
* up again, this method is called to repopulate the entries.
|
|
||||||
*/
|
|
||||||
public abstract Entry[] readEntries ()
|
|
||||||
throws PersistenceException;
|
|
||||||
|
|
||||||
/** The string to display when there are no matching results. */
|
/** The string to display when there are no matching results. */
|
||||||
protected abstract String getEmptyString ();
|
protected abstract String getEmptyString ();
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the entries from the database.
|
|
||||||
*/
|
|
||||||
public void readSongs ()
|
|
||||||
throws PersistenceException
|
|
||||||
{
|
|
||||||
Chooser.repository.populateSongs(_entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the entries from the database and plays them all.
|
|
||||||
*/
|
|
||||||
public void readAndPlay ()
|
|
||||||
throws PersistenceException
|
|
||||||
{
|
|
||||||
Chooser.repository.populateSongs(_entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the category for the displayed entry.
|
|
||||||
*/
|
|
||||||
public void recategorizeEntry ()
|
|
||||||
throws PersistenceException
|
|
||||||
{
|
|
||||||
Chooser.model.recategorize(_entry, _newcatid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the proper buttons, etc. for each entry.
|
* Creates the proper buttons, etc. for each entry.
|
||||||
*/
|
*/
|
||||||
@@ -158,37 +90,12 @@ public abstract class EntryList extends JScrollPane
|
|||||||
_bpanel.add(label);
|
_bpanel.add(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a browse and a play button
|
// create an entry item for this entry
|
||||||
GroupLayout gl = new HGroupLayout(GroupLayout.NONE);
|
_bpanel.add(new EntryItem(entries[i]));
|
||||||
gl.setOffAxisPolicy(GroupLayout.STRETCH);
|
|
||||||
gl.setJustification(GroupLayout.LEFT);
|
|
||||||
JPanel hpanel = new JPanel(gl);
|
|
||||||
JButton button;
|
|
||||||
|
|
||||||
// add a browse button
|
|
||||||
button = ButtonUtil.createControlButton(
|
|
||||||
BROWSE_ENTRY_TIP, "browse", _browseIcon, this, true);
|
|
||||||
button.putClientProperty("entry", entries[i]);
|
|
||||||
hpanel.add(button, GroupLayout.FIXED);
|
|
||||||
|
|
||||||
// add a play all button
|
|
||||||
button = ButtonUtil.createControlButton(
|
|
||||||
PLAY_ENTRY_TIP, "playall", _playIcon, this, true);
|
|
||||||
button.putClientProperty("entry", entries[i]);
|
|
||||||
hpanel.add(button, GroupLayout.FIXED);
|
|
||||||
|
|
||||||
// add the entry title
|
|
||||||
JLabel entryLabel = new JLabel(entries[i].title);
|
|
||||||
entryLabel.setFont(_nameFont);
|
|
||||||
entryLabel.setToolTipText(entries[i].title +
|
|
||||||
" (" + entries[i].entryid + ")");
|
|
||||||
hpanel.add(entryLabel);
|
|
||||||
|
|
||||||
_bpanel.add(hpanel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there were no entries, stick a label in to that effect
|
// if there were no entries, stick a label in to that effect
|
||||||
if (_entries.length == 0) {
|
if (entries.length == 0) {
|
||||||
_bpanel.add(new JLabel(getEmptyString()));
|
_bpanel.add(new JLabel(getEmptyString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,12 +131,12 @@ public abstract class EntryList extends JScrollPane
|
|||||||
|
|
||||||
// add a button for getting the heck out of here
|
// add a button for getting the heck out of here
|
||||||
JButton upbtn = ButtonUtil.createControlButton(
|
JButton upbtn = ButtonUtil.createControlButton(
|
||||||
UP_TIP, "up", ButtonUtil.getIcon(UP_ICON_PATH), this, true);
|
UP_TIP, "up", ButtonUtil.getIcon(UP_ICON_PATH), true);
|
||||||
header.add(upbtn, GroupLayout.FIXED);
|
header.add(upbtn, GroupLayout.FIXED);
|
||||||
|
|
||||||
// create an edit button
|
// create an edit button
|
||||||
JButton ebtn = ButtonUtil.createControlButton(
|
JButton ebtn = ButtonUtil.createControlButton(
|
||||||
EDIT_TIP, "edit", ButtonUtil.getIcon(EDIT_ICON_PATH), this, true);
|
EDIT_TIP, "edit", ButtonUtil.getIcon(EDIT_ICON_PATH), true);
|
||||||
header.add(ebtn, GroupLayout.FIXED);
|
header.add(ebtn, GroupLayout.FIXED);
|
||||||
|
|
||||||
// add a combo box for categorizing
|
// add a combo box for categorizing
|
||||||
@@ -238,7 +145,7 @@ public abstract class EntryList extends JScrollPane
|
|||||||
header.add(catcombo, GroupLayout.FIXED);
|
header.add(catcombo, GroupLayout.FIXED);
|
||||||
|
|
||||||
// configure the combo box
|
// configure the combo box
|
||||||
catcombo.addActionListener(this);
|
catcombo.addActionListener(Controller.DISPATCHER);
|
||||||
catcombo.setActionCommand("categorize");
|
catcombo.setActionCommand("categorize");
|
||||||
int catid = Chooser.model.getCategory(entry.entryid);
|
int catid = Chooser.model.getCategory(entry.entryid);
|
||||||
int catidx = ModelUtil.getCategoryIndex(Chooser.model, catid);
|
int catidx = ModelUtil.getCategoryIndex(Chooser.model, catid);
|
||||||
@@ -260,23 +167,7 @@ public abstract class EntryList extends JScrollPane
|
|||||||
|
|
||||||
// and add buttons for every song
|
// and add buttons for every song
|
||||||
for (int i = 0; i < entry.songs.length; i++) {
|
for (int i = 0; i < entry.songs.length; i++) {
|
||||||
gl = new HGroupLayout(GroupLayout.NONE);
|
_bpanel.add(new SongItem(entry.songs[i], SongItem.BROWSER));
|
||||||
gl.setJustification(GroupLayout.LEFT);
|
|
||||||
JPanel hpanel = new JPanel(gl);
|
|
||||||
|
|
||||||
// add a button for playing the song
|
|
||||||
JButton button = ButtonUtil.createControlButton(
|
|
||||||
PLAY_SONG_TIP, "play", _playIcon, this, true);
|
|
||||||
button.putClientProperty("song", entry.songs[i]);
|
|
||||||
hpanel.add(button, GroupLayout.FIXED);
|
|
||||||
|
|
||||||
// add the song title
|
|
||||||
JLabel trackLabel = new JLabel(entry.songs[i].title);
|
|
||||||
trackLabel.setFont(_nameFont);
|
|
||||||
trackLabel.setToolTipText(entry.songs[i].title);
|
|
||||||
hpanel.add(trackLabel);
|
|
||||||
|
|
||||||
_bpanel.add(hpanel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset our scroll position so that we're displaying the top of
|
// reset our scroll position so that we're displaying the top of
|
||||||
@@ -294,99 +185,17 @@ public abstract class EntryList extends JScrollPane
|
|||||||
model.setValue(model.getMinimum());
|
model.setValue(model.getMinimum());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void actionPerformed (ActionEvent e)
|
protected Controller _controller;
|
||||||
{
|
|
||||||
String cmd = e.getActionCommand();
|
|
||||||
if (cmd.equals("browse")) {
|
|
||||||
JButton src = (JButton)e.getSource();
|
|
||||||
_entry = (Entry)src.getClientProperty("entry");
|
|
||||||
// start up the task that reads this CDs songs from the database
|
|
||||||
TaskMaster.invokeMethodTask("readSongs", this, this);
|
|
||||||
|
|
||||||
} else if (cmd.equals("playall")) {
|
|
||||||
JButton src = (JButton)e.getSource();
|
|
||||||
_entry = (Entry)src.getClientProperty("entry");
|
|
||||||
// start up the task that reads this CDs songs from the database
|
|
||||||
TaskMaster.invokeMethodTask("readAndPlay", this, this);
|
|
||||||
|
|
||||||
} else if (cmd.equals("play")) {
|
|
||||||
JButton src = (JButton)e.getSource();
|
|
||||||
Song song = (Song)src.getClientProperty("song");
|
|
||||||
Chooser.scontrol.append(song.entryid, song.songid, song.location);
|
|
||||||
|
|
||||||
} else if (cmd.equals("up")) {
|
|
||||||
// re-read the category beacuse this entry may have been
|
|
||||||
// recategorized
|
|
||||||
TaskMaster.invokeMethodTask("readEntries", this, this);
|
|
||||||
|
|
||||||
} else if (cmd.equals("edit")) {
|
|
||||||
EditDialog dialog = new EditDialog(_entry);
|
|
||||||
dialog.setSize(400, 400);
|
|
||||||
SwingUtil.centerWindow(dialog);
|
|
||||||
dialog.show();
|
|
||||||
|
|
||||||
} else if (cmd.equals("categorize")) {
|
|
||||||
JComboBox cb = (JComboBox)e.getSource();
|
|
||||||
String catname = (String)cb.getSelectedItem();
|
|
||||||
_newcatid = Chooser.model.getCategoryId(catname);
|
|
||||||
// do the recategorization in a separate task
|
|
||||||
TaskMaster.invokeMethodTask("recategorizeEntry", this, this);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Log.warning("Unknown action event: " + cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ancestorAdded (AncestorEvent e)
|
|
||||||
{
|
|
||||||
// stick a "loading" label in the list to let the user know
|
|
||||||
// what's up
|
|
||||||
_bpanel.add(new JLabel("Loading..."));
|
|
||||||
|
|
||||||
// we need to revalidate the component because we added a child
|
|
||||||
revalidate();
|
|
||||||
repaint();
|
|
||||||
|
|
||||||
// start up the task that reads the CD info from the database
|
|
||||||
TaskMaster.invokeMethodTask("readEntries", this, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ancestorRemoved (AncestorEvent e)
|
|
||||||
{
|
|
||||||
// clear out our entry ui elements
|
|
||||||
_bpanel.removeAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ancestorMoved (AncestorEvent e)
|
|
||||||
{
|
|
||||||
// nothing doing
|
|
||||||
}
|
|
||||||
|
|
||||||
protected JPanel _bpanel;
|
protected JPanel _bpanel;
|
||||||
|
|
||||||
protected Entry[] _entries;
|
|
||||||
protected Entry _entry;
|
|
||||||
protected int _newcatid;
|
|
||||||
|
|
||||||
protected Font _titleFont;
|
protected Font _titleFont;
|
||||||
protected Font _nameFont;
|
|
||||||
protected ImageIcon _playIcon;
|
|
||||||
protected ImageIcon _browseIcon;
|
|
||||||
|
|
||||||
protected static final String PLAY_ENTRY_TIP =
|
|
||||||
"Append this album to the playlist";
|
|
||||||
protected static final String PLAY_SONG_TIP =
|
|
||||||
"Append this song to the playlist";
|
|
||||||
protected static final String BROWSE_ENTRY_TIP =
|
|
||||||
"Browse the songs in this album";
|
|
||||||
protected static final String UP_TIP =
|
protected static final String UP_TIP =
|
||||||
"Back up to albums listing";
|
"Back up to albums listing";
|
||||||
protected static final String EDIT_TIP =
|
protected static final String EDIT_TIP =
|
||||||
"Edit the album information";
|
"Edit the album information";
|
||||||
|
|
||||||
protected static final String ICON_ROOT = "/robodj/chooser/images/";
|
protected static final String ICON_ROOT = "/robodj/chooser/images/";
|
||||||
protected static final String PLAY_ICON_PATH = ICON_ROOT + "play.png";
|
|
||||||
protected static final String BROWSE_ICON_PATH = ICON_ROOT + "browse.png";
|
|
||||||
protected static final String UP_ICON_PATH = ICON_ROOT + "up.png";
|
protected static final String UP_ICON_PATH = ICON_ROOT + "up.png";
|
||||||
protected static final String EDIT_ICON_PATH = ICON_ROOT + "edit.png";
|
protected static final String EDIT_ICON_PATH = ICON_ROOT + "edit.png";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
//
|
||||||
|
// $Id: Item.java,v 1.1 2003/05/04 18:16:06 mdb Exp $
|
||||||
|
|
||||||
|
package robodj.chooser;
|
||||||
|
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
|
||||||
|
import com.samskivert.swing.util.SwingUtil;
|
||||||
|
|
||||||
|
import robodj.util.ButtonUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base class for items displayed in an entry list or playlist.
|
||||||
|
*/
|
||||||
|
public class Item extends JPanel
|
||||||
|
{
|
||||||
|
// documentation inherited
|
||||||
|
protected void paintChildren (Graphics g)
|
||||||
|
{
|
||||||
|
Graphics2D gfx = (Graphics2D)g;
|
||||||
|
Object key = SwingUtil.activateAntiAliasing(gfx);
|
||||||
|
super.paintChildren(g);
|
||||||
|
SwingUtil.restoreAntiAliasing(gfx, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Font _nameFont = new Font("Dialog", Font.PLAIN, 12);
|
||||||
|
protected static Font _hasVotesFont =
|
||||||
|
new Font("Dialog", Font.ITALIC, 12);
|
||||||
|
|
||||||
|
protected static final String ICON_ROOT = "/robodj/chooser/images/";
|
||||||
|
|
||||||
|
protected static ImageIcon _playIcon =
|
||||||
|
ButtonUtil.getIcon(ICON_ROOT + "play.png");
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
//
|
||||||
|
// $Id: ItemController.java,v 1.1 2003/05/04 18:16:06 mdb Exp $
|
||||||
|
|
||||||
|
package robodj.chooser;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
|
||||||
|
import com.samskivert.io.PersistenceException;
|
||||||
|
import com.samskivert.swing.Controller;
|
||||||
|
import com.samskivert.swing.util.TaskMaster;
|
||||||
|
import com.samskivert.swing.util.TaskObserver;
|
||||||
|
|
||||||
|
import robodj.Log;
|
||||||
|
import robodj.repository.Song;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles standard commands when displaying lists of items.
|
||||||
|
*/
|
||||||
|
public abstract class ItemController extends Controller
|
||||||
|
implements TaskObserver
|
||||||
|
{
|
||||||
|
public ItemController (JComponent panel)
|
||||||
|
{
|
||||||
|
_panel = panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public boolean handleAction (ActionEvent e)
|
||||||
|
{
|
||||||
|
String cmd = e.getActionCommand();
|
||||||
|
if (cmd.equals(SongItem.ADD_VOTE)) {
|
||||||
|
JButton button = (JButton)e.getSource();
|
||||||
|
SongItem item = (SongItem)button.getParent();
|
||||||
|
_song = item.getSong();
|
||||||
|
if (_song.addVote(Chooser.frame.getUser(true))) {
|
||||||
|
item.update();
|
||||||
|
TaskMaster.invokeMethodTask("updateSong", this, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (cmd.equals(SongItem.CLEAR_VOTE)) {
|
||||||
|
JButton button = (JButton)e.getSource();
|
||||||
|
SongItem item = (SongItem)button.getParent();
|
||||||
|
_song = item.getSong();
|
||||||
|
if (_song.clearVote(Chooser.frame.getUser(true))) {
|
||||||
|
item.update();
|
||||||
|
TaskMaster.invokeMethodTask("updateSong", this, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.warning("Unknown action event: " + cmd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the song referenced by {@link #_song} in the repository.
|
||||||
|
*/
|
||||||
|
public void updateSong ()
|
||||||
|
{
|
||||||
|
if (_song != null) {
|
||||||
|
try {
|
||||||
|
Chooser.repository.updateSong(_song);
|
||||||
|
} catch (PersistenceException pe) {
|
||||||
|
Chooser.reportError(
|
||||||
|
"Failure updating song '" + _song + "'", pe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public void taskCompleted (String name, Object result)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface
|
||||||
|
public void taskFailed (String name, Throwable exception)
|
||||||
|
{
|
||||||
|
String msg;
|
||||||
|
if (Exception.class.equals(exception.getClass())) {
|
||||||
|
msg = exception.getMessage();
|
||||||
|
} else {
|
||||||
|
msg = exception.toString();
|
||||||
|
}
|
||||||
|
JOptionPane.showMessageDialog(_panel, msg, "Error",
|
||||||
|
JOptionPane.ERROR_MESSAGE);
|
||||||
|
Log.logStackTrace(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JComponent _panel;
|
||||||
|
protected Song _song;
|
||||||
|
}
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
//
|
||||||
|
// $Id: PlaylistController.java,v 1.1 2003/05/04 18:16:06 mdb Exp $
|
||||||
|
|
||||||
|
package robodj.chooser;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
|
||||||
|
import com.samskivert.io.PersistenceException;
|
||||||
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
|
import com.samskivert.swing.util.SwingUtil;
|
||||||
|
import com.samskivert.swing.util.TaskAdapter;
|
||||||
|
import com.samskivert.swing.util.TaskMaster;
|
||||||
|
|
||||||
|
import robodj.Log;
|
||||||
|
import robodj.chooser.PlaylistPanel.PlaylistEntry;
|
||||||
|
import robodj.repository.Entry;
|
||||||
|
import robodj.repository.Song;
|
||||||
|
import robodj.util.ServerControl.PlayingListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles playlist UI commands.
|
||||||
|
*/
|
||||||
|
public class PlaylistController extends ItemController
|
||||||
|
implements PlayingListener
|
||||||
|
{
|
||||||
|
public PlaylistController (PlaylistPanel panel)
|
||||||
|
{
|
||||||
|
super(panel);
|
||||||
|
_panel = panel;
|
||||||
|
|
||||||
|
// add ourselves as a playing listener
|
||||||
|
Chooser.scontrol.addPlayingListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited
|
||||||
|
public boolean handleAction (ActionEvent e)
|
||||||
|
{
|
||||||
|
String cmd = e.getActionCommand();
|
||||||
|
if (cmd.equals(PlaylistPanel.CLEAR)) {
|
||||||
|
TaskMaster.invokeMethodTask("clear", Chooser.scontrol, this);
|
||||||
|
|
||||||
|
} else if (cmd.equals(PlaylistPanel.REFRESH)) {
|
||||||
|
_panel.prepareForRefresh();
|
||||||
|
|
||||||
|
// start up the task that reads the CD info from the database
|
||||||
|
TaskMaster.invokeMethodTask("readPlaylist", this, this);
|
||||||
|
|
||||||
|
} else if (cmd.equals(SongItem.SKIP_TO)) {
|
||||||
|
JButton button = (JButton)e.getSource();
|
||||||
|
SongItem item = (SongItem)button.getParent();
|
||||||
|
|
||||||
|
// fire up a task to talk to the music daemon
|
||||||
|
final int songid = item.getSong().songid;
|
||||||
|
TaskMaster.invokeTask("noop", new TaskAdapter() {
|
||||||
|
public Object invoke () throws Exception {
|
||||||
|
Chooser.scontrol.skipto(songid);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
} else if (cmd.equals(SongItem.REMOVE)) {
|
||||||
|
JButton button = (JButton)e.getSource();
|
||||||
|
SongItem item = (SongItem)button.getParent();
|
||||||
|
|
||||||
|
// remove the entry UI elements
|
||||||
|
item.getParent().remove(item);
|
||||||
|
SwingUtil.refresh(_panel); // relayout
|
||||||
|
|
||||||
|
// remove the entry from the playlist
|
||||||
|
_panel.plist.remove((PlaylistEntry)item.extra);
|
||||||
|
|
||||||
|
// fire up a task to talk to the music daemon
|
||||||
|
final int songid = item.getSong().songid;
|
||||||
|
TaskMaster.invokeTask("noop", new TaskAdapter() {
|
||||||
|
public Object invoke () throws Exception {
|
||||||
|
Chooser.scontrol.remove(songid);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
} else if (cmd.equals(PlaylistPanel.REMOVE_ALL)) {
|
||||||
|
JButton src = (JButton)e.getSource();
|
||||||
|
PlaylistEntry entry =
|
||||||
|
(PlaylistEntry)src.getClientProperty("entry");
|
||||||
|
|
||||||
|
// remove all entries starting with this one until we get to
|
||||||
|
// one that has a different entryid
|
||||||
|
int count = 0;
|
||||||
|
Iterator iter = _panel.plist.iterator();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
PlaylistEntry pe = (PlaylistEntry)iter.next();
|
||||||
|
if (entry == pe) {
|
||||||
|
count++;
|
||||||
|
// remove the entry UI elements
|
||||||
|
pe.item.getParent().remove(pe.item);
|
||||||
|
// remove the entry
|
||||||
|
iter.remove();
|
||||||
|
|
||||||
|
} else if (count > 0) {
|
||||||
|
if (pe.entry.entryid == entry.entry.entryid) {
|
||||||
|
count++;
|
||||||
|
// remove the entry UI elements
|
||||||
|
pe.item.getParent().remove(pe.item);
|
||||||
|
// remove the entry
|
||||||
|
iter.remove();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// we hit an entry that doesn't match, bail
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the title and stuff
|
||||||
|
JPanel tpanel = (JPanel)src.getParent();
|
||||||
|
tpanel.getParent().remove(tpanel);
|
||||||
|
|
||||||
|
_panel.revalidate(); // relayout
|
||||||
|
|
||||||
|
// fire up a task to talk to the music daemon, telling it to
|
||||||
|
// remove those entries
|
||||||
|
final int songid = entry.song.songid;
|
||||||
|
final int fcount = count;
|
||||||
|
TaskMaster.invokeTask("noop", new TaskAdapter() {
|
||||||
|
public Object invoke () throws Exception {
|
||||||
|
Chooser.scontrol.removeGroup(songid, fcount);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return super.handleAction(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readPlaylist ()
|
||||||
|
throws PersistenceException
|
||||||
|
{
|
||||||
|
// clear out any previous playlist
|
||||||
|
_panel.plist.clear();
|
||||||
|
|
||||||
|
// get the playlist from the music daemon
|
||||||
|
String[] plist = Chooser.scontrol.getPlaylist();
|
||||||
|
|
||||||
|
// parse it into playlist entries
|
||||||
|
for (int i = 0; i < plist.length; i++) {
|
||||||
|
String[] toks = StringUtil.split(plist[i], "\t");
|
||||||
|
int eid, sid;
|
||||||
|
try {
|
||||||
|
eid = Integer.parseInt(toks[0]);
|
||||||
|
sid = Integer.parseInt(toks[1]);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
Log.warning("Bogus playlist record: '" + plist[i] + "'.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Entry entry = Chooser.model.getEntry(eid);
|
||||||
|
if (entry != null) {
|
||||||
|
Song song = entry.getSong(sid);
|
||||||
|
_panel.plist.add(new PlaylistEntry(entry, song));
|
||||||
|
} else {
|
||||||
|
Log.warning("Unable to load entry [eid=" + eid + "].");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now find out what's currently playing
|
||||||
|
Chooser.scontrol.refreshPlaying();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void playingUpdated (int songid, boolean paused)
|
||||||
|
{
|
||||||
|
// unhighlight whoever is playing now
|
||||||
|
PlaylistEntry pentry = getPlayingEntry();
|
||||||
|
if (pentry != null && pentry.item != null) {
|
||||||
|
pentry.item.setIsPlaying(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// grab the new playing song id
|
||||||
|
_playid = songid;
|
||||||
|
|
||||||
|
// highlight the playing song
|
||||||
|
for (int i = 0; i < _panel.plist.size(); i++) {
|
||||||
|
PlaylistEntry entry = (PlaylistEntry)_panel.plist.get(i);
|
||||||
|
if (entry.song.songid == _playid && entry.item != null) {
|
||||||
|
entry.item.setIsPlaying(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_panel.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PlaylistEntry getPlayingEntry ()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _panel.plist.size(); i++) {
|
||||||
|
PlaylistEntry entry = (PlaylistEntry)_panel.plist.get(i);
|
||||||
|
if (entry.song.songid == _playid) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void taskCompleted (String name, Object result)
|
||||||
|
{
|
||||||
|
if (name.equals("readPlaylist")) {
|
||||||
|
_panel.populatePlaylist(_panel.plist, _playid);
|
||||||
|
} else {
|
||||||
|
super.taskCompleted(name, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PlaylistPanel _panel;
|
||||||
|
|
||||||
|
protected int _playid;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: PlaylistPanel.java,v 1.13 2002/03/03 20:56:12 mdb Exp $
|
// $Id: PlaylistPanel.java,v 1.14 2003/05/04 18:16:06 mdb Exp $
|
||||||
|
|
||||||
package robodj.chooser;
|
package robodj.chooser;
|
||||||
|
|
||||||
@@ -19,11 +19,32 @@ import com.samskivert.util.StringUtil;
|
|||||||
import robodj.Log;
|
import robodj.Log;
|
||||||
import robodj.repository.*;
|
import robodj.repository.*;
|
||||||
import robodj.util.ButtonUtil;
|
import robodj.util.ButtonUtil;
|
||||||
import robodj.util.ServerControl.PlayingListener;
|
|
||||||
|
|
||||||
public class PlaylistPanel extends JPanel
|
public class PlaylistPanel extends ControlledPanel
|
||||||
implements TaskObserver, ActionListener, PlayingListener
|
|
||||||
{
|
{
|
||||||
|
public static class PlaylistEntry
|
||||||
|
{
|
||||||
|
public Entry entry;
|
||||||
|
|
||||||
|
public Song song;
|
||||||
|
|
||||||
|
public SongItem item;
|
||||||
|
|
||||||
|
public PlaylistEntry (Entry entry, Song song)
|
||||||
|
{
|
||||||
|
this.entry = entry;
|
||||||
|
this.song = song;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String REFRESH = "refresh";
|
||||||
|
|
||||||
|
public static final String CLEAR = "clear";
|
||||||
|
|
||||||
|
public static final String REMOVE_ALL = "remove_all";
|
||||||
|
|
||||||
|
public ArrayList plist = new ArrayList();
|
||||||
|
|
||||||
public PlaylistPanel ()
|
public PlaylistPanel ()
|
||||||
{
|
{
|
||||||
GroupLayout gl = new VGroupLayout(GroupLayout.STRETCH);
|
GroupLayout gl = new VGroupLayout(GroupLayout.STRETCH);
|
||||||
@@ -50,127 +71,34 @@ public class PlaylistPanel extends JPanel
|
|||||||
|
|
||||||
// add our control buttons
|
// add our control buttons
|
||||||
_clearbut = ButtonUtil.createControlButton(
|
_clearbut = ButtonUtil.createControlButton(
|
||||||
CLEAR_TIP, "clear", CLEAR_ICON_PATH, this);
|
CLEAR_TIP, "clear", CLEAR_ICON_PATH);
|
||||||
cbar.add(_clearbut);
|
cbar.add(_clearbut);
|
||||||
cbar.add(ButtonUtil.createControlButton(
|
cbar.add(ButtonUtil.createControlButton(
|
||||||
REFRESH_TIP, "refresh", REFRESH_ICON_PATH, this));
|
REFRESH_TIP, "refresh", REFRESH_ICON_PATH));
|
||||||
add(cbar, GroupLayout.FIXED);
|
add(cbar, GroupLayout.FIXED);
|
||||||
|
|
||||||
// use a special font for our name buttons
|
// use a special font for our name buttons
|
||||||
_nameFont = new Font("Helvetica", Font.PLAIN, 12);
|
_nameFont = new Font("Helvetica", Font.PLAIN, 12);
|
||||||
|
|
||||||
// create our icons
|
// create our icons
|
||||||
_skiptoIcon = ButtonUtil.getIcon(SKIPTO_ICON_PATH);
|
|
||||||
_removeSongIcon = ButtonUtil.getIcon(REMOVE_SONG_ICON_PATH);
|
|
||||||
_removeEntryIcon = ButtonUtil.getIcon(REMOVE_ENTRY_ICON_PATH);
|
_removeEntryIcon = ButtonUtil.getIcon(REMOVE_ENTRY_ICON_PATH);
|
||||||
|
|
||||||
// add ourselves as a playing listener
|
|
||||||
Chooser.scontrol.addPlayingListener(this);
|
|
||||||
|
|
||||||
// load up the playlist
|
|
||||||
refreshPlaylist();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void actionPerformed (ActionEvent e)
|
// documentation inherited
|
||||||
|
public void addNotify ()
|
||||||
{
|
{
|
||||||
String cmd = e.getActionCommand();
|
super.addNotify();
|
||||||
if (cmd.equals("clear")) {
|
// load up the playlist
|
||||||
TaskMaster.invokeMethodTask("clear", Chooser.scontrol, this);
|
_controller.postAction(this, REFRESH);
|
||||||
|
|
||||||
} else if (cmd.equals("refresh")) {
|
|
||||||
refreshPlaylist();
|
|
||||||
|
|
||||||
} else if (cmd.equals("skipto")) {
|
|
||||||
JButton src = (JButton)e.getSource();
|
|
||||||
PlaylistEntry entry =
|
|
||||||
(PlaylistEntry)src.getClientProperty("entry");
|
|
||||||
|
|
||||||
// fire up a task to talk to the music daemon
|
|
||||||
final int songid = entry.song.songid;
|
|
||||||
TaskMaster.invokeTask("noop", new TaskAdapter() {
|
|
||||||
public Object invoke () throws Exception {
|
|
||||||
Chooser.scontrol.skipto(songid);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
} else if (cmd.equals("remove")) {
|
|
||||||
JButton src = (JButton)e.getSource();
|
|
||||||
PlaylistEntry entry =
|
|
||||||
(PlaylistEntry)src.getClientProperty("entry");
|
|
||||||
|
|
||||||
// remove the entry UI elements
|
|
||||||
JPanel epanel = (JPanel)entry.label.getParent();
|
|
||||||
epanel.getParent().remove(epanel);
|
|
||||||
revalidate(); // relayout
|
|
||||||
|
|
||||||
// remove the entry from the playlist
|
|
||||||
_plist.remove(entry);
|
|
||||||
|
|
||||||
// fire up a task to talk to the music daemon
|
|
||||||
final int songid = entry.song.songid;
|
|
||||||
TaskMaster.invokeTask("noop", new TaskAdapter() {
|
|
||||||
public Object invoke () throws Exception {
|
|
||||||
Chooser.scontrol.remove(songid);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
} else if (cmd.equals("remove_all")) {
|
|
||||||
JButton src = (JButton)e.getSource();
|
|
||||||
PlaylistEntry entry =
|
|
||||||
(PlaylistEntry)src.getClientProperty("entry");
|
|
||||||
|
|
||||||
// remove all entries starting with this one until we get to
|
|
||||||
// one that has a different entryid
|
|
||||||
int count = 0;
|
|
||||||
Iterator iter = _plist.iterator();
|
|
||||||
while (iter.hasNext()) {
|
|
||||||
PlaylistEntry pe = (PlaylistEntry)iter.next();
|
|
||||||
if (entry == pe) {
|
|
||||||
count++;
|
|
||||||
// remove the entry UI elements
|
|
||||||
JPanel epanel = (JPanel)pe.label.getParent();
|
|
||||||
epanel.getParent().remove(epanel);
|
|
||||||
// remove the entry
|
|
||||||
iter.remove();
|
|
||||||
|
|
||||||
} else if (count > 0) {
|
|
||||||
if (pe.entry.entryid == entry.entry.entryid) {
|
|
||||||
count++;
|
|
||||||
// remove the entry UI elements
|
|
||||||
JPanel epanel = (JPanel)pe.label.getParent();
|
|
||||||
epanel.getParent().remove(epanel);
|
|
||||||
// remove the entry
|
|
||||||
iter.remove();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// we hit an entry that doesn't match, bail
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove the title and stuff
|
// documentation inherited
|
||||||
JPanel tpanel = (JPanel)src.getParent();
|
protected Controller createController ()
|
||||||
tpanel.getParent().remove(tpanel);
|
{
|
||||||
|
return new PlaylistController(this);
|
||||||
revalidate(); // relayout
|
|
||||||
|
|
||||||
// fire up a task to talk to the music daemon, telling it to
|
|
||||||
// remove those entries
|
|
||||||
final int songid = entry.song.songid;
|
|
||||||
final int fcount = count;
|
|
||||||
TaskMaster.invokeTask("noop", new TaskAdapter() {
|
|
||||||
public Object invoke () throws Exception {
|
|
||||||
Chooser.scontrol.removeGroup(songid, fcount);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}, this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void refreshPlaylist ()
|
protected void prepareForRefresh ()
|
||||||
{
|
{
|
||||||
// stick a "loading" label in the list to let the user know
|
// stick a "loading" label in the list to let the user know
|
||||||
// what's up
|
// what's up
|
||||||
@@ -179,65 +107,9 @@ public class PlaylistPanel extends JPanel
|
|||||||
// swing doesn't automatically validate after adding/removing
|
// swing doesn't automatically validate after adding/removing
|
||||||
// children
|
// children
|
||||||
_bpanel.revalidate();
|
_bpanel.revalidate();
|
||||||
|
|
||||||
// start up the task that reads the CD info from the database
|
|
||||||
TaskMaster.invokeMethodTask("readPlaylist", this, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readPlaylist ()
|
protected void populatePlaylist (ArrayList plist, int playid)
|
||||||
throws PersistenceException
|
|
||||||
{
|
|
||||||
// clear out any previous playlist
|
|
||||||
_plist.clear();
|
|
||||||
|
|
||||||
// get the playlist from the music daemon
|
|
||||||
String[] plist = Chooser.scontrol.getPlaylist();
|
|
||||||
|
|
||||||
// parse it into playlist entries
|
|
||||||
for (int i = 0; i < plist.length; i++) {
|
|
||||||
String[] toks = StringUtil.split(plist[i], "\t");
|
|
||||||
int eid, sid;
|
|
||||||
try {
|
|
||||||
eid = Integer.parseInt(toks[0]);
|
|
||||||
sid = Integer.parseInt(toks[1]);
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
Log.warning("Bogus playlist record: '" + plist[i] + "'.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Entry entry = Chooser.model.getEntry(eid);
|
|
||||||
if (entry != null) {
|
|
||||||
Song song = entry.getSong(sid);
|
|
||||||
_plist.add(new PlaylistEntry(entry, song));
|
|
||||||
} else {
|
|
||||||
Log.warning("Unable to load entry [eid=" + eid + "].");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now find out what's currently playing
|
|
||||||
Chooser.scontrol.refreshPlaying();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void taskCompleted (String name, Object result)
|
|
||||||
{
|
|
||||||
if (name.equals("readPlaylist")) {
|
|
||||||
populatePlaylist();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void taskFailed (String name, Throwable exception)
|
|
||||||
{
|
|
||||||
String msg;
|
|
||||||
if (Exception.class.equals(exception.getClass())) {
|
|
||||||
msg = exception.getMessage();
|
|
||||||
} else {
|
|
||||||
msg = exception.toString();
|
|
||||||
}
|
|
||||||
JOptionPane.showMessageDialog(this, msg, "Error",
|
|
||||||
JOptionPane.ERROR_MESSAGE);
|
|
||||||
Log.logStackTrace(exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void populatePlaylist ()
|
|
||||||
{
|
{
|
||||||
// clear out any existing children
|
// clear out any existing children
|
||||||
_bpanel.removeAll();
|
_bpanel.removeAll();
|
||||||
@@ -249,8 +121,8 @@ public class PlaylistPanel extends JPanel
|
|||||||
|
|
||||||
// add buttons for every entry
|
// add buttons for every entry
|
||||||
String title = null;
|
String title = null;
|
||||||
for (int i = 0; i < _plist.size(); i++) {
|
for (int i = 0; i < plist.size(); i++) {
|
||||||
PlaylistEntry entry = (PlaylistEntry)_plist.get(i);
|
PlaylistEntry entry = (PlaylistEntry)plist.get(i);
|
||||||
JButton button;
|
JButton button;
|
||||||
|
|
||||||
// add record/artist indicators when the record and artist
|
// add record/artist indicators when the record and artist
|
||||||
@@ -266,41 +138,27 @@ public class PlaylistPanel extends JPanel
|
|||||||
JLabel label = new JLabel(entry.entry.title + " - " +
|
JLabel label = new JLabel(entry.entry.title + " - " +
|
||||||
entry.entry.artist);
|
entry.entry.artist);
|
||||||
tpanel.add(label);
|
tpanel.add(label);
|
||||||
tpanel.add(newButton(REMOVE_ENTRY_TIP, "remove_all",
|
tpanel.add(newButton(REMOVE_ENTRY_TIP, REMOVE_ALL,
|
||||||
_removeEntryIcon, entry));
|
_removeEntryIcon, entry));
|
||||||
|
|
||||||
_bpanel.add(tpanel);
|
_bpanel.add(tpanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a browse and a play button
|
// create a song item to display this playlist entry
|
||||||
JPanel hpanel = new JPanel();
|
entry.item = new SongItem(entry.song, SongItem.PLAYLIST);
|
||||||
gl = new HGroupLayout(GroupLayout.NONE);
|
entry.item.setIsPlaying(entry.song.songid == playid);
|
||||||
gl.setOffAxisPolicy(GroupLayout.STRETCH);
|
entry.item.extra = entry;
|
||||||
gl.setJustification(GroupLayout.LEFT);
|
_bpanel.add(entry.item);
|
||||||
hpanel.setLayout(gl);
|
|
||||||
|
|
||||||
hpanel.add(newButton(SKIPTO_TIP, "skipto", _skiptoIcon, entry));
|
|
||||||
hpanel.add(newButton(REMOVE_SONG_TIP, "remove",
|
|
||||||
_removeSongIcon, entry));
|
|
||||||
|
|
||||||
entry.label = new JLabel(entry.song.title);
|
|
||||||
entry.label.setForeground((entry.song.songid == _playid) ?
|
|
||||||
Color.red : Color.black);
|
|
||||||
entry.label.setFont(_nameFont);
|
|
||||||
entry.label.setToolTipText(entry.song.title);
|
|
||||||
hpanel.add(entry.label);
|
|
||||||
|
|
||||||
// let the bpanel know that we want to scroll the active track
|
// let the bpanel know that we want to scroll the active track
|
||||||
// label into place once we're all laid out
|
// label into place once we're all laid out
|
||||||
if (entry.song.songid == _playid) {
|
if (entry.song.songid == playid) {
|
||||||
_bpanel.setScrollTarget(hpanel);
|
_bpanel.setScrollTarget(entry.item);
|
||||||
}
|
}
|
||||||
|
|
||||||
_bpanel.add(hpanel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there were no entries, stick a label in to that effect
|
// if there were no entries, stick a label in to that effect
|
||||||
if (_plist.size() == 0) {
|
if (plist.size() == 0) {
|
||||||
_bpanel.add(new JLabel("Nothing playing."));
|
_bpanel.add(new JLabel("Nothing playing."));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,59 +171,11 @@ public class PlaylistPanel extends JPanel
|
|||||||
ImageIcon icon, Object clientProperty)
|
ImageIcon icon, Object clientProperty)
|
||||||
{
|
{
|
||||||
JButton button = ButtonUtil.createControlButton(
|
JButton button = ButtonUtil.createControlButton(
|
||||||
tooltip, command, icon, this, true);
|
tooltip, command, icon, true);
|
||||||
button.putClientProperty("entry", clientProperty);
|
button.putClientProperty("entry", clientProperty);
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playingUpdated (int songid, boolean paused)
|
|
||||||
{
|
|
||||||
// unhighlight whoever is playing now
|
|
||||||
PlaylistEntry pentry = getPlayingEntry();
|
|
||||||
if (pentry != null && pentry.label != null) {
|
|
||||||
pentry.label.setForeground(Color.black);
|
|
||||||
}
|
|
||||||
|
|
||||||
// grab the new playing song id
|
|
||||||
_playid = songid;
|
|
||||||
|
|
||||||
// highlight the playing song
|
|
||||||
for (int i = 0; i < _plist.size(); i++) {
|
|
||||||
PlaylistEntry entry = (PlaylistEntry)_plist.get(i);
|
|
||||||
if (entry.song.songid == _playid &&
|
|
||||||
entry.label != null) {
|
|
||||||
entry.label.setForeground(Color.red);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected PlaylistEntry getPlayingEntry ()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < _plist.size(); i++) {
|
|
||||||
PlaylistEntry entry = (PlaylistEntry)_plist.get(i);
|
|
||||||
if (entry.song.songid == _playid) {
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static class PlaylistEntry
|
|
||||||
{
|
|
||||||
public Entry entry;
|
|
||||||
|
|
||||||
public Song song;
|
|
||||||
|
|
||||||
public JLabel label;
|
|
||||||
|
|
||||||
public PlaylistEntry (Entry entry, Song song)
|
|
||||||
{
|
|
||||||
this.entry = entry;
|
|
||||||
this.song = song;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A panel that can be made to scroll a particular child into view
|
* A panel that can be made to scroll a particular child into view
|
||||||
* when it becomes visible.
|
* when it becomes visible.
|
||||||
@@ -402,10 +212,6 @@ public class PlaylistPanel extends JPanel
|
|||||||
protected SmartPanel _bpanel;
|
protected SmartPanel _bpanel;
|
||||||
protected JButton _clearbut;
|
protected JButton _clearbut;
|
||||||
|
|
||||||
protected ArrayList _plist = new ArrayList();
|
|
||||||
protected int _playid;
|
|
||||||
protected int _oldid = -1;
|
|
||||||
|
|
||||||
protected Font _nameFont;
|
protected Font _nameFont;
|
||||||
|
|
||||||
protected ImageIcon _skiptoIcon;
|
protected ImageIcon _skiptoIcon;
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
//
|
//
|
||||||
// $Id: QueryEntryList.java,v 1.1 2002/02/22 07:06:33 mdb Exp $
|
// $Id: QueryEntryList.java,v 1.2 2003/05/04 18:16:06 mdb Exp $
|
||||||
|
|
||||||
package robodj.chooser;
|
package robodj.chooser;
|
||||||
|
|
||||||
import com.samskivert.io.PersistenceException;
|
import com.samskivert.io.PersistenceException;
|
||||||
|
import com.samskivert.swing.Controller;
|
||||||
|
|
||||||
import robodj.repository.Entry;
|
import robodj.repository.Entry;
|
||||||
|
|
||||||
@@ -17,17 +18,18 @@ public class QueryEntryList extends EntryList
|
|||||||
_query = query;
|
_query = query;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// documentation inherited
|
||||||
* Reads in the entries for this query.
|
protected Controller createController ()
|
||||||
*/
|
|
||||||
public Entry[] readEntries ()
|
|
||||||
throws PersistenceException
|
|
||||||
{
|
{
|
||||||
|
return new EntryController(this) {
|
||||||
|
public Entry[] readEntries () throws PersistenceException {
|
||||||
if (_entries == null) {
|
if (_entries == null) {
|
||||||
_entries = Chooser.repository.matchEntries(_query);
|
_entries = Chooser.repository.matchEntries(_query);
|
||||||
}
|
}
|
||||||
return _entries;
|
return _entries;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
protected String getEmptyString ()
|
protected String getEmptyString ()
|
||||||
{
|
{
|
||||||
@@ -36,7 +38,4 @@ public class QueryEntryList extends EntryList
|
|||||||
|
|
||||||
/** The query with which we're looking up entries. */
|
/** The query with which we're looking up entries. */
|
||||||
protected String _query;
|
protected String _query;
|
||||||
|
|
||||||
/** A cached copy of our query results. */
|
|
||||||
protected Entry[] _entries;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,166 @@
|
|||||||
|
//
|
||||||
|
// $Id: SongItem.java,v 1.1 2003/05/04 18:16:06 mdb Exp $
|
||||||
|
|
||||||
|
package robodj.chooser;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
|
||||||
|
import com.samskivert.swing.HGroupLayout;
|
||||||
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
|
import robodj.repository.Song;
|
||||||
|
import robodj.util.ButtonUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a particular song (used by the playlist and the entry list).
|
||||||
|
*/
|
||||||
|
public class SongItem extends Item
|
||||||
|
{
|
||||||
|
/** Configures this song item for playlist mode. */
|
||||||
|
public static final int PLAYLIST = 0;
|
||||||
|
|
||||||
|
/** Configures this song item for browser mode. */
|
||||||
|
public static final int BROWSER = 1;
|
||||||
|
|
||||||
|
// playlist mode actions
|
||||||
|
public static final String SKIP_TO = "song:skip_to";
|
||||||
|
public static final String REMOVE = "song:remove";
|
||||||
|
|
||||||
|
// browser mode actions
|
||||||
|
public static final String PLAY = "song:play";
|
||||||
|
|
||||||
|
// ubiquitous actions
|
||||||
|
public static final String ADD_VOTE = "song:add_vote";
|
||||||
|
public static final String CLEAR_VOTE = "song:clear_vote";
|
||||||
|
|
||||||
|
/** Used depending on where this song is being displayed. */
|
||||||
|
public Object extra;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a song item configured with the supplied song and specified
|
||||||
|
* mode.
|
||||||
|
*/
|
||||||
|
public SongItem (Song song, int mode)
|
||||||
|
{
|
||||||
|
_song = song;
|
||||||
|
|
||||||
|
// configure our layout manager
|
||||||
|
HGroupLayout gl = new HGroupLayout(HGroupLayout.NONE);
|
||||||
|
gl.setJustification(HGroupLayout.LEFT);
|
||||||
|
setLayout(gl);
|
||||||
|
|
||||||
|
// create our control buttons
|
||||||
|
JButton button;
|
||||||
|
switch (mode) {
|
||||||
|
case PLAYLIST:
|
||||||
|
// add a button for skipping to this song
|
||||||
|
button = ButtonUtil.createControlButton(
|
||||||
|
SKIP_TO_TIP, SKIP_TO, _skipToIcon, true);
|
||||||
|
button.putClientProperty("song", _song);
|
||||||
|
add(button, HGroupLayout.FIXED);
|
||||||
|
|
||||||
|
// add a button for removing this song
|
||||||
|
button = ButtonUtil.createControlButton(
|
||||||
|
REMOVE_TIP, REMOVE, _removeIcon, true);
|
||||||
|
button.putClientProperty("song", _song);
|
||||||
|
add(button, HGroupLayout.FIXED);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BROWSER:
|
||||||
|
// add a button for playing the song
|
||||||
|
button = ButtonUtil.createControlButton(
|
||||||
|
PLAY_SONG_TIP, PLAY, _playIcon, true);
|
||||||
|
button.putClientProperty(SONG_PROP, _song);
|
||||||
|
add(button, HGroupLayout.FIXED);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
System.err.println("Unknown song mode: " + mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add a vote button
|
||||||
|
_voteButton = ButtonUtil.createControlButton(
|
||||||
|
ADD_VOTE_TIP, ADD_VOTE, _addVoteIcon, true);
|
||||||
|
_voteButton.putClientProperty(SONG_PROP, song);
|
||||||
|
add(_voteButton, HGroupLayout.FIXED);
|
||||||
|
|
||||||
|
// add the song title
|
||||||
|
_trackLabel = new JLabel(_song.title);
|
||||||
|
add(_trackLabel);
|
||||||
|
|
||||||
|
// update our display based on our votes
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsPlaying (boolean isPlaying)
|
||||||
|
{
|
||||||
|
_trackLabel.setForeground(isPlaying ? Color.red : Color.black);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update ()
|
||||||
|
{
|
||||||
|
if (!StringUtil.blank(_song.votes)) {
|
||||||
|
_trackLabel.setFont(_hasVotesFont);
|
||||||
|
_trackLabel.setToolTipText("<html>" + _song.title +
|
||||||
|
"<br>Votes: " + _song.votes);
|
||||||
|
} else {
|
||||||
|
_trackLabel.setFont(_nameFont);
|
||||||
|
_trackLabel.setToolTipText(_song.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_song.hasVoted(Chooser.frame.getUser(false))) {
|
||||||
|
_voteButton.setIcon(_clearVoteIcon);
|
||||||
|
_voteButton.setActionCommand(CLEAR_VOTE);
|
||||||
|
_voteButton.setToolTipText(CLEAR_VOTE_TIP);
|
||||||
|
} else {
|
||||||
|
_voteButton.setIcon(_addVoteIcon);
|
||||||
|
_voteButton.setActionCommand(ADD_VOTE);
|
||||||
|
_voteButton.setToolTipText(ADD_VOTE_TIP);
|
||||||
|
}
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Song getSong ()
|
||||||
|
{
|
||||||
|
return _song;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Song getSong (Object source)
|
||||||
|
{
|
||||||
|
return (Song)((JButton)source).getClientProperty(SONG_PROP);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Song _song;
|
||||||
|
protected JButton _voteButton;
|
||||||
|
protected JLabel _trackLabel;
|
||||||
|
|
||||||
|
protected static final String SONG_PROP = "song";
|
||||||
|
|
||||||
|
protected static final String SKIP_TO_TIP =
|
||||||
|
"Skip to this song";
|
||||||
|
protected static final String REMOVE_TIP =
|
||||||
|
"Remove this song from the playlist";
|
||||||
|
|
||||||
|
protected static final String PLAY_SONG_TIP =
|
||||||
|
"Append this song to the playlist";
|
||||||
|
|
||||||
|
protected static final String ADD_VOTE_TIP =
|
||||||
|
"Add your vote for this song";
|
||||||
|
protected static final String CLEAR_VOTE_TIP =
|
||||||
|
"Remove your vote for this song";
|
||||||
|
|
||||||
|
protected static ImageIcon _skipToIcon =
|
||||||
|
ButtonUtil.getIcon(ICON_ROOT + "skip.png");
|
||||||
|
protected static ImageIcon _removeIcon =
|
||||||
|
ButtonUtil.getIcon(ICON_ROOT + "remove_song.png");
|
||||||
|
|
||||||
|
protected static ImageIcon _addVoteIcon =
|
||||||
|
ButtonUtil.getIcon(ICON_ROOT + "add_vote.png");
|
||||||
|
protected static ImageIcon _clearVoteIcon =
|
||||||
|
ButtonUtil.getIcon(ICON_ROOT + "clear_vote.png");
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 792 B |
Binary file not shown.
|
After Width: | Height: | Size: 797 B |
@@ -0,0 +1,33 @@
|
|||||||
|
#
|
||||||
|
# $Id: config.properties,v 1.1 2003/05/04 18:16:06 mdb Exp $
|
||||||
|
#
|
||||||
|
# Default RoboDJ configuration properties
|
||||||
|
|
||||||
|
#
|
||||||
|
# The directory in which RoboDJ will store its ripped music
|
||||||
|
|
||||||
|
# repository.basedir = /export/robodj/repository
|
||||||
|
|
||||||
|
#
|
||||||
|
# The directory in which the importer will store temp files while ripping
|
||||||
|
|
||||||
|
# repository.tmpdir = /export/robodj/tmp
|
||||||
|
|
||||||
|
#
|
||||||
|
# The JDBC configuration for accessing the music database
|
||||||
|
|
||||||
|
jdbc.default.driver = org.gjt.mm.mysql.Driver
|
||||||
|
jdbc.default.username = _USERNAME_
|
||||||
|
jdbc.default.password = _PASSWORD_
|
||||||
|
jdbc.default.url = jdbc:mysql://_DBHOST_:3306/_DBNAME_
|
||||||
|
|
||||||
|
#
|
||||||
|
# The CDDB host to which to connect
|
||||||
|
|
||||||
|
cddb.host = freedb.freedb.org
|
||||||
|
|
||||||
|
#
|
||||||
|
# The music server host and port information
|
||||||
|
|
||||||
|
# musicd.host = hostname
|
||||||
|
musicd.port = 2500
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: CDDBLookupPanel.java,v 1.6 2002/11/11 17:05:20 mdb Exp $
|
// $Id: CDDBLookupPanel.java,v 1.7 2003/05/04 18:16:07 mdb Exp $
|
||||||
|
|
||||||
package robodj.importer;
|
package robodj.importer;
|
||||||
|
|
||||||
@@ -171,6 +171,7 @@ public class CDDBLookupPanel
|
|||||||
Song song = (entry.songs[i] = new Song());
|
Song song = (entry.songs[i] = new Song());
|
||||||
song.title = names[i];
|
song.title = names[i];
|
||||||
song.position = i+1;
|
song.position = i+1;
|
||||||
|
song.votes = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// create the rip panel and pass the entry and info along
|
// create the rip panel and pass the entry and info along
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// $Id: RipPanel.java,v 1.8 2002/11/11 17:05:37 mdb Exp $
|
// $Id: RipPanel.java,v 1.9 2003/05/04 18:16:07 mdb Exp $
|
||||||
|
|
||||||
package robodj.importer;
|
package robodj.importer;
|
||||||
|
|
||||||
@@ -430,8 +430,8 @@ public class RipPanel
|
|||||||
protected Entry _entry;
|
protected Entry _entry;
|
||||||
|
|
||||||
protected static final String DB_FAILURE_MSG =
|
protected static final String DB_FAILURE_MSG =
|
||||||
"An error occurred while communicating with the database. You " +
|
"An error occurred while communicating with the database.\n" +
|
||||||
"may wish to examine the following error message, remedy the " +
|
"You may wish to examine the following error message, remedy the\n" +
|
||||||
"problem with the database and retry the operation. Or you can " +
|
"problem with the database and retry the operation. Or you can\n" +
|
||||||
"abort the operation and cancel the import process entirely.";
|
"abort the operation and cancel the import process entirely.";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
//
|
//
|
||||||
// $Id: Song.java,v 1.1 2000/11/08 06:42:57 mdb Exp $
|
// $Id: Song.java,v 1.2 2003/05/04 18:16:07 mdb Exp $
|
||||||
|
|
||||||
package robodj.repository;
|
package robodj.repository;
|
||||||
|
|
||||||
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A song maps approximately to an individual piece of music. In most
|
* A song maps approximately to an individual piece of music. In most
|
||||||
* cases repository entries are created from albums which contain some
|
* cases repository entries are created from albums which contain some
|
||||||
@@ -38,10 +40,103 @@ public class Song
|
|||||||
*/
|
*/
|
||||||
public int duration;
|
public int duration;
|
||||||
|
|
||||||
|
/** A comma separated list of the "users" that have voted for this
|
||||||
|
* song where "users" is the short string (initials) provided by a
|
||||||
|
* client when voting for a song. */
|
||||||
|
public String votes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a voter to the votes string for this entry, not including a
|
||||||
|
* voter that already exists in the voter set.
|
||||||
|
*
|
||||||
|
* @return true if the voter set was changed as a result of this
|
||||||
|
* addition, false if not.
|
||||||
|
*/
|
||||||
|
public boolean addVote (String voter)
|
||||||
|
{
|
||||||
|
// handle our first vote separately
|
||||||
|
if (StringUtil.blank(votes)) {
|
||||||
|
votes = voter;
|
||||||
|
_votes = null; // clear our cached votes
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure this voter has not already cast their ballot
|
||||||
|
if (hasVoted(voter)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
votes = (votes + "," + voter);
|
||||||
|
_votes = null; // clear our cached votes
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a voter from the votes string for this song.
|
||||||
|
*
|
||||||
|
* @return true if the voter set was changed as a result of this
|
||||||
|
* addition, false if not.
|
||||||
|
*/
|
||||||
|
public boolean clearVote (String voter)
|
||||||
|
{
|
||||||
|
String[] votes = getVotes();
|
||||||
|
StringBuffer nvotes = new StringBuffer();
|
||||||
|
boolean saw = false;
|
||||||
|
for (int ii = 0; ii < votes.length; ii++) {
|
||||||
|
if (votes[ii].equalsIgnoreCase(voter)) {
|
||||||
|
saw = true;
|
||||||
|
} else {
|
||||||
|
if (nvotes.length() > 0) {
|
||||||
|
nvotes.append(",");
|
||||||
|
}
|
||||||
|
nvotes.append(votes[ii]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (saw) {
|
||||||
|
this.votes = nvotes.toString();
|
||||||
|
_votes = null;
|
||||||
|
}
|
||||||
|
return saw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link #votes} field broken up into an array of strings
|
||||||
|
* with one for each "vote".
|
||||||
|
*/
|
||||||
|
public String[] getVotes ()
|
||||||
|
{
|
||||||
|
if (_votes == null) {
|
||||||
|
if (StringUtil.blank(votes)) {
|
||||||
|
_votes = new String[0];
|
||||||
|
} else {
|
||||||
|
_votes = StringUtil.split(votes, ",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _votes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the specified user has voted.
|
||||||
|
*/
|
||||||
|
public boolean hasVoted (String voter)
|
||||||
|
{
|
||||||
|
String[] votes = getVotes();
|
||||||
|
for (int ii = 0; ii < votes.length; ii++) {
|
||||||
|
if (votes[ii].equalsIgnoreCase(voter)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of this instance.
|
||||||
|
*/
|
||||||
public String toString ()
|
public String toString ()
|
||||||
{
|
{
|
||||||
return "[songid=" + songid + ", entryid=" + entryid +
|
return StringUtil.fieldsToString(this);
|
||||||
", position=" + position + ", title=" + title +
|
|
||||||
", location=" + location + ", duration=" + duration + "]";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The decoded voter array for this song. */
|
||||||
|
protected transient String[] _votes;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
//
|
//
|
||||||
// $Id: ButtonUtil.java,v 1.1 2002/03/03 20:56:12 mdb Exp $
|
// $Id: ButtonUtil.java,v 1.2 2003/05/04 18:16:07 mdb Exp $
|
||||||
|
|
||||||
package robodj.util;
|
package robodj.util;
|
||||||
|
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -15,6 +14,8 @@ import javax.swing.BorderFactory;
|
|||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
|
|
||||||
|
import com.samskivert.swing.Controller;
|
||||||
|
|
||||||
import robodj.Log;
|
import robodj.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,32 +44,30 @@ public class ButtonUtil
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static JButton createControlButton (
|
public static JButton createControlButton (
|
||||||
String tooltip, String action, String iconPath, ActionListener al)
|
String tooltip, String action, String iconPath)
|
||||||
{
|
{
|
||||||
return createControlButton(tooltip, action, iconPath, al, false);
|
return createControlButton(tooltip, action, iconPath, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JButton createControlButton (
|
public static JButton createControlButton (
|
||||||
String tooltip, String action, String iconPath,
|
String tooltip, String action, String iconPath, boolean borderless)
|
||||||
ActionListener al, boolean borderless)
|
|
||||||
{
|
{
|
||||||
return createControlButton(
|
return createControlButton(
|
||||||
tooltip, action, getIcon(iconPath), al, borderless);
|
tooltip, action, getIcon(iconPath), borderless);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JButton createControlButton (
|
public static JButton createControlButton (
|
||||||
String tooltip, String action, ImageIcon icon, ActionListener al)
|
String tooltip, String action, ImageIcon icon)
|
||||||
{
|
{
|
||||||
return createControlButton(tooltip, action, icon, al, false);
|
return createControlButton(tooltip, action, icon, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static JButton createControlButton (
|
public static JButton createControlButton (
|
||||||
String tooltip, String action, ImageIcon icon,
|
String tooltip, String action, ImageIcon icon, boolean borderless)
|
||||||
ActionListener al, boolean borderless)
|
|
||||||
{
|
{
|
||||||
JButton cbut = new JButton(icon);
|
JButton cbut = new JButton(icon);
|
||||||
cbut.setActionCommand(action);
|
cbut.setActionCommand(action);
|
||||||
cbut.addActionListener(al);
|
cbut.addActionListener(Controller.DISPATCHER);
|
||||||
cbut.setToolTipText(tooltip);
|
cbut.setToolTipText(tooltip);
|
||||||
// clear out that annoying fat border that swing uses
|
// clear out that annoying fat border that swing uses
|
||||||
cbut.setBorder(borderless ? BorderFactory.createEmptyBorder() :
|
cbut.setBorder(borderless ? BorderFactory.createEmptyBorder() :
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
//
|
||||||
|
// $Id: RDJPrefs.java,v 1.1 2003/05/04 18:16:07 mdb Exp $
|
||||||
|
|
||||||
|
package robodj.util;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import com.samskivert.util.Config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains settings and preferences for this RoboDJ installation.
|
||||||
|
*/
|
||||||
|
public class RDJPrefs
|
||||||
|
{
|
||||||
|
/** Configuration key for {@link #getRepositoryDirectory}. */
|
||||||
|
public static final String REPO_DIR_KEY = "repository.basedir";
|
||||||
|
|
||||||
|
/** Configuration key for {@link #getJDBCConfig}. */
|
||||||
|
public static final String JDBC_DRIVER_KEY = "jdbc.default.driver";
|
||||||
|
|
||||||
|
/** Configuration key for {@link #getJDBCConfig}. */
|
||||||
|
public static final String JDBC_USERNAME_KEY = "jdbc.default.username";
|
||||||
|
|
||||||
|
/** Configuration key for {@link #getJDBCConfig}. */
|
||||||
|
public static final String JDBC_PASSWORD_KEY = "jdbc.default.password";
|
||||||
|
|
||||||
|
/** Configuration key for {@link #getJDBCConfig}. */
|
||||||
|
public static final String JDBC_URL_KEY = "jdbc.default.url";
|
||||||
|
|
||||||
|
/** Configuration key for {@link #getMusicDaemonHost}. */
|
||||||
|
public static final String MUSICD_HOST_KEY = "musicd.host";
|
||||||
|
|
||||||
|
/** Configuration key for {@link #getMusicDaemonPort}. */
|
||||||
|
public static final String MUSICD_PORT_KEY = "musicd.port";
|
||||||
|
|
||||||
|
/** Configuration key for {@link #getCDDBHost}. */
|
||||||
|
public static final String CDDB_HOST_KEY = "cddb.host";
|
||||||
|
|
||||||
|
/** Configuration key for {@link #getUser}. */
|
||||||
|
public static final String USER_KEY = "user";
|
||||||
|
|
||||||
|
/** Provides access to configuration data. */
|
||||||
|
public static Config config = new Config("robodj/config");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the repository directory name.
|
||||||
|
*/
|
||||||
|
public static String getRepositoryDirectory ()
|
||||||
|
{
|
||||||
|
return config.getValue(REPO_DIR_KEY, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the JDBC configuration for this RoboDJ installation.
|
||||||
|
*/
|
||||||
|
public static Properties getJDBCConfig ()
|
||||||
|
{
|
||||||
|
return config.getSubProperties("jdbc");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the host by which we connect to the music daemon.
|
||||||
|
*/
|
||||||
|
public static String getMusicDaemonHost ()
|
||||||
|
{
|
||||||
|
return config.getValue(MUSICD_HOST_KEY, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the port by which we connect to the music daemon.
|
||||||
|
*/
|
||||||
|
public static int getMusicDaemonPort ()
|
||||||
|
{
|
||||||
|
return config.getValue(MUSICD_PORT_KEY, 2500);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hostname of the CDDB server we use to look up album and
|
||||||
|
* track names.
|
||||||
|
*/
|
||||||
|
public static String getCDDBHost ()
|
||||||
|
{
|
||||||
|
return config.getValue(CDDB_HOST_KEY, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns our locally configured username.
|
||||||
|
*/
|
||||||
|
public static String getUser ()
|
||||||
|
{
|
||||||
|
return config.getValue(USER_KEY, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
//
|
||||||
|
// $Id: RDJPrefsPanel.java,v 1.1 2003/05/04 18:16:07 mdb Exp $
|
||||||
|
|
||||||
|
package robodj.util;
|
||||||
|
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.GridBagConstraints;
|
||||||
|
import java.awt.GridBagLayout;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.FocusEvent;
|
||||||
|
import java.awt.event.FocusListener;
|
||||||
|
import java.awt.event.WindowAdapter;
|
||||||
|
import java.awt.event.WindowEvent;
|
||||||
|
|
||||||
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
|
||||||
|
import com.samskivert.swing.Spacer;
|
||||||
|
import com.samskivert.swing.util.SwingUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays an interface for editing our RoboDJ preferences.
|
||||||
|
*/
|
||||||
|
public class RDJPrefsPanel extends JPanel
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Displays a frame containing a panel for editing our preferences.
|
||||||
|
*
|
||||||
|
* @param blockCaller if true the caller will be blocked until the
|
||||||
|
* user dismisses the preferences panel.
|
||||||
|
*/
|
||||||
|
public static void display (boolean blockCaller)
|
||||||
|
{
|
||||||
|
JFrame frame = new JFrame("RoboDJ Configuration");
|
||||||
|
RDJPrefsPanel panel = new RDJPrefsPanel(frame);
|
||||||
|
frame.setContentPane(panel);
|
||||||
|
frame.pack();
|
||||||
|
SwingUtil.centerWindow(frame);
|
||||||
|
frame.show();
|
||||||
|
|
||||||
|
if (blockCaller) {
|
||||||
|
panel.awaitDismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected RDJPrefsPanel (JFrame frame)
|
||||||
|
{
|
||||||
|
super(new GridBagLayout());
|
||||||
|
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||||
|
|
||||||
|
_frame = frame;
|
||||||
|
_frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
||||||
|
_frame.addWindowListener(new WindowAdapter() {
|
||||||
|
public void windowClosed (WindowEvent event) {
|
||||||
|
dismissed();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
|
c.anchor = GridBagConstraints.WEST;
|
||||||
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
c.weighty = 1;
|
||||||
|
|
||||||
|
c.gridwidth = GridBagConstraints.REMAINDER;
|
||||||
|
JLabel title = new JLabel("RoboDJ Preferences");
|
||||||
|
title.setHorizontalAlignment(JLabel.CENTER);
|
||||||
|
title.setFont(title.getFont().deriveFont(16f));
|
||||||
|
add(title, c);
|
||||||
|
add(new Spacer(10, 10), c);
|
||||||
|
|
||||||
|
for (int ii = 0; ii < PREFS.length; ii += 2) {
|
||||||
|
if (PREFS[ii].equals("")) {
|
||||||
|
c.gridwidth = GridBagConstraints.REMAINDER;
|
||||||
|
add(new Spacer(10, 10), c);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
JLabel label = new JLabel(PREFS[ii]);
|
||||||
|
c.gridwidth = GridBagConstraints.RELATIVE;
|
||||||
|
c.ipadx = 5;
|
||||||
|
c.ipady = 8;
|
||||||
|
c.weightx = 1;
|
||||||
|
add(label, c);
|
||||||
|
|
||||||
|
JTextField value = new JTextField(
|
||||||
|
RDJPrefs.config.getValue(PREFS[ii+1], "")) {
|
||||||
|
public Dimension getPreferredSize () {
|
||||||
|
Dimension d = super.getPreferredSize();
|
||||||
|
d.width = Math.min(250, d.width);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
PrefListener plist = new PrefListener(value, PREFS[ii+1]);
|
||||||
|
value.addFocusListener(plist);
|
||||||
|
value.addActionListener(plist);
|
||||||
|
c.ipadx = 0;
|
||||||
|
c.ipady = 2;
|
||||||
|
c.weightx = 2;
|
||||||
|
c.gridwidth = GridBagConstraints.REMAINDER;
|
||||||
|
add(value, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add a dismiss button
|
||||||
|
JButton dismiss = new JButton("Dismiss");
|
||||||
|
dismiss.addActionListener(new ActionListener() {
|
||||||
|
public void actionPerformed (ActionEvent event) {
|
||||||
|
_frame.dispose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
c.fill = GridBagConstraints.NONE;
|
||||||
|
c.anchor = GridBagConstraints.EAST;
|
||||||
|
c.ipadx = c.ipady = 0;
|
||||||
|
add(new Spacer(10, 10), c);
|
||||||
|
add(dismiss, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized void awaitDismiss ()
|
||||||
|
{
|
||||||
|
while (_frame.isShowing()) {
|
||||||
|
try {
|
||||||
|
wait();
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized void dismissed ()
|
||||||
|
{
|
||||||
|
notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class PrefListener
|
||||||
|
implements ActionListener, FocusListener
|
||||||
|
{
|
||||||
|
public PrefListener (JTextField text, String prefKey)
|
||||||
|
{
|
||||||
|
_text = text;
|
||||||
|
_prefKey = prefKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface FocusListener
|
||||||
|
public void focusGained (FocusEvent e)
|
||||||
|
{
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface FocusListener
|
||||||
|
public void focusLost (FocusEvent e)
|
||||||
|
{
|
||||||
|
actionPerformed(new ActionEvent(_text, 0, "focusLost"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// documentation inherited from interface ActionListener
|
||||||
|
public void actionPerformed (ActionEvent e)
|
||||||
|
{
|
||||||
|
RDJPrefs.config.setValue(_prefKey, _text.getText());
|
||||||
|
System.out.println("Updated " + _prefKey + " -> " +
|
||||||
|
_text.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JTextField _text;
|
||||||
|
protected String _prefKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected JFrame _frame;
|
||||||
|
|
||||||
|
/** Defines our configurable preferences. */
|
||||||
|
protected static final String[] PREFS = {
|
||||||
|
"Music repository directory", RDJPrefs.REPO_DIR_KEY,
|
||||||
|
"", "",
|
||||||
|
"Music database JDBC driver", RDJPrefs.JDBC_DRIVER_KEY,
|
||||||
|
"Music database JDBC url", RDJPrefs.JDBC_URL_KEY,
|
||||||
|
"Music database JDBC username", RDJPrefs.JDBC_USERNAME_KEY,
|
||||||
|
"Music database JDBC password", RDJPrefs.JDBC_PASSWORD_KEY,
|
||||||
|
"", "",
|
||||||
|
"Music server hostname", RDJPrefs.MUSICD_HOST_KEY,
|
||||||
|
"Music server port", RDJPrefs.MUSICD_PORT_KEY,
|
||||||
|
"", "",
|
||||||
|
"CDDB server hostname", RDJPrefs.CDDB_HOST_KEY,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* $Id: create_repository.mysql,v 1.1 2000/12/10 07:00:47 mdb Exp $
|
* $Id: create_repository.mysql,v 1.2 2003/05/04 18:16:07 mdb Exp $
|
||||||
*
|
*
|
||||||
* Creates the necessary database tables in MySQL for the music repository.
|
* Creates the necessary database tables in MySQL for the music repository.
|
||||||
*/
|
*/
|
||||||
@@ -27,6 +27,7 @@ CREATE TABLE songs (
|
|||||||
title VARCHAR(200) NOT NULL,
|
title VARCHAR(200) NOT NULL,
|
||||||
location VARCHAR(200) NOT NULL,
|
location VARCHAR(200) NOT NULL,
|
||||||
duration INTEGER(10) NOT NULL,
|
duration INTEGER(10) NOT NULL,
|
||||||
|
votes VARCHAR(255) NOT NULL,
|
||||||
|
|
||||||
CONSTRAINT fk_songs_entries FOREIGN KEY (entryid)
|
CONSTRAINT fk_songs_entries FOREIGN KEY (entryid)
|
||||||
REFERENCES entries (entryid) ON DELETE CASCADE
|
REFERENCES entries (entryid) ON DELETE CASCADE
|
||||||
|
|||||||
Reference in New Issue
Block a user