package net.pms.newgui; import java.awt.*; import java.awt.event.*; import java.io.File; import java.net.URL; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.plaf.metal.MetalIconFactory; import net.pms.Messages; import net.pms.util.BasicPlayer; import net.pms.util.UMSUtils; import org.apache.commons.lang.StringUtils; public class PlayerControlPanel extends JPanel implements ActionListener { private static final long serialVersionUID = 8972730138916895247L; private BasicPlayer.Logical player; @SuppressWarnings("unused") private AbstractAction add, remove, clear, play, pause, stop, next, prev, forward, rewind, mute, volume, seturi, excl; private Button position; private JSlider volumeSlider; private JTextField uri; private JComboBox uris; private boolean edited, playing; private String lasturi; private File pwd; private boolean playControl, volumeControl, expanded; int sliding; private static ImageIcon addIcon, removeIcon, clearIcon, playIcon, pauseIcon, stopIcon, fwdIcon, rewIcon, nextIcon, prevIcon, volumeIcon, muteIcon, sliderIcon; public PlayerControlPanel(final BasicPlayer player) { if (playIcon == null) { loadIcons(); } this.player = (BasicPlayer.Logical)player; player.connect(this); int controls = player.getControls(); playControl = (controls & BasicPlayer.PLAYCONTROL) != 0; volumeControl = (controls & BasicPlayer.VOLUMECONTROL) != 0; expanded = true; sliding = 0; try { pwd = new File(player.getState().uri).getParentFile(); } catch (Exception e) { pwd = new File(""); } setPreferredSize(new Dimension(530, 70)); setLayout(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; c.insets = new Insets(6, 0, 0, 0); c.gridx = 0; c.gridy = 0; c.weightx = c.weighty = 1.0; Toolbar ctrl = new Toolbar(); ctrl.setLayout(new BoxLayout(ctrl, BoxLayout.X_AXIS)); if (volumeControl) { if (playControl) { addVolumeControls(ctrl); } else { ctrl.add(Box.createHorizontalGlue()); addVolumeControls(ctrl); ctrl.add(Box.createHorizontalGlue()); add(ctrl, c); } } if (playControl) { ctrl.add(Box.createHorizontalStrut(volumeControl ? 55 : 140)); addPlayControls(ctrl); ctrl.add(Box.createHorizontalGlue()); addStatus(ctrl); add(ctrl, c); c.gridy++; Toolbar uri = new Toolbar(); addUriControls(uri); add(uri, c); } player.alert(); final ActionListener self = this; getEnclosingWindow(this).addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { player.disconnect(self); } }); } public void addPlayControls(Container parent) { parent.add(new Button(36, prev = new AbstractAction("", prevIcon) { private static final long serialVersionUID = 7558487023838124078L; @Override public void actionPerformed(ActionEvent e) { player.prev(); } })); parent.add(new Button(36, rewind = new AbstractAction("", rewIcon) { private static final long serialVersionUID = -1520355550308740828L; @Override public void actionPerformed(ActionEvent e) { player.rewind(); } })); parent.add(new Button(36, play = new AbstractAction("", playIcon) { private static final long serialVersionUID = -5492279549624322429L; @Override public void actionPerformed(ActionEvent e) { setEdited(false); player.pressPlay(uri.getText(), null); } })); parent.add(new Button(36, stop = new AbstractAction("", stopIcon) { private static final long serialVersionUID = 8389133040373106061L; @Override public void actionPerformed(ActionEvent e) { player.pressStop(); } })); parent.add(new Button(36, forward = new AbstractAction("", fwdIcon) { private static final long serialVersionUID = 9017731678937164070L; @Override public void actionPerformed(ActionEvent e) { player.forward(); } })); parent.add(new Button(36, next = new AbstractAction("", nextIcon) { private static final long serialVersionUID = -2100492235066666555L; @Override public void actionPerformed(ActionEvent e) { player.next(); } })); } public void addStatus(final Container parent) { parent.add(position = new Button(new AbstractAction("") { private static final long serialVersionUID = 2L; @Override public void actionPerformed(ActionEvent e) { toggleView(parent); } })); position.setHorizontalAlignment(SwingConstants.RIGHT); position.setToolTipText(Messages.getString("PlayerControlPanel.0")); } public void addVolumeControls(Container parent) { UIDefaults defaults = UIManager.getDefaults(); Object hti = defaults.put("Slider.horizontalThumbIcon", sliderIcon); Object tb = defaults.put("Slider.trackBorder", BorderFactory.createEmptyBorder()); volumeSlider = new JSlider(SwingConstants.HORIZONTAL, 0, 100, 0); Dimension d = new Dimension(80, 20); volumeSlider.setPreferredSize(d); volumeSlider.setSize(d); volumeSlider.setMaximumSize(d); volumeSlider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { // Fire only when the slider is in motion, i.e. not during external updates if (((JSlider) e.getSource()).getValueIsAdjusting()) { player.setVolume(volumeSlider.getValue()); // For smoothness ignore external volume data until // the 3rd update after sliding has finished sliding = 3; } } }); volumeSlider.setFocusable(false); // volumeSlider.setPaintLabels(true); // volumeSlider.setLabelTable(new Hashtable<Integer, JLabel>() {{ // put(0, new JLabel("<html><b>-</b></html>")); // put(100, new JLabel("<html><b>+</b></html>")); // }}); // volumeSlider.setAlignmentX(0.25f); parent.add(volumeSlider); defaults.put("Slider.horizontalThumbIcon", hti); defaults.put("Slider.trackBorder", tb); Button muteButton = new Button(mute = new AbstractAction("", volumeIcon) { private static final long serialVersionUID = 4263195311825852854L; @Override public void actionPerformed(ActionEvent e) { player.mute(); } }); parent.add(muteButton); } public void addUriControls(Container parent) { uris = new JComboBox(player.getPlaylist()); uris.setMaximumRowCount(20); uris.setEditable(true); // limit width to available space uris.setPrototypeDisplayValue(""); uri = (JTextField) uris.getEditor().getEditorComponent(); uri.addFocusListener(new java.awt.event.FocusAdapter() { public void focusGained(java.awt.event.FocusEvent evt) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { uri.select(0, 0); } }); } }); uri.getDocument().addDocumentListener(new DocumentListener() { @Override public void changedUpdate(DocumentEvent e) { setEdited(true); if (!playing) { play.setEnabled(!StringUtils.isBlank(uri.getText())); } } @Override public void insertUpdate(DocumentEvent e) { changedUpdate(e); } @Override public void removeUpdate(DocumentEvent e) { changedUpdate(e); } }); parent.add(uris); Button a = new Button(add = new AbstractAction("", addIcon) { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { setEdited(false); player.add(-1, uri.getText(), null, null, true); } }); a.setToolTipText(Messages.getString("PlayerControlPanel.1")); parent.add(a); Button r = new Button(remove = new AbstractAction("", removeIcon) { private static final long serialVersionUID = 8732700198165912103L; @Override public void actionPerformed(ActionEvent e) { player.remove(uri.getText()); } }); r.setToolTipText(Messages.getString("PlayerControlPanel.2")); parent.add(r); Button c = new Button(clear = new AbstractAction("", clearIcon) { private static final long serialVersionUID = -2484978035031713948L; @Override public void actionPerformed(ActionEvent e) { player.clear(); } }); c.setToolTipText(Messages.getString("PlayerControlPanel.3")); parent.add(c); parent.add(new Button(new AbstractAction("", MetalIconFactory.getTreeFolderIcon()) { private static final long serialVersionUID = -2826057503405341316L; @Override public void actionPerformed(ActionEvent e) { JFileChooser fc = new JFileChooser(pwd); fc.setFileSelectionMode(JFileChooser.FILES_ONLY); if (fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) { uri.setText(fc.getSelectedFile().getPath()); setEdited(true); } pwd = fc.getCurrentDirectory(); } })); } public static Window getEnclosingWindow(Component c) { return c == null ? JOptionPane.getRootFrame() : c instanceof Window ? (Window) c : getEnclosingWindow(c.getParent()); } public BasicPlayer getPlayer() { return player; } public void setEdited(boolean b) { edited = b; updatePlaylist(); } public void updatePlaylist() { boolean empty = uris.getModel().getSize() == 0; add.setEnabled((edited || empty) && StringUtils.isNotBlank(uri.getText())); remove.setEnabled(! empty); clear.setEnabled(! empty); boolean more = uris.getModel().getSize() > 1; next.setEnabled(more); prev.setEnabled(more); edited = false; } public void refresh(BasicPlayer.State state) { if (playControl) { playing = state.playback != BasicPlayer.STOPPED; // update playback status play.putValue(Action.SMALL_ICON, state.playback == BasicPlayer.PLAYING ? pauseIcon : playIcon); stop.setEnabled(playing); forward.setEnabled(playing); rewind.setEnabled(playing); // update position position.setText(UMSUtils.playedDurationStr(state.position, state.duration)); // update uris only if meaningfully new boolean isNew = !StringUtils.isBlank(state.uri) && !state.uri.equals(lasturi); lasturi = state.uri; if (isNew) { if (edited) { player.add(-1, uri.getText(), null, null, false); setEdited(false); } uri.setText(state.name); } play.setEnabled(playing || !StringUtils.isBlank(uri.getText())); updatePlaylist(); } if (volumeControl) { // update rendering status mute.putValue(Action.SMALL_ICON, state.mute ? muteIcon : volumeIcon); volumeSlider.setEnabled(!state.mute); // ignore volume during slider motion if (--sliding < 0) { volumeSlider.setValue(state.volume); } } } @Override public void actionPerformed(final ActionEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { refresh(((BasicPlayer) e.getSource()).getState()); } }); } private static void loadIcons() { addIcon = loadIcon("/resources/images/player/add16.png"); removeIcon = loadIcon("/resources/images/player/remove16.png"); clearIcon = loadIcon("/resources/images/player/clear16.png"); playIcon = loadIcon("/resources/images/player/play16.png"); pauseIcon = loadIcon("/resources/images/player/pause16.png"); stopIcon = loadIcon("/resources/images/player/stop16.png"); fwdIcon = loadIcon("/resources/images/player/fwd16.png"); rewIcon = loadIcon("/resources/images/player/rew16.png"); nextIcon = loadIcon("/resources/images/player/next16.png"); prevIcon = loadIcon("/resources/images/player/prev16.png"); volumeIcon = loadIcon("/resources/images/player/vol16.png"); muteIcon = loadIcon("/resources/images/player/mute16.png"); sliderIcon = loadIcon("/resources/images/player/bar16.png"); } private static ImageIcon loadIcon(String path) { URL url = PlayerControlPanel.class.getResource(path); if (url != null) { return new ImageIcon(url); } throw new RuntimeException("icon not found: " + path); } public void toggleView(Component child) { Component anchor = child.getParent(); anchor.setPreferredSize(anchor.getSize()); // Toggle sibling visibility expanded = !expanded; for (Component c : anchor.getParent().getComponents()) { if (c != anchor) { c.setVisible(expanded); } } // Redraw without moving the anchor (if possible) int y = (int) anchor.getLocation().getY(); JFrame top = (JFrame) SwingUtilities.getWindowAncestor(this); top.setVisible(false); top.pack(); Point p = top.getLocation(); top.setLocation((int) p.getX(), y - (int) anchor.getLocation().getY() + (int) p.getY()); top.setVisible(true); } static class Button extends JButton { private static final long serialVersionUID = 8649059925768844933L; public Button(Action a) { this(0, a); } public Button(int width, Action a) { super(a); if (width > 0) { setPreferredSize(new Dimension(width, 30)); } setFocusable(false); } } static class Toolbar extends JToolBar { private static final long serialVersionUID = -657958964967514184L; public Toolbar() { super(SwingConstants.HORIZONTAL); setFloatable(false); setRollover(true); setOpaque(false); setBorderPainted(false); } } }