/*
* PlaylistPanelList.java
*
* Created on May 16, 2007, 11:52:07 PM
*
* Allows music items to be dropped onto it, then moved around
* to create playlists.
*
*/
package com.pugh.sockso.gui;
import com.pugh.sockso.db.Database;
import com.pugh.sockso.Utils;
import com.pugh.sockso.music.Track;
import com.pugh.sockso.music.Album;
import com.pugh.sockso.music.Artist;
import com.pugh.sockso.music.Playlist;
import com.pugh.sockso.music.MusicItem;
import com.pugh.sockso.music.CollectionManager;
import java.awt.Component;
import java.awt.Point;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetListener;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DragGestureEvent;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import javax.swing.JLabel;
import javax.swing.DefaultListModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.ListSelectionModel;
import javax.swing.JOptionPane;
import javax.swing.JList;
import javax.swing.JFrame;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.log4j.Logger;
import com.google.inject.Inject;
public class PlaylistPanel extends MusicList implements DropTargetListener {
private static Logger log = Logger.getLogger( PlaylistPanel.class );
private final JFrame parent;
private final Database db;
private final CollectionManager cm;
private DefaultListModel model;
private DropTarget dropTarget;
private int dummyIndex;
private Track dragItem;
/**
* a custom cell renderer for displaying tracks information properly, when
* it's in the list normally, and when it's being dragged.
*
*/
private final class MyCellRenderer extends DefaultListCellRenderer {
@Override
public Component getListCellRendererComponent( JList list, Object value, int index, boolean isSelected, boolean focus ) {
JLabel c = (JLabel) super.getListCellRendererComponent( list, value, index, isSelected, focus );
// just displaying a normal track?
if ( value instanceof Track ) {
Track track = (Track) value;
c.setText( getTrackDisplay(track) );
}
// this is the dummy item, so we're dragging
else {
c.setBackground( list.getSelectionBackground() );
c.setText( getTrackDisplay(dragItem) );
}
return c;
}
private String getTrackDisplay( Track track ) {
return track.getName() + " - " +
track.getArtist().getName() +
" (" + track.getAlbum().getName() + ")";
}
}
/**
* constructor
*
*/
@Inject
public PlaylistPanel( final AppFrame parent, final Database db, final CollectionManager cm ) {
super( DnDConstants.ACTION_MOVE );
this.parent = parent;
this.db = db;
this.cm = cm;
dummyIndex = -1;
model = new DefaultListModel();
dropTarget = new DropTarget( this, DnDConstants.ACTION_COPY_OR_MOVE, this );
setModel( model );
setCellRenderer( new MyCellRenderer() );
setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
}
/**
* handles drop events onto this component
*
* @param evt the drop event
*
*/
public void drop( DropTargetDropEvent evt ) {
try {
Transferable trans = evt.getTransferable();
MusicItem item = (MusicItem) trans.getTransferData( MusicItem.MUSIC_ITEM_FLAVOR );
// is it an internal move?
if ( dummyIndex != -1 ) {
model.removeElementAt( dummyIndex );
model.insertElementAt( dragItem, dummyIndex );
dragItem = null;
dummyIndex = -1;
}
// otherwise external drag event
else insertIntoPlaylist( evt.getLocation(), item );
}
catch ( IOException e ) {
log.error( e.getMessage() );
}
catch ( UnsupportedFlavorException e ) {
log.error( e.getMessage() );
}
catch ( ClassCastException e ) {
log.error( e.getMessage() );
}
}
/**
* inserts the music item into the current playlist
*
* @param item the music item to add
*
*/
private void insertIntoPlaylist( Point location, MusicItem item ) {
try {
String sql = null;
String type = item.getType();
// get sql for track query
if ( type.equals(MusicItem.COLLECTION) )
sql = Track.getSelectFromSql(); // select everything in the collection
else if ( type.equals(MusicItem.PLAYLIST) ) {
Playlist playlist = (Playlist) item;
sql = Playlist.getSelectTracksSql(playlist.getId(),"");
}
else if ( type.equals(MusicItem.ARTIST) ) {
Artist artist = (Artist) item;
sql = Track.getSelectFromSql() +
" where t.artist_id = '" + artist.getId() + "' ";
}
else if ( type.equals(MusicItem.ALBUM) ) {
Album album = (Album) item;
sql = Track.getSelectFromSql() +
" where t.album_id = '" + album.getId() + "' ";
}
else if ( type.equals(MusicItem.TRACK) ) {
Track track = (Track) item;
sql = Track.getSelectFromSql() +
" where t.id = '" + track.getId() + "' ";
}
if ( sql != null ) {
ResultSet rs = null;
PreparedStatement st = null;
try {
st = db.prepare( sql );
rs = st.executeQuery();
int index = locationToIndex( location );
while ( rs.next() ) {
Track track = Track.createFromResultSet(rs);
if ( index < 1 ) model.addElement( track );
else model.insertElementAt( track, index + 1 );
}
}
finally {
Utils.close( rs );
Utils.close( st );
}
}
}
catch ( SQLException e ) {
log.error( e.getMessage() );
}
}
public void dragEnter( DropTargetDragEvent evt ) {
evt.acceptDrag( DnDConstants.ACTION_COPY_OR_MOVE );
}
public void dragExit( DropTargetEvent evt ) {}
public void dropActionChanged( DropTargetDragEvent evt ) {}
public void dragOver( DropTargetDragEvent evt ) {
if ( dummyIndex == -1 ) return;
int index = locationToIndex( evt.getLocation() );
if ( model.getSize() >= dummyIndex )
model.removeElementAt( dummyIndex );
model.insertElementAt( "", index );
dummyIndex = index;
}
/**
* returns the element at the given point (null if there's
* nothing actually there
*
* @param where the point to get item from
* @return the item at that point
*
*/
private MusicItem getValue( Point where ) {
int idx = locationToIndex( where );
return (MusicItem) model.getElementAt( idx );
}
/**
* returns an array of the tracks in the playlist as Track objects
*
* @return array of tracks
*
*/
private Track[] getTracks() {
int size = model.getSize();
Track[] tracks = new Track[ size ];
for ( int i=0; i<size; i++ )
tracks[ i ] = (Track) model.getElementAt( i );
return tracks;
}
/**
* saves the current playlist
*
*/
public void save() {
// get name of playlist from user
final String playlistName = JOptionPane.showInputDialog( parent, "Please enter a name for the playlist:" );
if ( playlistName == null ) return;
final Track[] tracks = getTracks();
try {
if ( playlistExists(playlistName) ) {
if ( JOptionPane.showConfirmDialog( this, "That playlist already exists, replace it?",
"Overwrite Playlist", JOptionPane.YES_NO_OPTION ) != JOptionPane.YES_OPTION ) {
return;
}
}
cm.savePlaylist( playlistName, tracks );
}
catch ( SQLException e ) {
JOptionPane.showMessageDialog( parent, e.getMessage() );
log.error( e.getMessage() );
}
}
/**
* Checks if a playlist by this name exists in the database
*
* @param name
*
* @return
*
* @throws SQLException
*
*/
protected boolean playlistExists( final String name ) throws SQLException {
ResultSet rs = null;
PreparedStatement st = null;
try {
final String sql = " select id " +
" from playlists p " +
" where name = ? ";
st = db.prepare( sql );
st.setString( 1, name );
rs = st.executeQuery();
return rs.next();
}
finally {
Utils.close( rs );
Utils.close( st );
}
}
/**
* removes the selected items from the playlist
*
*/
public void removeSelection() {
int[] indexes = getSelectedIndices();
for ( int i=0; i<indexes.length; i++ )
model.removeElementAt( indexes[i] );
}
/**
* clears the playlist of all its items
*
*/
public void clear() {
model.clear();
}
/**
* this list acts as a drag source for items being moved around inside it
*
* @param evt the drag gesture event
*
*/
@Override
public void dragGestureRecognized( DragGestureEvent evt ) {
if ( getSelectedValue() == null ) return;
super.dragGestureRecognized( evt );
// remove drag item
dummyIndex = getSelectedIndex();
dragItem = (Track) model.getElementAt( dummyIndex );
model.removeElementAt( dummyIndex );
// and insert dummy
model.insertElementAt( "", dummyIndex );
}
}