diff --git a/projects/robodj/src/java/robodj/chooser/Chooser.java b/projects/robodj/src/java/robodj/chooser/Chooser.java index 50e403bb..f963c228 100644 --- a/projects/robodj/src/java/robodj/chooser/Chooser.java +++ b/projects/robodj/src/java/robodj/chooser/Chooser.java @@ -1,5 +1,5 @@ // -// $Id: Chooser.java,v 1.4 2001/07/21 02:04:37 shaper Exp $ +// $Id: Chooser.java,v 1.5 2001/07/26 00:24:22 mdb Exp $ package robodj.chooser; @@ -78,7 +78,7 @@ public class Chooser // the screen and show it ChooserFrame frame = new ChooserFrame(); frame.setSize(550, 500); - SwingUtil.centerFrame(frame); + SwingUtil.centerWindow(frame); frame.setVisible(true); } } diff --git a/projects/robodj/src/java/robodj/chooser/EditDialog.java b/projects/robodj/src/java/robodj/chooser/EditDialog.java new file mode 100644 index 00000000..730c5f36 --- /dev/null +++ b/projects/robodj/src/java/robodj/chooser/EditDialog.java @@ -0,0 +1,117 @@ +// +// $Id: EditDialog.java,v 1.1 2001/07/26 00:24:22 mdb Exp $ + +package robodj.chooser; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.*; + +import java.sql.SQLException; + +import com.samskivert.swing.*; +import com.samskivert.swing.util.*; + +import robodj.Log; +import robodj.repository.Entry; +import robodj.repository.EntryEditor; + +public class EditDialog + extends JDialog + implements TaskObserver, ActionListener +{ + public EditDialog (Entry entry) + { + setTitle("Edit " + entry.title); + + // keep this around for later + _entry = entry; + + // we create a top-level panel to manage everything + JPanel top = new JPanel(); + GroupLayout gl = new VGroupLayout(GroupLayout.STRETCH); + gl.setOffAxisPolicy(GroupLayout.STRETCH); + top.setLayout(gl); + + // give ourselves a wee bit of a border + top.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + + // create our entry editor + _editor = new EntryEditor(Chooser.model, entry); + top.add(_editor); + + // create some control buttons + gl = new HGroupLayout(GroupLayout.NONE); + gl.setJustification(GroupLayout.RIGHT); + JPanel btnPanel = new JPanel(gl); + btnPanel.add(createControlButton("Update", "update")); + btnPanel.add(createControlButton("Revert", "revert")); + btnPanel.add(createControlButton("Cancel", "cancel")); + top.add(btnPanel, GroupLayout.FIXED); + + getContentPane().add(top, BorderLayout.CENTER); + } + + public void actionPerformed (ActionEvent e) + { + String cmd = e.getActionCommand(); + if (cmd.equals("update")) { + // flush the edits to the entry + _editor.applyToEntry(); + // do the update in a separate task + TaskMaster.invokeMethodTask("updateEntry", this, this); + + } else if (cmd.equals("revert")) { + _editor.reset(); + + } else if (cmd.equals("cancel")) { + // get the hell out of dodge + dispose(); + + } else { + System.out.println("Unknown action event: " + cmd); + } + } + + /** + * Updates the entry in the repository. + */ + public void updateEntry () + throws SQLException + { + Chooser.model.updateEntry(_entry); + } + + public void taskCompleted (String name, Object result) + { + if (name.equals("updateEntry")) { + // we're done updating, we can go away now + dispose(); + } + } + + 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 JButton createControlButton (String label, String action) + { + JButton btn = new JButton(label); + btn.setActionCommand(action); + btn.addActionListener(this); + return btn; + } + + protected Entry _entry; + protected EntryEditor _editor; +} diff --git a/projects/robodj/src/java/robodj/chooser/EntryList.java b/projects/robodj/src/java/robodj/chooser/EntryList.java index f7f732fa..1040a99a 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.3 2001/07/12 23:06:55 mdb Exp $ +// $Id: EntryList.java,v 1.4 2001/07/26 00:24:22 mdb Exp $ package robodj.chooser; @@ -37,6 +37,8 @@ public class EntryList // put it into a scrolling pane JScrollPane bscroll = new JScrollPane(_bpanel); + bscroll.setVerticalScrollBarPolicy( + JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); add(bscroll); // add our navigation button @@ -191,6 +193,10 @@ public class EntryList if (_entries.length == 0) { _bpanel.add(new JLabel("No entries in this category.")); } + + // we've removed and added components and swing won't properly + // repaint automatically + _bpanel.repaint(); } protected void populateSong (Entry entry) @@ -205,15 +211,29 @@ public class EntryList ((GroupLayout)_bpanel.getLayout()).setOffAxisPolicy( GroupLayout.NONE); - // create a combo box for the categories + // create a combo box and accoutrements for selecting the category + JPanel catPanel = new JPanel(new HGroupLayout(GroupLayout.STRETCH)); + JLabel catLabel = new JLabel("Set category"); JComboBox catcombo = new JComboBox(ModelUtil.catBoxNames(Chooser.model)); + + // configure the combo box catcombo.addActionListener(this); catcombo.setActionCommand("categorize"); int catid = Chooser.model.getCategory(entry.entryid); int catidx = ModelUtil.getCategoryIndex(Chooser.model, catid); catcombo.setSelectedIndex(catidx+1); - _bpanel.add(catcombo); + + // create an edit button + JButton ebtn = new JButton("Edit entry"); + ebtn.setActionCommand("edit"); + ebtn.addActionListener(this); + + // wire it all up + catPanel.add(catLabel, GroupLayout.FIXED); + catPanel.add(catcombo); + catPanel.add(ebtn, GroupLayout.FIXED); + _bpanel.add(catPanel, GroupLayout.FIXED); // add a label for the title JLabel label = new JLabel(entry.title); @@ -241,6 +261,10 @@ public class EntryList button.putClientProperty("song", entry.songs[i]); _bpanel.add(button); } + + // we've removed and added components and swing won't properly + // repaint automatically + _bpanel.repaint(); } public void actionPerformed (ActionEvent e) @@ -268,6 +292,12 @@ public class EntryList // 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(); diff --git a/projects/robodj/src/java/robodj/importer/Importer.java b/projects/robodj/src/java/robodj/importer/Importer.java index 58c65cbe..444779c0 100644 --- a/projects/robodj/src/java/robodj/importer/Importer.java +++ b/projects/robodj/src/java/robodj/importer/Importer.java @@ -1,5 +1,5 @@ // -// $Id: Importer.java,v 1.5 2001/07/21 02:04:37 shaper Exp $ +// $Id: Importer.java,v 1.6 2001/07/26 00:24:22 mdb Exp $ package robodj.importer; @@ -61,7 +61,7 @@ public class Importer // center the frame in the screen and show it frame.setSize(640, 480); - SwingUtil.centerFrame(frame); + SwingUtil.centerWindow(frame); frame.setVisible(true); } } diff --git a/projects/robodj/src/java/robodj/importer/ImporterFrame.java b/projects/robodj/src/java/robodj/importer/ImporterFrame.java index 5ab13b9e..9ba5299f 100644 --- a/projects/robodj/src/java/robodj/importer/ImporterFrame.java +++ b/projects/robodj/src/java/robodj/importer/ImporterFrame.java @@ -1,5 +1,5 @@ // -// $Id: ImporterFrame.java,v 1.4 2001/07/12 22:32:27 mdb Exp $ +// $Id: ImporterFrame.java,v 1.5 2001/07/26 00:24:22 mdb Exp $ package robodj.importer; @@ -68,12 +68,16 @@ public class ImporterFrame extends JFrame abutton.setActionCommand(command); abutton.addActionListener(target); _buttonPanel.add(abutton); + // swing doesn't properly repaint after adding/removing children + _buttonPanel.repaint(); return abutton; } public void clearControlButtons () { _buttonPanel.removeAll(); + // swing doesn't properly repaint after adding/removing children + _buttonPanel.repaint(); } protected ImporterPanel _panel; diff --git a/projects/robodj/src/java/robodj/repository/EntryEditor.java b/projects/robodj/src/java/robodj/repository/EntryEditor.java new file mode 100644 index 00000000..fd58414f --- /dev/null +++ b/projects/robodj/src/java/robodj/repository/EntryEditor.java @@ -0,0 +1,197 @@ +// +// $Id: EntryEditor.java,v 1.1 2001/07/26 00:24:22 mdb Exp $ + +package robodj.repository; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.event.*; + +import javax.swing.*; +import javax.swing.table.*; +import java.util.StringTokenizer; + +import com.samskivert.swing.*; +import com.samskivert.net.cddb.CDDB; + +import robodj.Log; + +public class EntryEditor + extends JPanel +{ + public EntryEditor (Model model, Entry entry) + { + GroupLayout gl = new VGroupLayout(GroupLayout.STRETCH); + gl.setOffAxisPolicy(GroupLayout.STRETCH); + setLayout(gl); + + // we'll need the table model to listen to the title and artist + // text fields + _emodel = new EntryModel(entry); + + // create the title editing components + JPanel titPanel = new JPanel(new HGroupLayout(GroupLayout.STRETCH)); + JLabel titLabel = new JLabel("Title"); + _titText = new JTextField(); + _titText.getDocument().addDocumentListener(_emodel); + _titText.getDocument().putProperty("name", "title"); + titPanel.add(titLabel, GroupLayout.FIXED); + titPanel.add(_titText); + add(titPanel, GroupLayout.FIXED); + + // create the artist editing components + JPanel artPanel = new JPanel(new HGroupLayout(GroupLayout.STRETCH)); + JLabel artLabel = new JLabel("Artist"); + _artText = new JTextField(); + _artText.getDocument().addDocumentListener(_emodel); + _artText.getDocument().putProperty("name", "artist"); + artPanel.add(artLabel, GroupLayout.FIXED); + artPanel.add(_artText); + add(artPanel, GroupLayout.FIXED); + + // create the track table + _trackTable = new JTable(_emodel); + TableColumn tcol = _trackTable.getColumnModel().getColumn(0); + // i wish this fucking worked + // tcol.sizeWidthToFit(); + // then i wouldn't have do this nasty hack + tcol.setMaxWidth(20); + + // make something for it to scroll around in + JScrollPane tscroll = new JScrollPane(_trackTable); + add(tscroll); + + JPanel butPanel = new JPanel(new HGroupLayout(GroupLayout.NONE)); + JButton fixCaseBtn = new JButton("Canonicalize text"); + fixCaseBtn.addActionListener(new ActionListener () { + public void actionPerformed (ActionEvent ae) { + fixCase(); + } + }); + butPanel.add(fixCaseBtn); + add(butPanel, GroupLayout.FIXED); + + // reset the editor which will read in the values from the model + reset(); + } + + /** + * Applies the edits made in the editor to the entry. + */ + public void applyToEntry () + { + flushEdits(); + _emodel.flushToEntry(); + } + + protected void flushEdits () + { + // commit any uncommitted edits the user might have in progress + TableCellEditor editor = _trackTable.getCellEditor(); + if (editor != null) { + editor.stopCellEditing(); + } + } + + /** + * Cleans up the case of the track, title and artist text. This + * currently means capitalizing every word and performing some case + * fixups according to an exception list. + */ + public void fixCase () + { + // clean up the title + String title = fixCase(_emodel.getTitle()); + _emodel.setTitle(title); + _titText.setText(title); + + // clean up the artist + String artist = fixCase(_emodel.getArtist()); + _emodel.setArtist(artist); + _artText.setText(artist); + + // clean up the tracks + flushEdits(); + String[] names = _emodel.getTrackNames(); + for (int i = 0; i < names.length; i++) { + _emodel.setTrackName(i, fixCase(names[i])); + } + } + + /** + * Fixes the case of the supplied string according to our case fixing + * rules. + */ + protected String fixCase (String text) + { + StringTokenizer tok = new StringTokenizer(text); + StringBuffer ntbuf = new StringBuffer(); + String excep; + + for (int i = 0; tok.hasMoreTokens(); i++) { + String token = tok.nextToken(); + if (i > 0) { + ntbuf.append(" "); + } + + if ((excep = getException(token)) != null) { + // if this token should be overridden explicitly, do so + ntbuf.append(excep); + + } else if (Character.isLowerCase(token.charAt(0))) { + // if the first character is lower case, fix it + ntbuf.append(Character.toUpperCase(token.charAt(0))); + if (token.length() > 1) { + ntbuf.append(token.substring(1)); + } + + } else { + // otherwise don't mess with it + ntbuf.append(token); + } + } + + return ntbuf.toString(); + } + + /** + * Returns the exception string for this token if there is one + * otherwise null. + */ + protected String getException (String token) + { + // we could toLowerCase() the token and keep all this stuff in a + // hashtable, but the list is small and we'd rather not create a + // zillion objects with all those toLowerCase() calls, so we just + // search the exception list linearly + for (int i = 0; i < OVERRIDE_KEYS.length; i++) { + if (token.equalsIgnoreCase(OVERRIDE_KEYS[i])) { + return OVERRIDE_VALS[i]; + } + } + return null; + } + + public void reset () + { + flushEdits(); + _emodel.reset(); + _titText.setText(_emodel.getTitle()); + _artText.setText(_emodel.getArtist()); + } + + protected JTextField _titText; + protected JTextField _artText; + protected JTable _trackTable; + + protected EntryModel _emodel; + protected int _newcatid; + + // used in case fixups + protected static String[] OVERRIDE_KEYS = { + "vs.", "dj", "/" + }; + protected static String[] OVERRIDE_VALS = { + "vs.", "DJ", "-" + }; +} diff --git a/projects/robodj/src/java/robodj/repository/EntryModel.java b/projects/robodj/src/java/robodj/repository/EntryModel.java new file mode 100644 index 00000000..3363e77c --- /dev/null +++ b/projects/robodj/src/java/robodj/repository/EntryModel.java @@ -0,0 +1,186 @@ +// +// $Id: EntryModel.java,v 1.1 2001/07/26 00:24:22 mdb Exp $ + +package robodj.repository; + +import javax.swing.JTextField; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.table.AbstractTableModel; +import javax.swing.text.Document; +import javax.swing.text.BadLocationException; + +import robodj.Log; + +public class EntryModel + extends AbstractTableModel + implements DocumentListener +{ + public EntryModel (Entry entry) + { + _entry = entry; + _names = new String[entry.songs.length]; + parseEntry(); + } + + /** + * Resets the model from the information in the entry. + */ + public void reset () + { + // reread our data + parseEntry(); + // fire a data changed event to let the table know what's up + fireTableDataChanged(); + } + + /** + * Extracts the info from the entry, overwriting any local + * modifications made to the model. + */ + protected void parseEntry () + { + _title = _entry.title; + _artist = _entry.artist; + + for (int i = 0; i < _names.length; i++) { + _names[i] = _entry.songs[i].title; + } + } + + /** + * Updates the entry with the values stored in the model. + */ + public void flushToEntry () + { + _entry.title = _title; + _entry.artist = _artist; + + for (int i = 0; i < _names.length; i++) { + _entry.songs[i].title = _names[i]; + } + } + + public String getTitle () + { + return _title; + } + + public String getArtist () + { + return _artist; + } + + // this does not cause the UI displaying the title to update because + // it isn't listening to the model, we're only listening to it + public void setTitle (String title) + { + _title = title; + } + + // this does not cause the UI displaying the artist to update because + // it isn't listening to the model, we're only listening to it + public void setArtist (String artist) + { + _artist = artist; + } + + // this does cause the track display to update + public void setTrackName (int index, String name) + { + _names[index] = name; + fireTableCellUpdated(index, TITLE_COLUMN); + } + + public String[] getTrackNames () + { + return _names; + } + + public void insertUpdate (DocumentEvent e) + { + updateText(e.getDocument()); + } + + public void removeUpdate (DocumentEvent e) + { + updateText(e.getDocument()); + } + + public void changedUpdate (DocumentEvent e) + { + updateText(e.getDocument()); + } + + protected void updateText (Document doc) + { + try { + if (doc.getProperty("name").equals("title")) { + _title = doc.getText(0, doc.getLength()); + } else if (doc.getProperty("name").equals("artist")) { + _artist = doc.getText(0, doc.getLength()); + } + + } catch (BadLocationException ble) { + Log.warning("Can't extract text from text field?!"); + Log.logStackTrace(ble); + } + } + + public int getRowCount () + { + return _names.length; + } + + public int getColumnCount () + { + return COLUMN_NAMES.length; + } + + public Object getValueAt (int rowIndex, int columnIndex) + { + switch (columnIndex) { + case TITLE_COLUMN: + return _names[rowIndex]; + + default: + case 0: + return new Integer(rowIndex+1); + } + } + + public String getColumnName (int column) + { + return COLUMN_NAMES[column]; + } + + public Class getColumnClass (int column) + { + return COLUMN_CLASSES[column]; + } + + public boolean isCellEditable (int row, int column) + { + return column == TITLE_COLUMN; + } + + public void setValueAt (Object value, int row, int column) + { + if (column == TITLE_COLUMN) { + _names[row] = (String)value; + fireTableCellUpdated(row, column); + } + } + + protected Entry _entry; + + protected String _title; + protected String _artist; + protected String[] _names; + + protected static final String[] COLUMN_NAMES = { "T#", "Title" }; + protected static final Class[] COLUMN_CLASSES = { + Integer.class, String.class }; + + protected static final int TITLE_COLUMN = 1; +} diff --git a/projects/robodj/src/java/robodj/repository/Model.java b/projects/robodj/src/java/robodj/repository/Model.java index 5eeeabf2..59fe3971 100644 --- a/projects/robodj/src/java/robodj/repository/Model.java +++ b/projects/robodj/src/java/robodj/repository/Model.java @@ -1,5 +1,5 @@ // -// $Id: Model.java,v 1.1 2001/06/07 08:37:47 mdb Exp $ +// $Id: Model.java,v 1.2 2001/07/26 00:24:22 mdb Exp $ package robodj.repository; @@ -97,6 +97,17 @@ public class Model return ents; } + /** + * Updates this entry in the repository. The structure of the entry + * should not have changed (number of songs), but the contents (title, + * artist, names) may have changed. + */ + public void updateEntry (Entry entry) + throws SQLException + { + _rep.updateEntry(entry); + } + /** * Associates the specified entry with the specified category. An * entry can be mapped into multiple categories. diff --git a/projects/robodj/src/java/robodj/repository/ModelUtil.java b/projects/robodj/src/java/robodj/repository/ModelUtil.java index 9c7c2f74..d4292b90 100644 --- a/projects/robodj/src/java/robodj/repository/ModelUtil.java +++ b/projects/robodj/src/java/robodj/repository/ModelUtil.java @@ -1,9 +1,7 @@ // -// $Id: ModelUtil.java,v 1.1 2001/06/07 08:37:47 mdb Exp $ +// $Id: ModelUtil.java,v 1.2 2001/07/26 00:24:22 mdb Exp $ -package robodj.chooser; - -import robodj.repository.*; +package robodj.repository; /** * Some utility functions.