diff --git a/pom.xml b/pom.xml
index f2d77fb..a76065b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -101,6 +101,7 @@
com.google.inject
guice
4.0
+ compile
diff --git a/src/main/java/musicplayer/LibraryConfigGUI.java b/src/main/java/musicplayer/LibraryConfigGUI.java
index ef1db02..ba8687b 100644
--- a/src/main/java/musicplayer/LibraryConfigGUI.java
+++ b/src/main/java/musicplayer/LibraryConfigGUI.java
@@ -1,10 +1,11 @@
package musicplayer;
-import musicplayer.swingmodels.LibraryListModel;
+import musicplayer.util.ConfigManager;
import javax.swing.*;
import java.awt.*;
import java.io.File;
+import java.util.ArrayList;
import java.util.List;
class LibraryConfigGUI {
@@ -73,4 +74,74 @@ class LibraryConfigGUI {
files.forEach(listModel::removeFile);
saveLibraryList();
}
+
+ private class LibraryListModel extends AbstractListModel {
+
+ private List libraryFolders = new ArrayList<>();
+
+ @Override
+ public int getSize() {
+ return libraryFolders.size();
+ }
+
+ @Override
+ public T getElementAt(int index) {
+ return libraryFolders.get(index);
+ }
+
+ /**
+ * Add a directory to the list of directories used for library indexing.
+ * @param folder Directory to add.
+ */
+ public void addFolder(T folder){
+ if(folder.exists() && folder.isDirectory()) {
+ libraryFolders.add(folder);
+ fireAllContentsChanged();
+ }
+ }
+
+ /**
+ * @return List of directories currently used for library indexing.
+ */
+ public List currentFolderList(){
+ return new ArrayList<>(libraryFolders); // Copy? Don't want modification via here.
+ }
+
+ /**
+ * Check if this File already exists in libraryFolders.
+ * @param file File to find.
+ * @return Does file exist in libraryFolders.
+ */
+ public boolean contains(T file){
+ return libraryFolders.contains(file);
+ }
+
+ /**
+ * Set the list contained in libraryFolders.
+ * @param folderList New directory list.
+ */
+ public void setFolderList(List folderList){
+ if(folderList != null && folderList.size() > 0)
+ libraryFolders = new ArrayList<>(folderList);
+ else
+ libraryFolders = new ArrayList<>();
+ fireAllContentsChanged();
+ }
+
+ /**
+ * Remove a directory from libraryFolders.
+ * @param file Directory to be removed.
+ */
+ public void removeFile(T file){
+ libraryFolders.remove(file);
+ fireAllContentsChanged();
+ }
+
+ /**
+ * Fire that libraryFolders contents has changed.
+ */
+ private void fireAllContentsChanged(){
+ fireContentsChanged(this, 0, libraryFolders.size() == 0 ? 0 : libraryFolders.size() - 1);
+ }
+ }
}
diff --git a/src/main/java/musicplayer/LibraryTreeCellRenderer.java b/src/main/java/musicplayer/LibraryTreeCellRenderer.java
deleted file mode 100644
index 48ffa87..0000000
--- a/src/main/java/musicplayer/LibraryTreeCellRenderer.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package musicplayer;
-
-import musicplayer.model.Album;
-
-import javax.swing.*;
-import javax.swing.tree.DefaultMutableTreeNode;
-import javax.swing.tree.TreeCellRenderer;
-import java.awt.*;
-
-class LibraryTreeCellRenderer implements TreeCellRenderer {
-
- private final JLabel label;
- private static Icon missingIcon = new ImageIcon(LibraryTreeCellRenderer.class.getClassLoader().getResource("missing.gif"));
-
- LibraryTreeCellRenderer() {
- label = new JLabel();
- }
-
- @Override
- public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
- Object o = ((DefaultMutableTreeNode) value).getUserObject();
- label.setIcon(null);
- if (o != null) {
- label.setText(o.toString());
- if (o instanceof Album) {
- Album album = (Album) o;
- label.setIcon(missingIcon);
- album.getAlbumArt().ifPresent(x -> label.setIcon(new ImageIcon(x)));
- }
- }
- return label;
- }
-}
diff --git a/src/main/java/musicplayer/PlayerGUI.java b/src/main/java/musicplayer/PlayerGUI.java
index 4a0215a..29a51e9 100644
--- a/src/main/java/musicplayer/PlayerGUI.java
+++ b/src/main/java/musicplayer/PlayerGUI.java
@@ -3,17 +3,14 @@ package musicplayer;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
-import musicplayer.callbacks.LibraryCallbackInterface;
import musicplayer.callbacks.PlayerCallbackInterface;
import musicplayer.db.IDatabase;
-import musicplayer.model.Album;
-import musicplayer.model.Artist;
-import musicplayer.model.HasSongs;
+import musicplayer.library.ILibrary;
import musicplayer.model.Song;
import musicplayer.player.IPlayer;
-import musicplayer.swingmodels.PlaylistTableModel;
+import musicplayer.playlist.IPlaylist;
+import musicplayer.util.ConfigManager;
import musicplayer.util.Icons;
-import musicplayer.util.LibraryUtils;
import musicplayer.util.PlaylistUtils;
import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
@@ -23,12 +20,7 @@ import org.jnativehook.keyboard.NativeKeyListener;
import javax.swing.*;
import javax.swing.event.HyperlinkEvent;
import javax.swing.filechooser.FileNameExtensionFilter;
-import javax.swing.tree.DefaultMutableTreeNode;
-import javax.swing.tree.DefaultTreeModel;
-import javax.swing.tree.TreeNode;
import java.awt.*;
-import java.awt.event.ItemEvent;
-import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedReader;
@@ -36,35 +28,25 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
-import java.nio.file.Path;
import java.util.*;
-import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
-public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterface {
- private JPanel mainPanel;
- private final JTree libraryView = new JTree();
- private final JTable playList = new JTable();
+public class PlayerGUI extends JPanel implements PlayerCallbackInterface {
+ private final IPlaylist playlist;
private JSlider seekBar;
- private final JComboBox libraryDisplayType = new JComboBox<>();
private final IPlayer player;
- private final IDatabase database;
- private final PlaylistTableModel playlistTableModel = new PlaylistTableModel(new ArrayList<>());
- private static final DefaultMutableTreeNode updatingNode = new DefaultMutableTreeNode();
- private boolean libraryUpdating = false;
+ private final ILibrary library;
private final AtomicBoolean seeking = new AtomicBoolean(false);
- private final Map libraryDisplayVariants = createDisplayVariantMap();
-
@Inject
- public PlayerGUI(IPlayer player, IDatabase database) {
+ public PlayerGUI(IPlayer player, IPlaylist playlist, ILibrary library) {
this.player = player;
- this.database = database;
+ this.playlist = playlist;
+ this.library = library;
createUI();
- resetTree();
Thread seekBarUpdater = new Thread(() -> {
boolean running = true;
while (running) {
@@ -99,15 +81,15 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
System.out.println(ex.getMessage());
System.out.println(Arrays.toString(ex.getStackTrace()));
}
- refreshLibrary();
+ library.refreshLibrary();
}
public static void main(String[] args) {
- Injector injector = Guice.createInjector();
+ Injector injector = Guice.createInjector(new SwingUIModule());
PlayerGUI playerGUI = injector.getInstance(PlayerGUI.class);
JFrame frame = new JFrame();
frame.setMinimumSize(new Dimension(600, 400));
- frame.setContentPane(playerGUI.mainPanel);
+ frame.setContentPane(playerGUI);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
@@ -125,61 +107,6 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
});
}
- /**
- * Reset the libraryView to contain nothing.
- */
- private void resetTree() {
- libraryView.removeAll();
- }
-
- private > void populateLibraryWithGroupedSongs(List libraryData){
- DefaultTreeModel model = new DefaultTreeModel(new DefaultMutableTreeNode());
- DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) model.getRoot();
- Collections.sort(libraryData);
- libraryData.forEach(x -> {
- DefaultMutableTreeNode outerNode = new DefaultMutableTreeNode(x);
- addNodeToTreeModel(model, parentNode, outerNode);
- x.getSongs().forEach(y -> addNodeToTreeModel(model, outerNode, new DefaultMutableTreeNode(y)));
- });
- libraryView.setModel(model);
- }
-
- /**
- * Populate the library with all songs.
- *
- * @param libraryData List of songs.
- */
- private void populateLibraryWithSongsOnly(List libraryData) {
- DefaultTreeModel model = new DefaultTreeModel(new DefaultMutableTreeNode());
- DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) model.getRoot();
- Collections.sort(libraryData);
- libraryData.forEach(x -> addNodeToTreeModel(model, parentNode, new DefaultMutableTreeNode(x)));
- libraryView.setModel(model);
- }
-
- /**
- * Add an item to libraryView.
- *
- * @param parentNode Node that should be the parent of this node.
- * @param node This node.
- */
- private void addNodeToTreeModel(DefaultTreeModel model, DefaultMutableTreeNode parentNode, DefaultMutableTreeNode node) {
- model.insertNodeInto(node, parentNode, parentNode.getChildCount());
- if (parentNode == model.getRoot()) {
- model.nodeStructureChanged((TreeNode) model.getRoot());
- }
- }
-
- /**
- * Add a song to the current playlist.
- *
- * @param song Song to be added to the playlist.
- */
- private void addToPlaylist(Song song) {
- playlistTableModel.addSong(song);
- playList.revalidate();
- }
-
/**
* Set the highlighted song in the playlist to this song.
*
@@ -187,17 +114,14 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
*/
@Override
public void setSongHighlighting(Song playingSong) {
- int index = playlistTableModel.getSongIndex(playingSong);
- if(index >= 0)
- SwingUtilities.invokeLater(() -> playList.setRowSelectionInterval(index, index));
- playlistTableModel.setPlayingRow(index);
+ playlist.setPlayingSong(playingSong);
}
private void playSong(Optional song){
try {
player.playSong(song);
} catch (StartPlayingException e) {
- JOptionPane.showMessageDialog(mainPanel, "Failed to play " + song.get().toString() + ".\n" +
+ JOptionPane.showMessageDialog(this, "Failed to play " + song.get().toString() + ".\n" +
"If file path contains non-ASCII characters, please restart the application with UTF-8 encoding.");
}
}
@@ -207,7 +131,7 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
*/
public void playNextSong() {
SwingUtilities.invokeLater(() -> seekBar.setValue(0));
- playSong(playlistTableModel.nextSong(player.getCurrentSong()));
+ playSong(playlist.getNext(player.getCurrentSong()));
}
/**
@@ -215,7 +139,7 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
*/
public void playPreviousSong() {
SwingUtilities.invokeLater(() -> seekBar.setValue(0));
- playSong(playlistTableModel.previous(player.getCurrentSong()));
+ playSong(playlist.getPrevious(player.getCurrentSong()));
}
/**
@@ -229,76 +153,16 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
@Override
public void removeInvalidSong(Song invalidSong) {
- playlistTableModel.removeSong(invalidSong);
+ playlist.delete(invalidSong);
}
public void playerStopped(){
setSongHighlighting(player.getCurrentSong());
- playlistTableModel.setPlayingRow(-1);
- }
-
- /**
- * Refresh the library with songs in the currently selected display format.
- */
- private void refreshLibrary() {
- DefaultTreeModel model = new DefaultTreeModel(new DefaultMutableTreeNode());
- DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) model.getRoot();
- DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("Refreshing Library...");
- addNodeToTreeModel(model, parentNode, newNode);
- libraryView.setModel(model);
- Thread populateThread = new Thread(() -> {
- libraryDisplayVariants.get(libraryDisplayType.getSelectedItem().toString()).run();
- // If we get here and the model hasn't changed, the library must be empty.
- if(libraryView.getModel() == model){
- DefaultTreeModel failedModel = new DefaultTreeModel(new DefaultMutableTreeNode());
- DefaultMutableTreeNode failedParentNode = (DefaultMutableTreeNode) failedModel.getRoot();
- DefaultMutableTreeNode failedNode = new DefaultMutableTreeNode("Library is empty!");
- addNodeToTreeModel(failedModel, failedParentNode, failedNode);
- libraryView.setModel(failedModel);
- }
- }, "libraryRefresh");
- populateThread.start();
- }
-
- private void updateLibrary(){
- libraryUpdating = true;
- DefaultTreeModel model = new DefaultTreeModel(new DefaultMutableTreeNode());
- DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) model.getRoot();
- DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("Updating Library...");
- addNodeToTreeModel(model, parentNode, newNode);
- addNodeToTreeModel(model, parentNode, updatingNode);
- libraryView.setModel(model);
- List dirs = ConfigManager.getLibraryDirectories().stream().map(File::toPath).collect(Collectors.toList());
- LibraryUtils.processSongsWithCallback(database, dirs, this);
- }
-
- @Override
- public void libraryUpdated(boolean successful) {
- if (successful)
- refreshLibrary();
- libraryUpdating = false;
- }
-
- @Override
- public void currentlyUpdating(String name) {
- updatingNode.setUserObject(name);
- ((DefaultTreeModel)libraryView.getModel()).nodeChanged(updatingNode);
- }
-
- /**
- * @return Map of display types for the library paired code to populate the library with data in the correct format.
- */
- private Map createDisplayVariantMap() {
- Map value = new HashMap<>();
- value.put("Song", () -> database.listAllT(Song.class).ifPresent(this::populateLibraryWithSongsOnly));
- value.put("Album/Song", () -> database.listAllT(Album.class).ifPresent(this::populateLibraryWithGroupedSongs));
- value.put("Artist/Song", () -> database.listAllT(Artist.class).ifPresent(this::populateLibraryWithGroupedSongs));
-
- return value;
+ playlist.setPlayingSong(null);
}
/*
- mainPanel
+ this
+--------------------+
|menuBar |
+---------+----------+
@@ -311,79 +175,22 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
+--------------------+
*/
private void createUI() {
- mainPanel = new JPanel();
- mainPanel.setLayout(new BorderLayout(0, 0));
+ this.setLayout(new BorderLayout(0, 0));
final JSplitPane libraryAndPlaylistPane = new JSplitPane();
libraryAndPlaylistPane.setDividerLocation(240);
- libraryAndPlaylistPane.setRightComponent(createPlaylistArea());
- mainPanel.add(libraryAndPlaylistPane, BorderLayout.CENTER);
- final JPanel libraryAndComboboxPane = new JPanel();
- libraryAndComboboxPane.setLayout(new BorderLayout(0, 0));
- libraryAndPlaylistPane.setLeftComponent(libraryAndComboboxPane);
- libraryDisplayVariants.keySet().forEach(libraryDisplayType::addItem);
- libraryDisplayType.setSelectedIndex(ConfigManager.getLastDisplayTypeIndex());
- libraryDisplayType.addItemListener(e -> {
- if (e.getStateChange() == ItemEvent.SELECTED) {
- ConfigManager.setLastDisplayTypeIndex(libraryDisplayType.getSelectedIndex());
- if(!libraryUpdating)
- libraryDisplayVariants.get(libraryDisplayType.getSelectedItem().toString()).run();
- }
- });
- libraryAndComboboxPane.add(libraryDisplayType, BorderLayout.NORTH);
- final JScrollPane libraryPane = new JScrollPane();
- libraryAndComboboxPane.add(libraryPane, BorderLayout.CENTER);
- libraryPane.setViewportView(createLibraryArea());
+ libraryAndPlaylistPane.setRightComponent((JScrollPane)playlist);
+ this.add(libraryAndPlaylistPane, BorderLayout.CENTER);
+ libraryAndPlaylistPane.setLeftComponent((JPanel)library);
final JPanel controlPane = new JPanel();
controlPane.setLayout(new BorderLayout(0, 0));
- mainPanel.add(controlPane, BorderLayout.SOUTH);
+ this.add(controlPane, BorderLayout.SOUTH);
seekBar = new JSlider();
seekBar.setMinorTickSpacing(1);
seekBar.setValue(0);
seekBar.addMouseListener(new SeekbarMouseListener());
controlPane.add(seekBar, BorderLayout.NORTH);
controlPane.add(createControlButtons(), BorderLayout.SOUTH);
- mainPanel.add(createMenuBar(), BorderLayout.NORTH);
- }
-
- /**
- * @return Area containing the library.
- */
- private JTree createLibraryArea() {
- libraryView.setRootVisible(false);
- libraryView.setToggleClickCount(1);
- libraryView.setCellRenderer(new LibraryTreeCellRenderer());
- libraryView.addMouseListener(new LibraryMouseAdapter());
- libraryView.setScrollsOnExpand(false);
- return libraryView;
- }
-
- /**
- * @return Area containing the playlist.
- */
- private JScrollPane createPlaylistArea() {
- JScrollPane playlistScroll = new JScrollPane();
- playList.setRowSelectionAllowed(true);
- playList.setModel(playlistTableModel);
- playList.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
- playList.getColumnModel().getColumn(0).setPreferredWidth(10);
- playList.getColumnModel().getColumn(0).setMaxWidth(10);
- playList.getColumnModel().getColumn(1).setPreferredWidth(40);
- playList.getColumnModel().getColumn(1).setMaxWidth(40);
- JPopupMenu popupMenu = new JPopupMenu();
- JMenuItem menuItem = new JMenuItem("Remove");
- menuItem.addActionListener((e) -> playlistTableModel.removeSong(playList.getSelectedRows()));
- popupMenu.add(menuItem);
- playList.setComponentPopupMenu(popupMenu);
- popupMenu = new JPopupMenu();
- menuItem = new JMenuItem("Clear all");
- menuItem.addActionListener((e) -> {
- playlistTableModel.removeAll();
- player.stop();
- });
- popupMenu.add(menuItem);
- playlistScroll.setComponentPopupMenu(popupMenu);
- playlistScroll.setViewportView(playList);
- return playlistScroll;
+ this.add(createMenuBar(), BorderLayout.NORTH);
}
/**
@@ -394,16 +201,10 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
toolBar.setFloatable(false);
JPanel controlBar = new JPanel(new GridLayout(1, 5));
JButton playButton = new JButton();
+ playButton.setName("playButton");
playButton.setIcon(Icons.playIcon);
playButton.setMnemonic('P');
- playButton.addActionListener(e -> {
- if (playList.getRowCount() > 0) {
- if (playList.getSelectedRowCount() > 0)
- playSong(playlistTableModel.getSong(playList.getSelectedRow()));
- else
- playSong(playlistTableModel.getFirst());
- }
- });
+ playButton.addActionListener(e -> playSong(playlist.getActive()));
controlBar.add(playButton);
JButton pauseButton = new JButton();
pauseButton.setIcon(Icons.pauseIcon);
@@ -465,12 +266,12 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
// Tools menu
JMenu tools = new JMenu("Tools");
JMenuItem menuItem = new JMenuItem("Refresh Library");
- menuItem.addActionListener(e -> refreshLibrary());
+ menuItem.addActionListener(e -> library.refreshLibrary());
tools.add(menuItem);
menuItem = new JMenuItem("Update Library");
menuItem.setToolTipText("Reindex and refresh library.");
- menuItem.addActionListener(e -> updateLibrary());
+ menuItem.addActionListener(e -> library.updateLibrary());
tools.add(menuItem);
menuItem = new JMenuItem("Library Settings");
@@ -483,7 +284,7 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
JMenu playlistTools = new JMenu("Playlist");
menuItem = new JMenuItem("Clear All");
menuItem.addActionListener((e) -> {
- playlistTableModel.removeAll();
+ playlist.deleteAll();
player.stop();
});
playlistTools.add(menuItem);
@@ -491,17 +292,17 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
FileNameExtensionFilter m3uExtensionFilter = new FileNameExtensionFilter("m3u playlist", "m3u");
menuItem = new JMenuItem("Save Playlist");
menuItem.addActionListener(e -> {
- if(!playlistTableModel.isEmpty()) {
+ if(!playlist.isEmpty()) {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
fileChooser.setSelectedFile(new File("playlist.m3u"));
fileChooser.setFileFilter(m3uExtensionFilter);
- if (fileChooser.showSaveDialog(mainPanel) == JFileChooser.APPROVE_OPTION) {
+ if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
String filename = fileChooser.getSelectedFile().toString();
if (!filename.endsWith(".m3u"))
filename += ".m3u";
try {
- PlaylistUtils.writePlaylistToFile(playlistTableModel.getSongList(), new File(filename));
+ PlaylistUtils.writePlaylistToFile(playlist.getSongList(), new File(filename));
} catch (IOException e1) {
JOptionPane.showMessageDialog(null, e1.getMessage() + "\n" + Arrays.toString(e1.getStackTrace()), "Error", JOptionPane.ERROR_MESSAGE);
}
@@ -515,8 +316,8 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogType(JFileChooser.OPEN_DIALOG);
fileChooser.setFileFilter(m3uExtensionFilter);
- if(fileChooser.showOpenDialog(mainPanel) == JFileChooser.APPROVE_OPTION){
- PlaylistUtils.readPlaylistFromFile(fileChooser.getSelectedFile()).stream().forEach(playlistTableModel::addSong);
+ if(fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION){
+ PlaylistUtils.readPlaylistFromFile(fileChooser.getSelectedFile()).stream().forEach(playlist::addSong);
}
});
playlistTools.add(menuItem);
@@ -540,7 +341,7 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
e1.printStackTrace();
}
});
- JOptionPane.showMessageDialog(mainPanel, messagePane, "About", JOptionPane.INFORMATION_MESSAGE);
+ JOptionPane.showMessageDialog(this, messagePane, "About", JOptionPane.INFORMATION_MESSAGE);
} catch (IOException e1) {
e1.printStackTrace();
}
@@ -551,25 +352,6 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
return menuBar;
}
- private class LibraryMouseAdapter extends MouseAdapter {
- @Override
- public void mousePressed(MouseEvent e) {
- int selRow = libraryView.getRowForLocation(e.getX(), e.getY());
- if(e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2 && selRow != -1)
- try {
- Object selectedItem = ((DefaultMutableTreeNode) libraryView.getPathForLocation(e.getX(), e.getY()).getLastPathComponent()).getUserObject();
- if (selectedItem != null) {
- if (selectedItem instanceof Song) {
- addToPlaylist((Song) selectedItem);
- } else if (selectedItem instanceof Album) {
- ((Album) selectedItem).getSongs().forEach(PlayerGUI.this::addToPlaylist);
- }
- }
- } catch (NullPointerException ignored) {
- }
- }
- }
-
/**
* MouseListener implementation for the seekbar.
* Using this instead of the ChangeListener so that the polling for the current song position
@@ -624,12 +406,7 @@ public class PlayerGUI implements PlayerCallbackInterface, LibraryCallbackInterf
setSongHighlighting(player.getCurrentSong()); // Resume won't function if a different song is selected.
}
else{
- if (playList.getRowCount() > 0) {
- if (playList.getSelectedRowCount() > 0)
- playSong(playlistTableModel.getSong(playList.getSelectedRow()));
- else
- playSong(playlistTableModel.getFirst());
- }
+ playSong(playlist.getActive());
}
break;
case stop:
diff --git a/src/main/java/musicplayer/SwingUIModule.java b/src/main/java/musicplayer/SwingUIModule.java
new file mode 100644
index 0000000..9acc9bd
--- /dev/null
+++ b/src/main/java/musicplayer/SwingUIModule.java
@@ -0,0 +1,23 @@
+package musicplayer;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Singleton;
+import musicplayer.db.HibernateDatabase;
+import musicplayer.db.IDatabase;
+import musicplayer.library.ILibrary;
+import musicplayer.library.JTreeLibrary;
+import musicplayer.player.GStreamerPlayer;
+import musicplayer.player.IPlayer;
+import musicplayer.playlist.IPlaylist;
+import musicplayer.playlist.JTablePlaylist;
+
+public class SwingUIModule extends AbstractModule {
+
+ @Override
+ protected void configure() {
+ bind(IPlayer.class).to(GStreamerPlayer.class).in(Singleton.class);
+ bind(IDatabase.class).to(HibernateDatabase.class).in(Singleton.class);
+ bind(IPlaylist.class).to(JTablePlaylist.class).in(Singleton.class);
+ bind(ILibrary.class).to(JTreeLibrary.class).in(Singleton.class);
+ }
+}
diff --git a/src/main/java/musicplayer/db/HibernateDatabase.java b/src/main/java/musicplayer/db/HibernateDatabase.java
index f22eaed..aaab4bd 100644
--- a/src/main/java/musicplayer/db/HibernateDatabase.java
+++ b/src/main/java/musicplayer/db/HibernateDatabase.java
@@ -1,6 +1,6 @@
package musicplayer.db;
-import musicplayer.ConfigManager;
+import musicplayer.util.ConfigManager;
import musicplayer.model.*;
import musicplayer.util.AlbumArtExtractor;
import org.hibernate.Criteria;
@@ -18,7 +18,7 @@ import java.util.Properties;
/**
* Class for managing connection to Hibernate.
*/
-class HibernateDatabase implements IDatabase{
+public class HibernateDatabase implements IDatabase{
private static SessionFactory sessionFactory;
diff --git a/src/main/java/musicplayer/db/IDatabase.java b/src/main/java/musicplayer/db/IDatabase.java
index 8081ccc..aa8bd9a 100644
--- a/src/main/java/musicplayer/db/IDatabase.java
+++ b/src/main/java/musicplayer/db/IDatabase.java
@@ -1,12 +1,10 @@
package musicplayer.db;
-import com.google.inject.ImplementedBy;
import musicplayer.model.*;
import java.util.List;
import java.util.Optional;
-@ImplementedBy(HibernateDatabase.class)
public interface IDatabase {
Optional getOneAlbum(String name);
Optional getOneArtist(String name);
diff --git a/src/main/java/musicplayer/library/ILibrary.java b/src/main/java/musicplayer/library/ILibrary.java
new file mode 100644
index 0000000..1af778d
--- /dev/null
+++ b/src/main/java/musicplayer/library/ILibrary.java
@@ -0,0 +1,10 @@
+package musicplayer.library;
+
+import musicplayer.model.HasSongs;
+
+public interface ILibrary {
+ void showSongs();
+ > void showGroupedSongs(Class grouping);
+ void updateLibrary();
+ void refreshLibrary();
+}
diff --git a/src/main/java/musicplayer/library/JTreeLibrary.java b/src/main/java/musicplayer/library/JTreeLibrary.java
new file mode 100644
index 0000000..5465ac6
--- /dev/null
+++ b/src/main/java/musicplayer/library/JTreeLibrary.java
@@ -0,0 +1,222 @@
+package musicplayer.library;
+
+import com.google.inject.Inject;
+import musicplayer.util.ConfigManager;
+import musicplayer.callbacks.LibraryCallbackInterface;
+import musicplayer.db.IDatabase;
+import musicplayer.model.Album;
+import musicplayer.model.Artist;
+import musicplayer.model.HasSongs;
+import musicplayer.model.Song;
+import musicplayer.playlist.IPlaylist;
+import musicplayer.util.LibraryUtils;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeCellRenderer;
+import javax.swing.tree.TreeNode;
+import java.awt.*;
+import java.awt.event.ItemEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+
+public class JTreeLibrary extends JPanel implements ILibrary, LibraryCallbackInterface{
+
+ IDatabase database;
+ IPlaylist playlist;
+ private static final DefaultMutableTreeNode updatingNode = new DefaultMutableTreeNode();
+ private final AtomicBoolean libraryUpdating = new AtomicBoolean(false);
+ private final JComboBox libraryDisplayType = new JComboBox<>();
+ private final JTree libraryTree = new JTree();
+ private final Map libraryDisplayVariants = createDisplayVariantMap();
+
+ /**
+ * @return Map of display types for the library paired code to populate the library with data in the correct format.
+ */
+ private Map createDisplayVariantMap() {
+ Map value = new HashMap<>();
+ value.put("Song", this::showSongs);
+ value.put("Album/Song", () -> this.showGroupedSongs(Album.class));
+ value.put("Artist/Song", () -> this.showGroupedSongs(Artist.class));
+ return value;
+ }
+
+ @Inject
+ public JTreeLibrary(IDatabase database, IPlaylist playlist){
+ this.database = database;
+ this.playlist = playlist;
+ this.setLayout(new BorderLayout(0, 0));
+ libraryTree.setRootVisible(false);
+ libraryTree.setToggleClickCount(1);
+ libraryTree.setCellRenderer(new LibraryTreeCellRenderer());
+ libraryTree.addMouseListener(new LibraryMouseAdapter());
+ libraryTree.setScrollsOnExpand(false);
+ final JScrollPane libraryPane = new JScrollPane(libraryTree);
+ this.add(libraryPane, BorderLayout.CENTER);
+
+ libraryDisplayVariants.keySet().forEach(libraryDisplayType::addItem);
+ libraryDisplayType.setSelectedIndex(ConfigManager.getLastDisplayTypeIndex());
+ libraryDisplayType.addItemListener(e -> {
+ if (e.getStateChange() == ItemEvent.SELECTED) {
+ ConfigManager.setLastDisplayTypeIndex(libraryDisplayType.getSelectedIndex());
+ refreshLibrary();
+ }
+ });
+ this.add(libraryDisplayType, BorderLayout.NORTH);
+ }
+
+ /**
+ * Add an item to libraryView.
+ *
+ * @param parentNode Node that should be the parent of this node.
+ * @param node This node.
+ */
+ private void addNodeToTreeModel(DefaultTreeModel model, DefaultMutableTreeNode parentNode, DefaultMutableTreeNode node) {
+ model.insertNodeInto(node, parentNode, parentNode.getChildCount());
+ if (parentNode == model.getRoot()) {
+ model.nodeStructureChanged((TreeNode) model.getRoot());
+ }
+ }
+
+ private void showEmpty(){
+ DefaultTreeModel failedModel = new DefaultTreeModel(new DefaultMutableTreeNode());
+ DefaultMutableTreeNode failedParentNode = (DefaultMutableTreeNode) failedModel.getRoot();
+ DefaultMutableTreeNode failedNode = new DefaultMutableTreeNode("Library is empty!");
+ addNodeToTreeModel(failedModel, failedParentNode, failedNode);
+ libraryTree.setModel(failedModel);
+ }
+
+ @Override
+ public void showSongs() {
+ Optional> dbQuery = database.listAllT(Song.class);
+ dbQuery.ifPresent(x -> {
+ DefaultTreeModel model = new DefaultTreeModel(new DefaultMutableTreeNode());
+ DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) model.getRoot();
+ Collections.sort(x);
+ x.forEach(y -> addNodeToTreeModel(model, parentNode, new DefaultMutableTreeNode(y)));
+ libraryTree.setModel(model);
+ });
+ if (!dbQuery.isPresent())
+ showEmpty();
+ }
+
+ @Override
+ public > void showGroupedSongs(Class grouping) {
+ Optional> dbQuery = database.listAllT(grouping);
+ dbQuery.ifPresent(x -> {
+ DefaultTreeModel model = new DefaultTreeModel(new DefaultMutableTreeNode());
+ DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) model.getRoot();
+ Collections.sort(x);
+ x.forEach(y -> {
+ DefaultMutableTreeNode outerNode = new DefaultMutableTreeNode(y);
+ addNodeToTreeModel(model, parentNode, outerNode);
+ y.getSongs().forEach(z -> addNodeToTreeModel(model, outerNode, new DefaultMutableTreeNode(z)));
+ });
+ libraryTree.setModel(model);
+ });
+ if (!dbQuery.isPresent())
+ showEmpty();
+
+ }
+
+ @Override
+ public void updateLibrary() {
+ if(!libraryUpdating.get()) {
+ libraryUpdating.set(true);
+ DefaultTreeModel model = new DefaultTreeModel(new DefaultMutableTreeNode());
+ DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) model.getRoot();
+ DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("Updating Library...");
+ addNodeToTreeModel(model, parentNode, newNode);
+ addNodeToTreeModel(model, parentNode, updatingNode);
+ libraryTree.setModel(model);
+ List dirs = ConfigManager.getLibraryDirectories().stream().map(File::toPath).collect(Collectors.toList());
+ LibraryUtils.processSongsWithCallback(database, dirs, this);
+ }
+ }
+
+ @Override
+ public void refreshLibrary() {
+ if(!libraryUpdating.get()) { // Don't try to refresh while updating!!
+ DefaultTreeModel model = new DefaultTreeModel(new DefaultMutableTreeNode());
+ DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) model.getRoot();
+ DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("Refreshing Library...");
+ addNodeToTreeModel(model, parentNode, newNode);
+ libraryTree.setModel(model);
+ Thread populateThread = new Thread(() -> {
+ libraryDisplayVariants.get(libraryDisplayType.getSelectedItem().toString()).run();
+ // If we get here and the model hasn't changed, the library must be empty.
+ if (libraryTree.getModel() == model) {
+ DefaultTreeModel failedModel = new DefaultTreeModel(new DefaultMutableTreeNode());
+ DefaultMutableTreeNode failedParentNode = (DefaultMutableTreeNode) failedModel.getRoot();
+ DefaultMutableTreeNode failedNode = new DefaultMutableTreeNode("Library is empty!");
+ addNodeToTreeModel(failedModel, failedParentNode, failedNode);
+ libraryTree.setModel(failedModel);
+ }
+ }, "libraryRefresh");
+ populateThread.start();
+ }
+ }
+
+ @Override
+ public void libraryUpdated(boolean successful) {
+ libraryUpdating.set(false);
+ refreshLibrary();
+ }
+
+ @Override
+ public void currentlyUpdating(String name) {
+ updatingNode.setUserObject(name);
+ ((DefaultTreeModel)libraryTree.getModel()).nodeChanged(updatingNode);
+ }
+
+ private class LibraryMouseAdapter extends MouseAdapter {
+ @Override
+ public void mousePressed(MouseEvent e) {
+ int selRow = libraryTree.getRowForLocation(e.getX(), e.getY());
+ if(e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2 && selRow != -1)
+ try {
+ Object selectedItem = ((DefaultMutableTreeNode) libraryTree.getPathForLocation(e.getX(), e.getY()).getLastPathComponent()).getUserObject();
+ if (selectedItem != null) {
+ if (selectedItem instanceof Song) {
+ playlist.addSong((Song) selectedItem);
+ } else if (selectedItem instanceof HasSongs) {
+ ((HasSongs) selectedItem).getSongs().forEach(playlist::addSong);
+ }
+ }
+ } catch (NullPointerException ignored) {
+ }
+ }
+ }
+
+ private class LibraryTreeCellRenderer implements TreeCellRenderer {
+
+ private final JLabel label;
+ private final Icon missingIcon = new ImageIcon(JTreeLibrary.class.getClassLoader().getResource("missing.gif"));
+
+ LibraryTreeCellRenderer() {
+ label = new JLabel();
+ }
+
+ @Override
+ public java.awt.Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
+ Object o = ((DefaultMutableTreeNode) value).getUserObject();
+ label.setIcon(null);
+ if (o != null) {
+ label.setText(o.toString());
+ if (o instanceof Album) {
+ Album album = (Album) o;
+ label.setIcon(missingIcon);
+ album.getAlbumArt().ifPresent(x -> label.setIcon(new ImageIcon(x)));
+ }
+ }
+ return label;
+ }
+ }
+}
diff --git a/src/main/java/musicplayer/model/HasSongs.java b/src/main/java/musicplayer/model/HasSongs.java
index 836bf16..21538ea 100644
--- a/src/main/java/musicplayer/model/HasSongs.java
+++ b/src/main/java/musicplayer/model/HasSongs.java
@@ -2,7 +2,7 @@ package musicplayer.model;
import java.util.Set;
-public interface HasSongs {
+public interface HasSongs extends IDBType {
Set getSongs();
}
diff --git a/src/main/java/musicplayer/player/GStreamerPlayer.java b/src/main/java/musicplayer/player/GStreamerPlayer.java
index eb4ecaf..484012e 100644
--- a/src/main/java/musicplayer/player/GStreamerPlayer.java
+++ b/src/main/java/musicplayer/player/GStreamerPlayer.java
@@ -13,7 +13,7 @@ import org.gstreamer.elements.PlayBin2;
import java.io.File;
import java.util.Optional;
-class GStreamerPlayer implements IPlayer{
+public class GStreamerPlayer implements IPlayer{
private final PlayBin2 playBin;
diff --git a/src/main/java/musicplayer/player/IPlayer.java b/src/main/java/musicplayer/player/IPlayer.java
index 8a934b2..063a430 100644
--- a/src/main/java/musicplayer/player/IPlayer.java
+++ b/src/main/java/musicplayer/player/IPlayer.java
@@ -1,12 +1,10 @@
package musicplayer.player;
-import com.google.inject.ImplementedBy;
import musicplayer.StartPlayingException;
import musicplayer.model.Song;
import java.util.Optional;
-@ImplementedBy(GStreamerPlayer.class)
public interface IPlayer {
void playSong(Optional inputSong) throws StartPlayingException;
void stop();
diff --git a/src/main/java/musicplayer/playlist/IPlaylist.java b/src/main/java/musicplayer/playlist/IPlaylist.java
new file mode 100644
index 0000000..fbd724b
--- /dev/null
+++ b/src/main/java/musicplayer/playlist/IPlaylist.java
@@ -0,0 +1,24 @@
+package musicplayer.playlist;
+
+import musicplayer.model.Song;
+
+import java.util.List;
+import java.util.Optional;
+
+public interface IPlaylist {
+ void addSong(Song song);
+ Optional getFirst();
+ Optional getNext(Song currentSong);
+ Optional getPrevious(Song currentSong);
+ Optional get(int index);
+ Optional getActive();
+ List getSongList();
+ int getIndex(Song song);
+ void delete(Song song);
+ void delete(int index);
+ void delete(int[] indices);
+ void deleteAll();
+ boolean isEmpty();
+ void setPlayingSong(Song song);
+
+}
diff --git a/src/main/java/musicplayer/playlist/JTablePlaylist.java b/src/main/java/musicplayer/playlist/JTablePlaylist.java
new file mode 100644
index 0000000..9c1411a
--- /dev/null
+++ b/src/main/java/musicplayer/playlist/JTablePlaylist.java
@@ -0,0 +1,295 @@
+package musicplayer.playlist;
+
+import musicplayer.model.Song;
+
+import javax.swing.*;
+import javax.swing.table.AbstractTableModel;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+public class JTablePlaylist extends JScrollPane implements IPlaylist {
+
+ private final PlaylistTableModel playlistTableModel;
+ private final JTable playList;
+
+ public JTablePlaylist(){
+ playList = new JTable();
+ playlistTableModel = new PlaylistTableModel();
+ this.setViewportView(playList);
+ setupMouse();
+ playList.setRowSelectionAllowed(true);
+ playList.setModel(playlistTableModel);
+ playList.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
+ playList.getColumnModel().getColumn(0).setPreferredWidth(10);
+ playList.getColumnModel().getColumn(0).setMaxWidth(10);
+ playList.getColumnModel().getColumn(1).setPreferredWidth(40);
+ playList.getColumnModel().getColumn(1).setMaxWidth(40);
+ }
+
+ private void setupMouse(){
+ JPopupMenu popupMenu = new JPopupMenu();
+ JMenuItem menuItem = new JMenuItem("Remove");
+ menuItem.addActionListener((e) -> delete(playList.getSelectedRows()));
+ popupMenu.add(menuItem);
+ playList.setComponentPopupMenu(popupMenu);
+ popupMenu = new JPopupMenu();
+ menuItem = new JMenuItem("Clear all");
+ menuItem.addActionListener((e) -> deleteAll());
+ popupMenu.add(menuItem);
+ this.setComponentPopupMenu(popupMenu);
+ this.setViewportView(playList);
+ }
+
+ @Override
+ public void addSong(Song song) {
+ playlistTableModel.addSong(song);
+ playList.revalidate();
+ }
+
+ @Override
+ public Optional getFirst() {
+ return playlistTableModel.getFirst();
+ }
+
+ @Override
+ public Optional getNext(Song currentSong) {
+ return playlistTableModel.nextSong(currentSong);
+ }
+
+ @Override
+ public Optional getPrevious(Song currentSong) {
+ return playlistTableModel.previous(currentSong);
+ }
+
+ @Override
+ public Optional get(int index) {
+ return playlistTableModel.getSong(index);
+ }
+
+ @Override
+ public Optional getActive() {
+ if (playList.getRowCount() > 0) {
+ if (playList.getSelectedRowCount() > 0){
+ return get(playList.getSelectedRow());
+ }
+ }
+ return getFirst();
+ }
+
+ @Override
+ public List getSongList() {
+ return playlistTableModel.getSongList();
+ }
+
+ @Override
+ public int getIndex(Song song) {
+ return playlistTableModel.getSongIndex(song);
+ }
+
+ @Override
+ public void delete(Song song) {
+ playlistTableModel.removeSong(song);
+ }
+
+ @Override
+ public void delete(int index) {
+ playlistTableModel.removeSong(index);
+ }
+
+ @Override
+ public void delete(int[] indices) {
+ playlistTableModel.removeSong(indices);
+ }
+
+ @Override
+ public void deleteAll() {
+ playlistTableModel.removeAll();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return playlistTableModel.isEmpty();
+ }
+
+ @Override
+ public void setPlayingSong(Song song){
+ int index = playlistTableModel.getSongIndex(song);
+ if(index >= 0)
+ SwingUtilities.invokeLater(() -> playList.setRowSelectionInterval(index, index));
+ playlistTableModel.setPlayingRow(index);
+ }
+
+ private class PlaylistTableModel extends AbstractTableModel {
+
+ private List songList = new ArrayList<>();
+ private int playingRow = -1; // -1 means no song is currently playing.
+
+ public PlaylistTableModel(){ }
+
+ @Override
+ public int getRowCount() {
+ return songList.size();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return 5;
+ }
+
+ @Override
+ public Object getValueAt(int rowIndex, int columnIndex) {
+ Object o = "?";
+ Song song = songList.get(rowIndex);
+ switch (columnIndex){
+ case 0: o = (playingRow == rowIndex) ? "▶" : " "; break;
+ case 1: o = song.getTrackNumber(); break;
+ case 2: o = song.getTitle(); break;
+ case 3: o = song.getArtist(); break;
+ case 4: o = song.getAlbum(); break;
+ }
+ return o;
+ }
+
+ @Override
+ public Class> getColumnClass(int columnIndex) {
+ return Song.class;
+ }
+
+ @Override
+ public String getColumnName(int column) {
+ String name = "";
+ switch (column){
+ case 0 : name = " "; break;
+ case 1: name = "Track"; break;
+ case 2: name = "Title"; break;
+ case 3: name = "Artist"; break;
+ case 4: name = "Album"; break;
+ }
+ return name;
+ }
+
+ /**
+ * Add a song to the current playlist.
+ * @param song Song to add to the playlist.
+ */
+ public void addSong(Song song){
+ songList.add(song);
+ fireTableDataChanged();
+ }
+
+ /**
+ * @return The first song in the playlist.
+ */
+ public Optional getFirst(){
+ return songList.size() > 0 ? Optional.of(songList.get(0)) : Optional.empty();
+ }
+
+ /**
+ * Get the next song in the playlist. (Wraps around)
+ * @param song The current song.
+ * @return The song after the current song.
+ */
+ public Optional nextSong(Song song){
+ int index = songList.indexOf(song) + 1;
+ if(index >= songList.size()) index = 0;
+ return songList.size() > 0 ? Optional.of(songList.get(index)) : Optional.empty();
+ }
+
+ /**
+ * Get the previous song in the playlist. (Wraps around)
+ * @param song The current song.
+ * @return The song before the current song.
+ */
+ public Optional previous(Song song){
+ int index = songList.indexOf(song) - 1;
+ if(index < 0) index = songList.size() - 1;
+ return songList.size() > 0 ? Optional.of(songList.get(index)) : Optional.empty();
+ }
+
+ /**
+ * @param song Song to find in the current playlist.
+ * @return Index of song in the playlist.
+ */
+ public int getSongIndex(Song song){
+ return song != null ? songList.indexOf(song) : -1;
+ }
+
+ /**
+ * Remove the song at index from the playlist.
+ * @param index List index of the song to be removed.
+ */
+ public void removeSong(int index){
+ if(songList.size() > index && index >= 0){
+ songList.remove(index);
+ fireTableRowsDeleted(index, index);
+ if(index < playingRow)
+ playingRow--;
+ else if(playingRow == index)
+ playingRow = -1;
+ }
+ }
+
+ /**
+ * Remove a number of songs from the playlist.
+ * @param index Indices of the songs to be removed.
+ */
+ public void removeSong(int[] index){
+ List indices = IntStream.of(index).boxed().collect(Collectors.toList());
+ Collections.reverse(indices); // Do removals in reverse order to prevent mis-deletion.
+ indices.forEach(this::removeSong);
+ }
+
+ /**
+ * Remove a song from the playlist.
+ * @param song Song to be removed.
+ */
+ public void removeSong(Song song){
+ songList.remove(song);
+ fireTableDataChanged();
+ }
+
+ /**
+ * @return Is the playlist currently empty.
+ */
+ public boolean isEmpty(){
+ return songList.isEmpty();
+ }
+
+ /**
+ * Remove all songs from the playlist.
+ */
+ public void removeAll(){
+ songList.clear();
+ fireTableDataChanged();
+ }
+
+ /**
+ * Get a Song from the playlist based on its index.
+ * @param index Index of the Song to get.
+ * @return Song object at index.
+ */
+ public Optional getSong(int index){
+ return songList.size() > 0 && index >= 0 && index < songList.size() ? Optional.of(songList.get(index)) : Optional.empty();
+ }
+
+ /**
+ * Set the row at index to have a playing icon.
+ * @param index Row index to display as playing.
+ */
+ public void setPlayingRow(int index){
+ playingRow = index;
+ fireTableDataChanged();
+ }
+
+ /**
+ * @return Songs currently in the playlist.
+ */
+ public List getSongList(){
+ return songList;
+ }
+ }
+}
diff --git a/src/main/java/musicplayer/swingmodels/LibraryListModel.java b/src/main/java/musicplayer/swingmodels/LibraryListModel.java
deleted file mode 100644
index 162b442..0000000
--- a/src/main/java/musicplayer/swingmodels/LibraryListModel.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package musicplayer.swingmodels;
-
-import javax.swing.*;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-public class LibraryListModel extends AbstractListModel {
-
- private List libraryFolders = new ArrayList<>();
-
- @Override
- public int getSize() {
- return libraryFolders.size();
- }
-
- @Override
- public T getElementAt(int index) {
- return libraryFolders.get(index);
- }
-
- /**
- * Add a directory to the list of directories used for library indexing.
- * @param folder Directory to add.
- */
- public void addFolder(T folder){
- if(folder.exists() && folder.isDirectory()) {
- libraryFolders.add(folder);
- fireAllContentsChanged();
- }
- }
-
- /**
- * @return List of directories currently used for library indexing.
- */
- public List currentFolderList(){
- return new ArrayList<>(libraryFolders); // Copy? Don't want modification via here.
- }
-
- /**
- * Check if this File already exists in libraryFolders.
- * @param file File to find.
- * @return Does file exist in libraryFolders.
- */
- public boolean contains(T file){
- return libraryFolders.contains(file);
- }
-
- /**
- * Set the list contained in libraryFolders.
- * @param folderList New directory list.
- */
- public void setFolderList(List folderList){
- if(folderList != null && folderList.size() > 0)
- libraryFolders = new ArrayList<>(folderList);
- else
- libraryFolders = new ArrayList<>();
- fireAllContentsChanged();
- }
-
- /**
- * Remove a directory from libraryFolders.
- * @param file Directory to be removed.
- */
- public void removeFile(T file){
- libraryFolders.remove(file);
- fireAllContentsChanged();
- }
-
- /**
- * Fire that libraryFolders contents has changed.
- */
- private void fireAllContentsChanged(){
- fireContentsChanged(this, 0, libraryFolders.size() == 0 ? 0 : libraryFolders.size() - 1);
- }
-}
diff --git a/src/main/java/musicplayer/swingmodels/PlaylistTableModel.java b/src/main/java/musicplayer/swingmodels/PlaylistTableModel.java
deleted file mode 100644
index dd7867c..0000000
--- a/src/main/java/musicplayer/swingmodels/PlaylistTableModel.java
+++ /dev/null
@@ -1,182 +0,0 @@
-package musicplayer.swingmodels;
-
-import musicplayer.model.Song;
-
-import javax.swing.table.AbstractTableModel;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-import java.util.stream.IntStream;
-
-public class PlaylistTableModel extends AbstractTableModel {
-
- private List songList;
- private int playingRow = -1; // -1 means no song is currently playing.
-
- public PlaylistTableModel(List songList){
- this.songList = songList;
- }
-
- @Override
- public int getRowCount() {
- return songList.size();
- }
-
- @Override
- public int getColumnCount() {
- return 5;
- }
-
- @Override
- public Object getValueAt(int rowIndex, int columnIndex) {
- Object o = "?";
- Song song = songList.get(rowIndex);
- switch (columnIndex){
- case 0: o = (playingRow == rowIndex) ? "▶" : " "; break;
- case 1: o = song.getTrackNumber(); break;
- case 2: o = song.getTitle(); break;
- case 3: o = song.getArtist(); break;
- case 4: o = song.getAlbum(); break;
- }
- return o;
- }
-
- @Override
- public Class> getColumnClass(int columnIndex) {
- return Song.class;
- }
-
- @Override
- public String getColumnName(int column) {
- String name = "";
- switch (column){
- case 0 : name = " "; break;
- case 1: name = "Track"; break;
- case 2: name = "Title"; break;
- case 3: name = "Artist"; break;
- case 4: name = "Album"; break;
- }
- return name;
- }
-
- /**
- * Add a song to the current playlist.
- * @param song Song to add to the playlist.
- */
- public void addSong(Song song){
- songList.add(song);
- fireTableDataChanged();
- }
-
- /**
- * @return The first song in the playlist.
- */
- public Optional getFirst(){
- return songList.size() > 0 ? Optional.of(songList.get(0)) : Optional.empty();
- }
-
- /**
- * Get the next song in the playlist. (Wraps around)
- * @param song The current song.
- * @return The song after the current song.
- */
- public Optional nextSong(Song song){
- int index = songList.indexOf(song) + 1;
- if(index >= songList.size()) index = 0;
- return songList.size() > 0 ? Optional.of(songList.get(index)) : Optional.empty();
- }
-
- /**
- * Get the previous song in the playlist. (Wraps around)
- * @param song The current song.
- * @return The song before the current song.
- */
- public Optional previous(Song song){
- int index = songList.indexOf(song) - 1;
- if(index < 0) index = songList.size() - 1;
- return songList.size() > 0 ? Optional.of(songList.get(index)) : Optional.empty();
- }
-
- /**
- * @param song Song to find in the current playlist.
- * @return Index of song in the playlist.
- */
- public int getSongIndex(Song song){
- return songList.indexOf(song);
- }
-
- /**
- * Remove the song at index from the playlist.
- * @param index List index of the song to be removed.
- */
- public void removeSong(int index){
- if(songList.size() > index && index >= 0){
- songList.remove(index);
- fireTableRowsDeleted(index, index);
- if(index < playingRow)
- playingRow--;
- else if(playingRow == index)
- playingRow = -1;
- }
- }
-
- /**
- * Remove a number of songs from the playlist.
- * @param index Indices of the songs to be removed.
- */
- public void removeSong(int[] index){
- List indices = IntStream.of(index).boxed().collect(Collectors.toList());
- Collections.reverse(indices); // Do removals in reverse order to prevent mis-deletion.
- indices.forEach(this::removeSong);
- }
-
- /**
- * Remove a song from the playlist.
- * @param song Song to be removed.
- */
- public void removeSong(Song song){
- songList.remove(song);
- fireTableDataChanged();
- }
-
- /**
- * @return Is the playlist currently empty.
- */
- public boolean isEmpty(){
- return songList.isEmpty();
- }
-
- /**
- * Remove all songs from the playlist.
- */
- public void removeAll(){
- songList.clear();
- fireTableDataChanged();
- }
-
- /**
- * Get a Song from the playlist based on its index.
- * @param index Index of the Song to get.
- * @return Song object at index.
- */
- public Optional getSong(int index){
- return songList.size() > 0 && index >= 0 && index < songList.size() ? Optional.of(songList.get(index)) : Optional.empty();
- }
-
- /**
- * Set the row at index to have a playing icon.
- * @param index Row index to display as playing.
- */
- public void setPlayingRow(int index){
- playingRow = index;
- fireTableDataChanged();
- }
-
- /**
- * @return Songs currently in the playlist.
- */
- public List getSongList(){
- return songList;
- }
-}
diff --git a/src/main/java/musicplayer/ConfigManager.java b/src/main/java/musicplayer/util/ConfigManager.java
similarity index 99%
rename from src/main/java/musicplayer/ConfigManager.java
rename to src/main/java/musicplayer/util/ConfigManager.java
index 6d29cd2..2b8f6f5 100644
--- a/src/main/java/musicplayer/ConfigManager.java
+++ b/src/main/java/musicplayer/util/ConfigManager.java
@@ -1,4 +1,4 @@
-package musicplayer;
+package musicplayer.util;
import java.io.File;
import java.io.FileInputStream;