diff --git a/projects/robodj/src/java/robodj/chooser/BrowsePanel.java b/projects/robodj/src/java/robodj/chooser/BrowsePanel.java
index 5de43ca1..c2266ac1 100644
--- a/projects/robodj/src/java/robodj/chooser/BrowsePanel.java
+++ b/projects/robodj/src/java/robodj/chooser/BrowsePanel.java
@@ -1,14 +1,10 @@
//
-// $Id: BrowsePanel.java,v 1.1 2001/07/12 23:06:55 mdb Exp $
+// $Id: BrowsePanel.java,v 1.2 2002/02/22 07:06:33 mdb Exp $
package robodj.chooser;
-// import java.awt.*;
-// import java.awt.event.ActionEvent;
-// import java.awt.event.ActionListener;
import javax.swing.*;
-// import com.samskivert.swing.*;
import robodj.repository.*;
public class BrowsePanel extends JTabbedPane
@@ -20,13 +16,13 @@ public class BrowsePanel extends JTabbedPane
// create a tab for each category
for (int i = 0; i < cats.length; i++) {
- elist = new EntryList(cats[i].categoryid);
+ elist = new CategoryEntryList(cats[i].categoryid);
String tip = "Browse entries in '" + cats[i].name + "' category.";
addTab(cats[i].name, null, elist, tip);
}
// and add one for uncategorized entries
- elist = new EntryList(-1);
+ elist = new CategoryEntryList(-1);
addTab("Uncategorized", null, elist,
"Browse uncategorized entries.");
diff --git a/projects/robodj/src/java/robodj/chooser/CategoryEntryList.java b/projects/robodj/src/java/robodj/chooser/CategoryEntryList.java
new file mode 100644
index 00000000..f008a806
--- /dev/null
+++ b/projects/robodj/src/java/robodj/chooser/CategoryEntryList.java
@@ -0,0 +1,36 @@
+//
+// $Id: CategoryEntryList.java,v 1.1 2002/02/22 07:06:33 mdb Exp $
+
+package robodj.chooser;
+
+import com.samskivert.io.PersistenceException;
+
+import robodj.repository.Entry;
+
+public class CategoryEntryList extends EntryList
+{
+ /**
+ * Constructs an entry list for entries in a particular category.
+ */
+ public CategoryEntryList (int categoryId)
+ {
+ _categoryId = categoryId;
+ }
+
+ /**
+ * Reads in the entries for this category.
+ */
+ public Entry[] readEntries ()
+ throws PersistenceException
+ {
+ return Chooser.model.getEntries(_categoryId);
+ }
+
+ protected String getEmptyString ()
+ {
+ return "No entries in this category.";
+ }
+
+ /** The unique identifier of the category we are displaying. */
+ protected int _categoryId;
+}
diff --git a/projects/robodj/src/java/robodj/chooser/ChooserFrame.java b/projects/robodj/src/java/robodj/chooser/ChooserFrame.java
index 2bc23d68..22494b75 100644
--- a/projects/robodj/src/java/robodj/chooser/ChooserFrame.java
+++ b/projects/robodj/src/java/robodj/chooser/ChooserFrame.java
@@ -1,5 +1,5 @@
//
-// $Id: ChooserFrame.java,v 1.5 2001/07/13 00:11:05 mdb Exp $
+// $Id: ChooserFrame.java,v 1.6 2002/02/22 07:06:33 mdb Exp $
package robodj.chooser;
@@ -40,6 +40,10 @@ public class ChooserFrame
BrowsePanel bpanel = new BrowsePanel();
tip = "Browse and select tunes to play.";
tpane.addTab("Browse", null, bpanel, tip);
+
+ SearchPanel spanel = new SearchPanel();
+ tip = "Search titles and artists.";
+ tpane.addTab("Search", null, spanel, tip);
top.add(tpane);
// the bottom is the control bar
diff --git a/projects/robodj/src/java/robodj/chooser/EntryList.java b/projects/robodj/src/java/robodj/chooser/EntryList.java
index d813fd9f..d9fc3982 100644
--- a/projects/robodj/src/java/robodj/chooser/EntryList.java
+++ b/projects/robodj/src/java/robodj/chooser/EntryList.java
@@ -1,5 +1,5 @@
//
-// $Id: EntryList.java,v 1.9 2001/10/12 18:21:27 mdb Exp $
+// $Id: EntryList.java,v 1.10 2002/02/22 07:06:33 mdb Exp $
package robodj.chooser;
@@ -17,11 +17,10 @@ import com.samskivert.swing.util.*;
import robodj.Log;
import robodj.repository.*;
-public class EntryList
- extends JPanel
+public abstract class EntryList extends JPanel
implements TaskObserver, ActionListener, AncestorListener
{
- public EntryList (int categoryid)
+ public EntryList ()
{
GroupLayout gl = new VGroupLayout(GroupLayout.STRETCH);
gl.setOffAxisPolicy(GroupLayout.STRETCH);
@@ -48,10 +47,6 @@ public class EntryList
// use a special font for our name buttons
_nameFont = new Font("Helvetica", Font.PLAIN, 10);
- // keep track of the query that we'll use to populate our entries
- // display
- _catid = categoryid;
-
// listen to ancestor events
addAncestorListener(this);
}
@@ -90,13 +85,16 @@ public class EntryList
}
/**
- * Reads the entries from the database.
+ * 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 Entry[] readEntries ()
- throws PersistenceException
- {
- return Chooser.model.getEntries(_catid);
- }
+ public abstract Entry[] readEntries ()
+ throws PersistenceException;
+
+ /** The string to display when there are no matching results. */
+ protected abstract String getEmptyString ();
/**
* Reads the entries from the database.
@@ -190,7 +188,7 @@ public class EntryList
// if there were no entries, stick a label in to that effect
if (_entries.length == 0) {
- _bpanel.add(new JLabel("No entries in this category."));
+ _bpanel.add(new JLabel(getEmptyString()));
}
// reset our scroll position so that we're displaying the top of
@@ -205,6 +203,7 @@ public class EntryList
// we've removed and added components so we need to revalidate
revalidate();
+ repaint();
}
protected void populateSong (Entry entry)
@@ -356,7 +355,6 @@ public class EntryList
protected JPanel _bpanel;
protected JButton _upbut;
- protected int _catid;
protected Entry[] _entries;
protected Entry _entry;
diff --git a/projects/robodj/src/java/robodj/chooser/QueryEntryList.java b/projects/robodj/src/java/robodj/chooser/QueryEntryList.java
new file mode 100644
index 00000000..1c939bad
--- /dev/null
+++ b/projects/robodj/src/java/robodj/chooser/QueryEntryList.java
@@ -0,0 +1,42 @@
+//
+// $Id: QueryEntryList.java,v 1.1 2002/02/22 07:06:33 mdb Exp $
+
+package robodj.chooser;
+
+import com.samskivert.io.PersistenceException;
+
+import robodj.repository.Entry;
+
+public class QueryEntryList extends EntryList
+{
+ /**
+ * Constructs an entry list to display entries that match a query.
+ */
+ public QueryEntryList (String query)
+ {
+ _query = query;
+ }
+
+ /**
+ * Reads in the entries for this query.
+ */
+ public Entry[] readEntries ()
+ throws PersistenceException
+ {
+ if (_entries == null) {
+ _entries = Chooser.repository.matchEntries(_query);
+ }
+ return _entries;
+ }
+
+ protected String getEmptyString ()
+ {
+ return "No matches to your query.";
+ }
+
+ /** The query with which we're looking up entries. */
+ protected String _query;
+
+ /** A cached copy of our query results. */
+ protected Entry[] _entries;
+}
diff --git a/projects/robodj/src/java/robodj/chooser/SearchPanel.java b/projects/robodj/src/java/robodj/chooser/SearchPanel.java
new file mode 100644
index 00000000..e45c19bd
--- /dev/null
+++ b/projects/robodj/src/java/robodj/chooser/SearchPanel.java
@@ -0,0 +1,74 @@
+//
+// $Id: SearchPanel.java,v 1.1 2002/02/22 07:06:33 mdb Exp $
+
+package robodj.chooser;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.*;
+
+import com.samskivert.swing.*;
+import com.samskivert.util.StringUtil;
+
+import robodj.repository.*;
+
+public class SearchPanel extends JPanel
+ implements ActionListener
+{
+ public SearchPanel ()
+ {
+ GroupLayout gl = new VGroupLayout(GroupLayout.STRETCH);
+ gl.setOffAxisPolicy(GroupLayout.STRETCH);
+ gl.setJustification(GroupLayout.TOP);
+ setLayout(gl);
+
+ add(new JLabel("Enter a word to be matched against the " +
+ "artists, titles and songs:"), GroupLayout.FIXED);
+
+ gl = new HGroupLayout(GroupLayout.STRETCH);
+ JPanel qpanel = new JPanel(gl);
+ _qbox = new JTextField();
+ _qbox.addActionListener(this);
+ _qbox.setActionCommand("query");
+ qpanel.add(_qbox);
+
+ JButton qbutton = new JButton("Query");
+ qbutton.addActionListener(this);
+ qbutton.setActionCommand("query");
+ qpanel.add(qbutton, GroupLayout.FIXED);
+ add(qpanel, GroupLayout.FIXED);
+ }
+
+ public void actionPerformed (ActionEvent e)
+ {
+ if (e.getActionCommand().equals("query")) {
+ String text = _qbox.getText();
+ if (StringUtil.blank(text) || text.equals(_query)) {
+ // ignore empty text or repeat queries
+ return;
+ }
+
+ // make a note of the query
+ _query = text;
+
+ // remove any previous query results
+ if (_qlist != null) {
+ remove(_qlist);
+ _qlist = null;
+ }
+
+ // create and add a query entry list for the query
+ _qlist = new QueryEntryList(_query);
+ add(_qlist);
+ }
+ }
+
+ /** The most recent query. */
+ protected String _query;
+
+ /** The query text box. */
+ protected JTextField _qbox;
+
+ /** The entry list that displays the results of our query. */
+ protected QueryEntryList _qlist;
+}
diff --git a/projects/robodj/src/java/robodj/repository/Entry.java b/projects/robodj/src/java/robodj/repository/Entry.java
index dd1288d7..7150ddb3 100644
--- a/projects/robodj/src/java/robodj/repository/Entry.java
+++ b/projects/robodj/src/java/robodj/repository/Entry.java
@@ -1,5 +1,5 @@
//
-// $Id: Entry.java,v 1.3 2001/07/13 00:10:26 mdb Exp $
+// $Id: Entry.java,v 1.4 2002/02/22 07:06:33 mdb Exp $
package robodj.repository;
@@ -33,6 +33,18 @@ public class Entry
/** The songs associated with this entry. */
public transient Song[] songs;
+ /**
+ * Entry equality is based on entry id.
+ */
+ public boolean equals (Object other)
+ {
+ if (other instanceof Entry) {
+ return entryid == ((Entry)other).entryid;
+ } else {
+ return false;
+ }
+ }
+
/**
* Scans the songs array for the song with the specified id and
* returns it if found. Returns null otherwise.
diff --git a/projects/robodj/src/java/robodj/repository/Repository.java b/projects/robodj/src/java/robodj/repository/Repository.java
index 7667145f..772eb6b9 100644
--- a/projects/robodj/src/java/robodj/repository/Repository.java
+++ b/projects/robodj/src/java/robodj/repository/Repository.java
@@ -1,9 +1,10 @@
//
-// $Id: Repository.java,v 1.8 2001/09/21 03:09:01 mdb Exp $
+// $Id: Repository.java,v 1.9 2002/02/22 07:06:34 mdb Exp $
package robodj.repository;
import java.sql.*;
+import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
@@ -99,10 +100,10 @@ public class Repository extends JORARepository
* @param query a WHERE clause describing which entries should be
* selected (ie. 'where title like "%foo%"').
*
- * @return all entries in the table matching the query
- * parameters. Note: this member function does not populate
- * the songs arrays of the returned entries. Call
- * populateSongs on an entry by entry basis for that.
+ * @return all entries in the table matching the query parameters.
+ * Note: this member function does not populate the songs
+ * arrays of the returned entries. Call populateSongs on
+ * an entry by entry basis for that.
*/
public Entry[] getEntries (final String query)
throws PersistenceException
@@ -124,6 +125,84 @@ public class Repository extends JORARepository
});
}
+ /**
+ * Returns all entries for whom the supplied word is contained in the
+ * title or artist text or in the text of any of that entry's songs.
+ *
+ * @param match a string to be case-insensitively matched in the title
+ * or artist text.
+ *
+ * @return all entries in the table matching the query parameters.
+ * Note: this member function does not populate the songs
+ * arrays of the returned entries. Call populateSongs on
+ * an entry by entry basis for that.
+ */
+ public Entry[] matchEntries (final String match)
+ throws PersistenceException
+ {
+ return (Entry[])execute(new Operation() {
+ public Object invoke (Connection conn, DatabaseLiaison liaison)
+ throws PersistenceException, SQLException
+ {
+ System.out.println("looking up '" + match + "'.");
+
+ PreparedStatement stmt = null;
+ String query;
+
+ try {
+ // look up matching entries
+ query = "select entryid, title, artist, source " +
+ "from entries where title like ? or artist like ?";
+ stmt = conn.prepareStatement(query);
+ stmt.setString(1, "%" + match + "%");
+ stmt.setString(2, "%" + match + "%");
+
+ HashIntMap etable = new HashIntMap();
+ loadEntries(stmt, etable);
+ JDBCUtil.close(stmt);
+
+ // now look up matching songs
+ query = "select entries.entryid, entries.title, " +
+ "entries.artist, entries.source from " +
+ "songs, entries where " +
+ "songs.entryid = entries.entryid and " +
+ "songs.title like ?";
+ stmt = conn.prepareStatement(query);
+ stmt.setString(1, "%" + match + "%");
+ loadEntries(stmt, etable);
+
+ Entry[] entries = new Entry[etable.size()];
+ Iterator iter = etable.values().iterator();
+ for (int i = 0; iter.hasNext(); i++) {
+ entries[i] = (Entry)iter.next();
+ }
+ return entries;
+
+ } finally {
+ JDBCUtil.close(stmt);
+ }
+ }
+ });
+ }
+
+ /**
+ * Loads entries from the supplied query and inserts them into the
+ * supplied hash map.
+ */
+ protected void loadEntries (PreparedStatement stmt, HashIntMap entries)
+ throws SQLException
+ {
+ ResultSet rs = stmt.executeQuery();
+ while (rs.next()) {
+ Entry entry = new Entry();
+ entry.entryid = rs.getInt(1);
+ entry.title = rs.getString(2);
+ entry.artist = rs.getString(3);
+ entry.source = rs.getString(4);
+ entries.put(entry.entryid, entry);
+ }
+ }
+
/**
* Loads the songs for this entry from the database and inserts them
* into its songs array. If the songs are already loaded, this