package com.pugh.sockso.gui;
import com.pugh.sockso.db.Database;
import com.pugh.sockso.Utils;
import com.pugh.sockso.music.MusicItem;
import com.pugh.sockso.music.Artist;
import com.pugh.sockso.music.Album;
import com.pugh.sockso.music.Track;
import com.pugh.sockso.music.Collection;
import com.pugh.sockso.music.CollectionManagerListener;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.awt.dnd.DragSource;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceDragEvent;
import javax.swing.JTree;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.DefaultMutableTreeNode;
import org.apache.log4j.Logger;
import com.google.inject.Inject;
public class MusicTree extends JTree implements DragSourceListener, DragGestureListener, CollectionManagerListener, TreeExpansionListener {
private static Logger log = Logger.getLogger( MusicTree.class );
private DragSource dragSource;
private final Database db;
/**
* constructor
*
* @param db database connection
*
*/
@Inject
public MusicTree( Database db ) {
super( new MusicTreeNode(new Collection()) );
this.db = db;
}
/**
* Initialise the music tree
*
*/
public void init() {
dragSource = new DragSource();
dragSource.createDefaultDragGestureRecognizer( this, DnDConstants.ACTION_COPY, this );
setShowsRootHandles( true );
refresh();
addTreeExpansionListener( this );
}
/**
* Handler for when tree nodes are collapsed
*
* @param evt
*
*/
public void treeCollapsed( final TreeExpansionEvent evt ) {}
/**
* Handler for when tree nodes are expanded, we may need to load their children
*
* @param evt
*
*/
public void treeExpanded( final TreeExpansionEvent evt ) {
final TreePath path = evt.getPath();
try {
final MusicTreeNode node = (MusicTreeNode) path.getLastPathComponent();
final MusicItem item = (MusicItem) node.getUserObject();
if ( item.getType().equals(MusicItem.ARTIST) ) {
fillArtistNode( node, item );
}
else if ( item.getType().equals(MusicItem.ALBUM) ) {
fillAlbumNode( node, item );
}
}
catch ( final SQLException e ) {
log.debug( e );
}
}
/**
* Fills the collection node with all the artists
*
* @param root
*
* @throws SQLException
*
*/
private void fillCollectionNode( final DefaultMutableTreeNode root ) throws SQLException {
ResultSet rs = null;
PreparedStatement st = null;
try {
final String sql = " select ar.id, ar.name " +
" from artists ar " +
" order by ar.name asc ";
st = db.prepare( sql );
rs = st.executeQuery();
while ( rs.next() ) {
final Artist artist = new Artist.Builder()
.id(rs.getInt("id"))
.name(rs.getString("name"))
.build();
final MusicTreeNode node = new MusicTreeNode( artist );
node.add( new DefaultMutableTreeNode() );
root.add( node );
}
reloadNode( root );
}
finally {
Utils.close( rs );
Utils.close( st );
}
}
/**
* Fills an artist node with albums
*
* @param node
* @param item
*
*/
private void fillArtistNode( final MusicTreeNode node, final MusicItem item ) throws SQLException {
ResultSet rs = null;
PreparedStatement st = null;
try {
final Artist artist = new Artist.Builder()
.id(item.getId())
.name(item.getName())
.build();
final String sql = " select al.id, al.name, al.year " +
" from albums al " +
" where al.artist_id = ? " +
" order by al.name asc ";
st = db.prepare( sql );
st.setInt( 1, item.getId() );
rs = st.executeQuery();
node.removeAllChildren();
while ( rs.next() ) {
final Album album = new Album.Builder()
.artist(artist)
.id(rs.getInt("id"))
.name(rs.getString("name"))
.year(rs.getString("year"))
.build();
final MusicTreeNode child = new MusicTreeNode( album );
child.add( new DefaultMutableTreeNode() );
node.add( child );
}
reloadNode( node );
}
finally {
Utils.close( rs );
Utils.close( st );
}
}
/**
* Fills an album node with albums
*
* @param node
* @param item
*
*/
private void fillAlbumNode( final MusicTreeNode node, final MusicItem item ) throws SQLException {
ResultSet rs = null;
PreparedStatement st = null;
try {
final String sql = Track.getSelectFromSql() +
" where t.album_id = ? " +
" order by t.track_no asc, t.name asc ";
st = db.prepare( sql );
st.setInt( 1, item.getId() );
rs = st.executeQuery();
node.removeAllChildren();
while ( rs.next() ) {
final Track track = Track.createFromResultSet( rs );
final MusicTreeNode child = new MusicTreeNode( track );
node.add( child );
}
reloadNode( node );
}
finally {
Utils.close( rs );
Utils.close( st );
}
}
/**
* Refreshes the tree with the artists from the collection
*
*/
public void refresh() {
try {
final DefaultTreeModel model = (DefaultTreeModel) this.getModel();
final DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
root.removeAllChildren();
fillCollectionNode( root );
reloadNode( root );
expandNode( root );
}
catch ( final SQLException e ) {
log.error( e );
}
}
/**
* Expands a tree node and fires the expanded event
*
* @param node
*
*/
protected void expandNode( final TreeNode node ) {
final TreePath path = new TreePath( ((DefaultMutableTreeNode)node).getPath() );
expandPath( path );
fireTreeExpanded( path );
}
/**
* Informs the tree model that a node has changed
*
* @param node
*
*/
private void reloadNode( final TreeNode node ) {
final DefaultTreeModel model = (DefaultTreeModel) this.getModel();
model.nodeStructureChanged( node );
}
/**
* the collection has changed
*
* @param type the type of change
* @param message a description of the change
*
*/
public void collectionManagerChangePerformed( int type, String message ) {
if ( type == CollectionManagerListener.UPDATE_COMPLETE ) {
refresh();
}
}
/**
* a drag gesture has been made, need to initiate the drag
*
* @param evt the drag event
*
*/
public void dragGestureRecognized( DragGestureEvent evt ) {
try {
final MusicTreeNode node = (MusicTreeNode) getSelectionPath().getLastPathComponent();
dragSource.startDrag(
evt, DragSource.DefaultMoveDrop,
(MusicItem) node.getUserObject(), this
);
}
catch ( final ClassCastException e ) {
log.error( "Error starting drag: " + e.getMessage() );
}
}
/*
* un-used drag and drop methods
*
*/
public void dragDropEnd( DragSourceDropEvent evt ) {}
public void dragExit( DragSourceEvent evt ) {}
public void dropActionChanged( DragSourceDragEvent evt ) {}
public void dragOver( DragSourceDragEvent evt ) {}
public void dragEnter( DragSourceDragEvent evt ) {}
}