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