diff --git a/pom.xml b/pom.xml
index a76065b..e87628c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,7 +32,7 @@
true
lib/
- musicplayer.PlayerGUI
+ musicplayer.Main
diff --git a/src/main/java/musicplayer/Main.java b/src/main/java/musicplayer/Main.java
new file mode 100644
index 0000000..ef62669
--- /dev/null
+++ b/src/main/java/musicplayer/Main.java
@@ -0,0 +1,29 @@
+package musicplayer;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import musicplayer.swingui.PlayerGUI;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class Main {
+
+ public static void main(String[] args) {
+ runSwingUIMode();
+ }
+
+ /**
+ * Start the application with injection bindings contained in SwingUIModule
+ */
+ private static void runSwingUIMode() {
+ 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);
+ frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+ frame.pack();
+ frame.setVisible(true);
+ }
+}
diff --git a/src/main/java/musicplayer/SwingUIModule.java b/src/main/java/musicplayer/SwingUIModule.java
index 76e3f88..f9377bc 100644
--- a/src/main/java/musicplayer/SwingUIModule.java
+++ b/src/main/java/musicplayer/SwingUIModule.java
@@ -2,6 +2,7 @@ package musicplayer;
import com.google.inject.AbstractModule;
import com.google.inject.Singleton;
+import musicplayer.callbacks.PlayerCallbackInterface;
import musicplayer.db.HibernateDatabase;
import musicplayer.db.IDatabase;
import musicplayer.library.ILibrary;
@@ -10,6 +11,7 @@ import musicplayer.player.GStreamerPlayer;
import musicplayer.player.IPlayer;
import musicplayer.playlist.IPlaylist;
import musicplayer.playlist.JTablePlaylist;
+import musicplayer.swingui.PlayerGUI;
class SwingUIModule extends AbstractModule {
@@ -19,5 +21,6 @@ class SwingUIModule extends AbstractModule {
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);
+ bind(PlayerCallbackInterface.class).to(PlayerGUI.class);
}
}
diff --git a/src/main/java/musicplayer/callbacks/PlayerCallbackInterface.java b/src/main/java/musicplayer/callbacks/PlayerCallbackInterface.java
index c8a562f..4fa0a38 100644
--- a/src/main/java/musicplayer/callbacks/PlayerCallbackInterface.java
+++ b/src/main/java/musicplayer/callbacks/PlayerCallbackInterface.java
@@ -1,9 +1,5 @@
package musicplayer.callbacks;
-import com.google.inject.ImplementedBy;
-import musicplayer.PlayerGUI;
-
-@ImplementedBy(PlayerGUI.class)
public interface PlayerCallbackInterface {
/**
diff --git a/src/main/java/musicplayer/swingui/ControlBar.java b/src/main/java/musicplayer/swingui/ControlBar.java
new file mode 100644
index 0000000..908d691
--- /dev/null
+++ b/src/main/java/musicplayer/swingui/ControlBar.java
@@ -0,0 +1,119 @@
+package musicplayer.swingui;
+
+import musicplayer.StartPlayingException;
+import musicplayer.player.IPlayer;
+import musicplayer.util.Icons;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class ControlBar extends JPanel {
+ IPlayer player;
+
+ public ControlBar(IPlayer player){
+ this.player = player;
+ setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
+ add(playButton());
+ add(pauseButton());
+ add(stopButton());
+ add(previousButton());
+ add(nextButton());
+ add(repeatButton());
+ }
+
+ private JButton playButton(){
+ JButton playButton = new ControlButton(Icons.playIcon);
+ playButton.setName("playButton");
+ playButton.setMnemonic('P');
+ playButton.addActionListener(e -> {
+ try {
+ player.play();
+ } catch (StartPlayingException e1) {
+ showError(e1);
+ }
+ });
+ return playButton;
+ }
+
+ private JButton pauseButton(){
+ JButton pauseButton = new ControlButton(Icons.pauseIcon);
+ pauseButton.setMnemonic('E');
+ pauseButton.addActionListener(e -> player.pause());
+ return pauseButton;
+ }
+
+ private JButton stopButton(){
+ JButton stopButton = new ControlButton(Icons.stopIcon);
+ stopButton.setMnemonic('S');
+ stopButton.addActionListener(e -> player.stop());
+ return stopButton;
+ }
+
+ private JButton previousButton(){
+ JButton previousButton = new ControlButton(Icons.prevIcon);
+ previousButton.setMnemonic('R');
+ previousButton.addActionListener(e -> {
+ try {
+ player.previous();
+ } catch (StartPlayingException e1) {
+ showError(e1);
+ }
+ });
+ return previousButton;
+ }
+
+ private JButton nextButton(){
+ JButton nextButton = new ControlButton(Icons.nextIcon);
+ nextButton.setMnemonic('N');
+ nextButton.addActionListener(e -> {
+ try {
+ player.next();
+ } catch (StartPlayingException e1) {
+ showError(e1);
+ }
+ });
+ return nextButton;
+ }
+
+ private JButton repeatButton(){
+ JButton repeatButton = new ControlButton(Icons.repeatMultiIcon);
+ repeatButton.setToolTipText("Repeating playlist.");
+ repeatButton.addActionListener(e -> {
+ player.setRepeat(!player.isRepeating());
+ if(player.isRepeating()) {
+ repeatButton.setIcon(Icons.repeatSingleIcon);
+ repeatButton.setToolTipText("Repeating song.");
+ }
+ else {
+ repeatButton.setIcon(Icons.repeatMultiIcon);
+ repeatButton.setToolTipText("Repeating playlist.");
+ }
+ });
+ return repeatButton;
+ }
+
+
+ private void showError(StartPlayingException ex) {
+ JOptionPane.showMessageDialog(this, "Failed to play " + ex.getSong().toString() + ".\n" +
+ "If file path contains non-ASCII characters, please restart the application with UTF-8 encoding.");
+ }
+
+ /**
+ * Button for performing some control action on the player
+ */
+ private class ControlButton extends JButton {
+ public ControlButton(Icon icon){
+ setIcon(icon);
+ }
+
+ final Dimension buttonSize = new Dimension(40, 40);
+ @Override
+ public Dimension getPreferredSize() {
+ return buttonSize;
+ }
+ @Override
+ public Dimension getMaximumSize() {
+ return buttonSize;
+ }
+ }
+}
diff --git a/src/main/java/musicplayer/LibraryConfigGUI.java b/src/main/java/musicplayer/swingui/LibraryConfigGUI.java
similarity index 99%
rename from src/main/java/musicplayer/LibraryConfigGUI.java
rename to src/main/java/musicplayer/swingui/LibraryConfigGUI.java
index ba8687b..c3fee9f 100644
--- a/src/main/java/musicplayer/LibraryConfigGUI.java
+++ b/src/main/java/musicplayer/swingui/LibraryConfigGUI.java
@@ -1,4 +1,4 @@
-package musicplayer;
+package musicplayer.swingui;
import musicplayer.util.ConfigManager;
diff --git a/src/main/java/musicplayer/PlayerGUI.java b/src/main/java/musicplayer/swingui/PlayerGUI.java
similarity index 77%
rename from src/main/java/musicplayer/PlayerGUI.java
rename to src/main/java/musicplayer/swingui/PlayerGUI.java
index 3869875..b68e4bf 100644
--- a/src/main/java/musicplayer/PlayerGUI.java
+++ b/src/main/java/musicplayer/swingui/PlayerGUI.java
@@ -1,14 +1,12 @@
-package musicplayer;
+package musicplayer.swingui;
-import com.google.inject.Guice;
import com.google.inject.Inject;
-import com.google.inject.Injector;
+import musicplayer.StartPlayingException;
import musicplayer.callbacks.PlayerCallbackInterface;
import musicplayer.library.ILibrary;
import musicplayer.player.IPlayer;
import musicplayer.playlist.IPlaylist;
import musicplayer.util.ConfigManager;
-import musicplayer.util.Icons;
import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
import org.jnativehook.keyboard.NativeKeyEvent;
@@ -61,7 +59,7 @@ public class PlayerGUI extends JPanel implements PlayerCallbackInterface {
}
});
Runtime.getRuntime().addShutdownHook(hookShutdown);
- } catch(RuntimeException | NativeHookException |UnsatisfiedLinkError ex){
+ } catch (RuntimeException | NativeHookException | UnsatisfiedLinkError ex) {
System.out.println("Keyboard hook failed, global shortcuts will not work this session.");
System.out.println(ex.getMessage());
System.out.println(Arrays.toString(ex.getStackTrace()));
@@ -69,17 +67,6 @@ public class PlayerGUI extends JPanel implements PlayerCallbackInterface {
library.refreshLibrary();
}
- public static void main(String[] args) {
- 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);
- frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
- frame.pack();
- frame.setVisible(true);
- }
-
/**
* Set the value of seekBar.
*
@@ -87,7 +74,7 @@ public class PlayerGUI extends JPanel implements PlayerCallbackInterface {
*/
public void setSeekBarPosition(int position) {
SwingUtilities.invokeLater(() -> {
- if(!seeking.get())
+ if (!seeking.get())
seekBar.setValue(position);
});
}
@@ -101,7 +88,7 @@ public class PlayerGUI extends JPanel implements PlayerCallbackInterface {
SwingUtilities.invokeLater(() -> seekBar.setMaximum(seconds));
}
- private void showError(StartPlayingException ex){
+ private void showError(StartPlayingException ex) {
JOptionPane.showMessageDialog(this, "Failed to play " + ex.getSong().toString() + ".\n" +
"If file path contains non-ASCII characters, please restart the application with UTF-8 encoding.");
}
@@ -123,9 +110,9 @@ public class PlayerGUI extends JPanel implements PlayerCallbackInterface {
this.setLayout(new BorderLayout(0, 0));
final JSplitPane libraryAndPlaylistPane = new JSplitPane();
libraryAndPlaylistPane.setDividerLocation(240);
- libraryAndPlaylistPane.setRightComponent((JScrollPane)playlist);
+ libraryAndPlaylistPane.setRightComponent((JScrollPane) playlist);
this.add(libraryAndPlaylistPane, BorderLayout.CENTER);
- libraryAndPlaylistPane.setLeftComponent((JPanel)library);
+ libraryAndPlaylistPane.setLeftComponent((JPanel) library);
final JPanel controlPane = new JPanel();
controlPane.setLayout(new BorderLayout(0, 0));
this.add(controlPane, BorderLayout.SOUTH);
@@ -144,52 +131,7 @@ public class PlayerGUI extends JPanel implements PlayerCallbackInterface {
private JToolBar createControlButtons() {
JToolBar toolBar = new JToolBar();
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 -> {
- try {
- player.play();
- } catch (StartPlayingException e1) {
- showError(e1);
- }
- });
- controlBar.add(playButton);
- JButton pauseButton = new JButton();
- pauseButton.setIcon(Icons.pauseIcon);
- pauseButton.setMnemonic('E');
- pauseButton.addActionListener(e -> player.pause());
- controlBar.add(pauseButton);
- JButton stopButton = new JButton();
- stopButton.setIcon(Icons.stopIcon);
- stopButton.setMnemonic('S');
- stopButton.addActionListener(e -> player.stop());
- controlBar.add(stopButton);
- JButton previousButton = new JButton();
- previousButton.setIcon(Icons.prevIcon);
- previousButton.setMnemonic('R');
- previousButton.addActionListener(e -> {
- try {
- player.previous();
- } catch (StartPlayingException e1) {
- showError(e1);
- }
- });
- controlBar.add(previousButton);
- JButton nextButton = new JButton();
- nextButton.setIcon(Icons.nextIcon);
- nextButton.setMnemonic('N');
- nextButton.addActionListener(e -> {
- try {
- player.next();
- } catch (StartPlayingException e1) {
- showError(e1);
- }
- });
- controlBar.add(nextButton);
- toolBar.add(controlBar);
+ toolBar.add(new ControlBar(player));
toolBar.add(createVolumeControls());
return toolBar;
}
@@ -252,7 +194,7 @@ public class PlayerGUI extends JPanel implements PlayerCallbackInterface {
FileNameExtensionFilter m3uExtensionFilter = new FileNameExtensionFilter("m3u playlist", "m3u");
menuItem = new JMenuItem("Save Playlist");
menuItem.addActionListener(e -> {
- if(!playlist.isEmpty()) {
+ if (!playlist.isEmpty()) {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogType(JFileChooser.SAVE_DIALOG);
fileChooser.setSelectedFile(new File("playlist.m3u"));
@@ -276,7 +218,7 @@ public class PlayerGUI extends JPanel implements PlayerCallbackInterface {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogType(JFileChooser.OPEN_DIALOG);
fileChooser.setFileFilter(m3uExtensionFilter);
- if(fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION){
+ if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
IPlaylist.readPlaylistFromFile(fileChooser.getSelectedFile()).stream().forEach(playlist::addSong);
}
});
@@ -288,7 +230,7 @@ public class PlayerGUI extends JPanel implements PlayerCallbackInterface {
JMenu helpMenu = new JMenu("Help");
menuItem = new JMenuItem("About");
menuItem.addActionListener(e -> {
- try(BufferedReader reader = new BufferedReader(new InputStreamReader(PlayerGUI.class.getClassLoader().getResourceAsStream("LICENSE.txt")))) {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(PlayerGUI.class.getClassLoader().getResourceAsStream("LICENSE.txt")))) {
JEditorPane messagePane = new JEditorPane("text/html", reader.lines().collect(Collectors.toList()).get(0));
messagePane.setEditable(false);
messagePane.addHyperlinkListener(hl ->
@@ -319,10 +261,14 @@ public class PlayerGUI extends JPanel implements PlayerCallbackInterface {
*/
private class SeekbarMouseListener implements MouseListener {
@Override
- public void mouseClicked(MouseEvent e) { seeking.set(true); }
+ public void mouseClicked(MouseEvent e) {
+ seeking.set(true);
+ }
@Override
- public void mousePressed(MouseEvent e) { seeking.set(true); }
+ public void mousePressed(MouseEvent e) {
+ seeking.set(true);
+ }
@Override
public void mouseReleased(MouseEvent e) {
@@ -331,10 +277,12 @@ public class PlayerGUI extends JPanel implements PlayerCallbackInterface {
}
@Override
- public void mouseEntered(MouseEvent e) {}
+ public void mouseEntered(MouseEvent e) {
+ }
@Override
- public void mouseExited(MouseEvent e) {}
+ public void mouseExited(MouseEvent e) {
+ }
}
private class GlobalKeyboardShortcuts implements NativeKeyListener {
@@ -346,19 +294,19 @@ public class PlayerGUI extends JPanel implements PlayerCallbackInterface {
@Override
public void nativeKeyPressed(NativeKeyEvent nativeKeyEvent) {
- if(nativeKeyEvent.getKeyCode() == NativeKeyEvent.VC_ALT_L)
+ if (nativeKeyEvent.getKeyCode() == NativeKeyEvent.VC_ALT_L)
modified = true;
}
@Override
public void nativeKeyReleased(NativeKeyEvent nativeKeyEvent) {
- if(nativeKeyEvent.getKeyCode() == NativeKeyEvent.VC_ALT_L)
+ if (nativeKeyEvent.getKeyCode() == NativeKeyEvent.VC_ALT_L)
modified = false;
}
@Override
public void nativeKeyTyped(NativeKeyEvent nativeKeyEvent) {
- if(modified){
+ if (modified) {
try {
switch (nativeKeyEvent.getRawCode()) {
case playPause:
@@ -378,10 +326,10 @@ public class PlayerGUI extends JPanel implements PlayerCallbackInterface {
player.next();
break;
}
- }catch (StartPlayingException ex){
+ } catch (StartPlayingException ex) {
showError(ex);
}
}
}
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/musicplayer/util/ConfigManager.java b/src/main/java/musicplayer/util/ConfigManager.java
index 2b8f6f5..6401aa5 100644
--- a/src/main/java/musicplayer/util/ConfigManager.java
+++ b/src/main/java/musicplayer/util/ConfigManager.java
@@ -42,11 +42,7 @@ public final class ConfigManager {
*/
public static void saveLibraryDirectories(List folderList){
librarySettings.setProperty(foldersKey, folderList.stream().map(File::toString).collect(Collectors.joining(",")));
- try(FileWriter fileWriter = new FileWriter(settingsFilename)){
- librarySettings.store(fileWriter, "");
- } catch (IOException e) {
- e.printStackTrace();
- }
+ writeSettings();
}
/**
@@ -61,11 +57,7 @@ public final class ConfigManager {
} catch (IOException ignored) {
}
librarySettings.setProperty(databaseKey, "musicDB");
- try(FileWriter fileWriter = new FileWriter(settingsFilename)){
- librarySettings.store(fileWriter, "");
- } catch (IOException e) {
- e.printStackTrace();
- }
+ writeSettings();
return "jdbc:hsqldb:file:" + new File("musicDB").getAbsolutePath() + ";shutdown=true"; //Use relative file by default
}
@@ -89,11 +81,7 @@ public final class ConfigManager {
*/
public static void setLastDisplayTypeIndex(Integer index){
librarySettings.setProperty(libraryDisplayKey, index.toString());
- try(FileWriter fileWriter = new FileWriter(settingsFilename)){
- librarySettings.store(fileWriter, "");
- } catch (IOException e) {
- e.printStackTrace();
- }
+ writeSettings();
}
public static int getLastVolume(){
@@ -109,6 +97,10 @@ public final class ConfigManager {
public static void setLastVolume(Integer index){
librarySettings.setProperty(lastVolumeKey, index.toString());
+ writeSettings();
+ }
+
+ private static void writeSettings(){
try(FileWriter fileWriter = new FileWriter(settingsFilename)){
librarySettings.store(fileWriter, "");
} catch (IOException e) {
diff --git a/src/main/java/musicplayer/util/Icons.java b/src/main/java/musicplayer/util/Icons.java
index 3784a73..41b6d72 100644
--- a/src/main/java/musicplayer/util/Icons.java
+++ b/src/main/java/musicplayer/util/Icons.java
@@ -11,4 +11,6 @@ public final class Icons {
public static final Icon stopIcon = new ImageIcon(classLoader.getResource("glyphicons-176-stop.png"));
public static final Icon prevIcon = new ImageIcon(classLoader.getResource("glyphicons-171-step-backward.png"));
public static final Icon nextIcon = new ImageIcon(classLoader.getResource("glyphicons-179-step-forward.png"));
+ public static final Icon repeatSingleIcon = new ImageIcon(classLoader.getResource("glyphicons-86-repeat.png"));
+ public static final Icon repeatMultiIcon = new ImageIcon(classLoader.getResource("glyphicons-83-roundabout.png"));
}
diff --git a/src/main/resources/glyphicons-83-roundabout.png b/src/main/resources/glyphicons-83-roundabout.png
new file mode 100644
index 0000000..51c55a6
Binary files /dev/null and b/src/main/resources/glyphicons-83-roundabout.png differ
diff --git a/src/main/resources/glyphicons-86-repeat.png b/src/main/resources/glyphicons-86-repeat.png
new file mode 100644
index 0000000..6b0cc63
Binary files /dev/null and b/src/main/resources/glyphicons-86-repeat.png differ