/* * The GPLv3 licence : * ----------------- * Copyright (c) 2009 Ricardo Dias * * This file is part of MuVis. * * MuVis is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * MuVis is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MuVis. If not, see <http://www.gnu.org/licenses/>. */ package muvis.view; import com.vlsolutions.swing.docking.DockKey; import com.vlsolutions.swing.docking.Dockable; import com.vlsolutions.swing.docking.DockingConstants; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.File; import java.util.ArrayList; import javax.swing.DefaultListModel; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.ListCellRenderer; import javax.swing.SwingUtilities; import javax.swing.filechooser.FileNameExtensionFilter; import muvis.Elements; import muvis.Environment; import muvis.audio.playlist.Playlist; import muvis.audio.playlist.PlaylistItem; import muvis.util.Observable; import muvis.util.Observer; import muvis.util.Util; import muvis.view.controllers.PlaylistControllerInterface; /** * This class is the Interface View for the Playlist * The handlers for the swing interface are specified here, but the interaction * is made through a controller. * @author Ricardo */ public class PlaylistView extends PlaylistViewUI implements Dockable, ActionListener, Observer { //Controller for the interface private PlaylistControllerInterface playlistController; //Model for the list of tracks private DefaultListModel playlistListModel; //DocKey for this panel to be dockable private DockKey key; //file choosers for saving and loading playlist private JFileChooser loadPlaylistChooser, savePlaylistChooser; //Parent JFrame private JFrame parent; public PlaylistView(JFrame parent, PlaylistControllerInterface controller) { playlistController = controller; this.parent = parent; loadPlaylistChooser = new JFileChooser(new File("")); savePlaylistChooser = new JFileChooser(new File("")); loadPlaylistButton.addActionListener(this); savePlaylistButton.addActionListener(this); remTrackButton.addActionListener(this); managePlaylistButton.addActionListener(this); Environment.getEnvironmentInstance().getAudioPlaylist().registerObserver(this); initPlaylistList(); initDockKey(); } /** * This is the method for handling the actions in the buttons * Each button have a specific method for handling the input * @param event */ @Override public void actionPerformed(ActionEvent event) { if (event.getSource() == remTrackButton) { remPlaylistItemsAction(); } else if (event.getSource() == savePlaylistButton) { savePlaylistAction(); } else if (event.getSource() == loadPlaylistButton) { loadPlaylistAction(); } else if (event.getSource() == managePlaylistButton) { ManagePlaylistView managePlaylist = new ManagePlaylistView(parent); managePlaylist.setVisible(true); } } /** * Initializes and sets the properties of this dockable panel */ private void initDockKey() { key = new DockKey("Playlist"); key.setTooltip("Playlist List"); key.setCloseEnabled(false); key.setAutoHideEnabled(true); key.setMaximizeEnabled(false); key.setAutoHideBorder(DockingConstants.HIDE_RIGHT); } /** * Initializes all the properties related to the playlist visualization. */ private void initPlaylistList() { playlistListModel = new DefaultListModel(); listTracks.setModel(playlistListModel); listTracks.setCellRenderer(new PlaylistListCellRenderer()); MouseListener mouseListener = new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { Environment workspace = Environment.getEnvironmentInstance(); MusicControllerView musicPlayerControllerView = (MusicControllerView) workspace.getViewManager().getView(Elements.MUSIC_PLAYER_VIEW); musicPlayerControllerView.setPlayingType(MusicControllerView.PlayingType.PLAYLIST_MODE); //sets the cursor to the selected track int index = listTracks.locationToIndex(e.getPoint()); PlaylistItem item = (PlaylistItem)playlistListModel.getElementAt(index); int itemIndex = workspace.getAudioPlaylist().getIndex(item); workspace.getAudioPlaylist().updateCursor(itemIndex); musicPlayerControllerView.playTrack(); } } }; listTracks.addMouseListener(mouseListener); } /** * Action for loading the playlist: uses the controller associated to this view */ private void loadPlaylistAction(){ SwingUtilities.invokeLater(new Runnable() { @Override public void run() { loadPlaylistChooser.setDialogTitle("Select a playlist file"); FileNameExtensionFilter fFilter = new FileNameExtensionFilter("M3U playlist", "m3u"); loadPlaylistChooser.addChoosableFileFilter(fFilter); int returned = loadPlaylistChooser.showOpenDialog(parent); if (returned == JFileChooser.APPROVE_OPTION) { File file = loadPlaylistChooser.getSelectedFile(); playlistController.loadPlaylist(file.getName().toString(), loadPlaylistChooser.getCurrentDirectory().toString()); updateListTracksDisplay(); remTrackButton.setEnabled(true); } } }); } /** * Action for removing items from the playlist: uses the controller */ private void remPlaylistItemsAction() { //We can remove several playlist items int[] indices = listTracks.getSelectedIndices(); ArrayList<PlaylistItem> itemsToRemove = new ArrayList<PlaylistItem>(); for (int i = 0; i < indices.length; i++) { PlaylistItem playlistItemToRemove = (PlaylistItem) playlistListModel.getElementAt(indices[i]); //marking the tracks for removal itemsToRemove.add(playlistItemToRemove); int size = playlistListModel.getSize(); if (size == 0) { remTrackButton.setEnabled(false); } else { //Select an index. if (indices[i] == playlistListModel.getSize()) { //removed item in last position indices[i]--; } } } /* * Removing the items from the playlist */ playlistController.removeTracksFromPlaylist(itemsToRemove); for (PlaylistItem it : itemsToRemove){ playlistListModel.removeElement(it); } } /** * Action for saving the current playlist: uses the controller */ private void savePlaylistAction(){ SwingUtilities.invokeLater(new Runnable() { @Override public void run() { savePlaylistChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); savePlaylistChooser.setDialogTitle("Save your playlist"); FileNameExtensionFilter fFilter = new FileNameExtensionFilter("M3U playlist", "m3u"); savePlaylistChooser.addChoosableFileFilter(fFilter); savePlaylistChooser.setAcceptAllFileFilterUsed(false); int returned = savePlaylistChooser.showSaveDialog(parent); if (returned == JFileChooser.APPROVE_OPTION) { File file = savePlaylistChooser.getSelectedFile(); String playlistName = file.getName(); boolean saved = playlistController.savePlaylist(playlistName, savePlaylistChooser.getCurrentDirectory().toString()); if (saved){ JOptionPane.showMessageDialog(parent, "Playlist succefuly saved!", "Save Playlist", JOptionPane.INFORMATION_MESSAGE); } else { JOptionPane.showMessageDialog(parent, "Can't save the playlist!Please try later!", "Save Playlist", JOptionPane.ERROR_MESSAGE); } } } }); } private void updateListTracksDisplay() { Environment workspace = Environment.getEnvironmentInstance(); Playlist playlist = workspace.getAudioPlaylist(); PlaylistItem prevSelectedItem = (PlaylistItem)listTracks.getSelectedValue(); playlistListModel.clear(); int index = listTracks.getSelectedIndex(); //get selected index int prevSelectedItemIndex = 0; for (PlaylistItem item : playlist.getAllItems()) { if (index == -1) { //no selection, so insert at beginning index = 0; } else { //add after the selected item index++; } playlistListModel.insertElementAt(item, index); //try to select the previous element if (prevSelectedItem != null && prevSelectedItem.equals(item)){ prevSelectedItemIndex = index; } } //Select the new item and make it visible. //if previous selected item is still there, then it will continue selected listTracks.setSelectedIndex(prevSelectedItemIndex); listTracks.ensureIndexIsVisible(prevSelectedItemIndex); long playlistDuration = playlist.getTotalPlayingTime(); String playlistDurationStr = Util.secondsToTimeDisplay(playlistDuration); totalTracksInfoLabel.setText("Total: " + playlistDurationStr + "s, in " + playlist.getPlaylistSize() + "tracks."); } private void updateListTracksDisplayNewCursor() { Environment workspace = Environment.getEnvironmentInstance(); Playlist playlist = workspace.getAudioPlaylist(); PlaylistItem newCursor = playlist.getCursor(); int index = 0; for (; index < playlistListModel.getSize() ; index++) { if (playlistListModel.get(index).equals(newCursor)){ break; } } //Select the new item and make it visible. //if previous selected item is still there, then it will continue selected listTracks.setSelectedIndex(index); listTracks.ensureIndexIsVisible(index); } @Override public DockKey getDockKey() { return key; } @Override public Component getComponent() { return this; } /** * This updates the selected item in the playlist list view * @param obs */ @Override public void update(Observable obs, Object arg) { if (obs instanceof Playlist){ Playlist playlist = (Playlist)obs; if (Playlist.Event.NEW_CURSOR.equals(arg) || Playlist.Event.PLAYLIST_UPDATED.equals(arg)){ updateListTracksDisplayNewCursor(); } else if (Playlist.Event.PLAYLIST_RESIZED.equals(arg)){ updateListTracksDisplay(); getDockKey().setNotification(true); } else updateListTracksDisplay(); if (playlist.getPlaylistSize() > 0) remTrackButton.setEnabled(true); else remTrackButton.setEnabled(false); } } } /** * This class implements the CellRenderer for the playlist JList. * The only modification from the default cell renderer is the string that * is shown: replaced by PlaylistItem.getFormattedDisplayName() * @author Ricardo */ class PlaylistListCellRenderer extends JLabel implements ListCellRenderer { /* * This is the only method defined by ListCellRenderer. */ @Override public Component getListCellRendererComponent( JList list, Object value, // value to display int index, // cell index boolean isSelected, // is the cell selected boolean cellHasFocus) // the list and the cell have the focus { /* * this is the most important modification to the renderer, we * replace the text from the jlabel with the value we want. */ String s = ((PlaylistItem) value).getFormattedDisplayName(); setText(s); if (isSelected) { setBackground(list.getSelectionBackground()); setForeground(list.getSelectionForeground()); } else { setBackground(list.getBackground()); setForeground(list.getForeground()); } setEnabled(list.isEnabled()); setFont(list.getFont()); setOpaque(true); return this; } }