package org.lysty.ui;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.dnd.DropTarget;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DropMode;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTable;
import javax.swing.JToggleButton;
import javax.swing.ListCellRenderer;
import javax.swing.ScrollPaneConstants;
import javax.swing.WindowConstants;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.table.TableColumnModel;
import net.miginfocom.swing.MigLayout;
import org.apache.log4j.Logger;
import org.lysty.core.AppSettingsManager;
import org.lysty.core.PlaylistGenerator;
import org.lysty.core.StrategyFactory;
import org.lysty.dao.Song;
import org.lysty.dao.SongSelectionProfile;
import org.lysty.exceptions.StrategyInitiationException;
import org.lysty.strategies.StrategyConfiguration;
import org.lysty.ui.model.PlaylistProfileModel;
import org.lysty.util.FileUtils;
import org.lysty.util.Utils;
public class PlaylistProfileWindow extends LFrame implements
StrategySettingsListener {
private static final String SIZE_TYPE_LENGTH = "Songs";
private static final String SIZE_TYPE_TIME = "Minutes";
private static final int DEF_PLAYLIST_LEN = 10;
JTable table;
PlaylistProfileModel playlistModel;
protected JPopupMenu tablePopup;
private JSpinner spnSize;
private JComboBox cmbSizeType;
private JComboBox cmbStrategy;
private StrategySettingsWindow strategySettingsWindow;
private JToggleButton chkIsCircular;
private StrategyConfiguration currentStrategySettings;
private PlaylistGenerator currentStrategy;
JScrollPane scroller;
private JButton btnFillPlay;
private JButton btnSettings;
private Logger logger = Logger.getLogger(PlaylistProfileWindow.class);
private JLabel lblHelp;
private static PlaylistProfileWindow self = null;
public static PlaylistProfileWindow getInstance() {
if (self == null) {
self = new PlaylistProfileWindow("Lysty Partial Playlist Editor");
}
return self;
}
private PlaylistProfileWindow(String title) {
super(title);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
writeSettings();
}
@Override
public void windowClosed(WindowEvent e) {
}
});
this.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
init();
loadLastSettings();
}
/**
* Loads the last settings for each control
*/
private void loadLastSettings() {
spnSize.setValue(Integer.parseInt(AppSettingsManager.getProperty(
AppSettingsManager.LS_PPL_SIZE, DEF_PLAYLIST_LEN + "")));
String strategyClass = AppSettingsManager
.getProperty(AppSettingsManager.LS_FILL_STRATEGY);
if (Utils.stringNotNullOrEmpty(strategyClass)) {
PlaylistGenerator strategy = StrategyFactory
.getStrategyByClassName(strategyClass);
if (strategy != null)
cmbStrategy.setSelectedItem(strategy);
}
chkIsCircular.setSelected(AppSettingsManager
.getPropertyAsBoolean(AppSettingsManager.LS_IS_CIRC_PPL));
}
private void writeSettings() {
AppSettingsManager.setProperty(AppSettingsManager.LS_FILL_STRATEGY,
((PlaylistGenerator) cmbStrategy.getSelectedItem()).getClass()
.getName());
AppSettingsManager.setProperty(AppSettingsManager.LS_IS_CIRC_PPL,
chkIsCircular.isSelected() ? "true" : "false");
AppSettingsManager.setProperty(AppSettingsManager.LS_PPL_SIZE,
spnSize.getValue() + "");
}
public void init() {
createControlPanel();
table = new JTable();
table.setTableHeader(null);
scroller = new JScrollPane();
scroller.setViewportView(table);
scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
strategySettingsWindow = StrategySettingsWindow.getInstance();
strategySettingsWindow.setListener(this);
// Create the drag and drop listener
TableDragDropListener myDragDropListener = new TableDragDropListener(
table);
playlistModel = new PlaylistProfileModel(DEF_PLAYLIST_LEN);
table.setModel(playlistModel);
table.setDragEnabled(true);
table.setDropMode(DropMode.INSERT_ROWS);
table.setTransferHandler(new TableRowTransferHandler(table));
playlistModel.addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
spnSize.setValue(playlistModel.getRowCount());
}
});
table.setFillsViewportHeight(true);
TableColumnModel colModel = table.getColumnModel();
colModel.getColumn(0).setPreferredWidth(Integer.MAX_VALUE);
table.setDragEnabled(true);
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
int r = table.rowAtPoint(e.getPoint());
if (r >= 0 && r < table.getRowCount()) {
table.setRowSelectionInterval(r, r);
} else {
table.clearSelection();
}
final int rowindex = table.getSelectedRow();
if (rowindex < 0)
return;
if (e.isPopupTrigger() && e.getComponent() instanceof JTable) {
tablePopup = new JPopupMenu();
JMenuItem mnuRem = new JMenuItem(new AbstractAction(
"Remove") {
@Override
public void actionPerformed(ActionEvent e) {
playlistModel.removeRow(rowindex);
}
});
tablePopup.add(mnuRem);
tablePopup.show(e.getComponent(), e.getX(), e.getY());
}
}
});
// Connect the label with a drag and drop listener
new DropTarget(table, myDragDropListener);
this.setJMenuBar(createMenu());
// Add the label to the content
lblHelp = new JLabel(
"<html><i>Drag and drop songs to desired positions in the list</i></html>");
// Show the frame
setToolTips();
layoutUI();
this.setVisible(true);
}
private void setToolTips() {
chkIsCircular.setToolTipText("create cirular playlist");
cmbStrategy
.setToolTipText("Fill strategy to use for creating the playlist");
table.setToolTipText("Drag and drop songs to the desired positions in this table");
}
private void layoutUI() {
JPanel pnlControl = new JPanel(new MigLayout("insets 2 2 2 2",
"[][][][]push[]", "[]"));
pnlControl.setBorder(new TitledBorder(null, "", TitledBorder.LEADING,
TitledBorder.TOP, null, null));
pnlControl.add(new JLabel("Size"));
pnlControl.add(spnSize);
pnlControl.add(cmbSizeType);
pnlControl.add(chkIsCircular);
pnlControl.add(btnFillPlay);
JPanel pnlStrategy = new JPanel(new MigLayout("insets 2 2 2 2",
"[][grow][]", "[]"));
pnlStrategy.setBorder(new TitledBorder(null, "", TitledBorder.LEADING,
TitledBorder.TOP, null, null));
pnlStrategy.add(new JLabel("Fill Method"));
pnlStrategy.add(cmbStrategy, "grow");
pnlStrategy.add(btnSettings);
JPanel panel = new JPanel(new MigLayout("", "[grow]", "[][][][grow]"));
panel.add(pnlControl, "cell 0 0, grow");
panel.add(pnlStrategy, "cell 0 1,grow");
panel.add(lblHelp, "cell 0 2,center,span");
panel.add(scroller, "cell 0 3,grow");
setContentPane(panel);
pack();
this.setMinimumSize(new Dimension(330, 400));
this.setMaximumSize(new Dimension(330, 800));
this.setSize(new Dimension(330, 450));
setVisible(true);
}
public void setSize(int size) {
spnSize.setValue(size);
}
private void createControlPanel() {
cmbSizeType = new JComboBox();
spnSize = new JSpinner();
cmbSizeType.addItem(SIZE_TYPE_LENGTH);
// cmbSizeType.addItem(SIZE_TYPE_TIME); //not supported yet
int defLen = DEF_PLAYLIST_LEN;
spnSize.setValue(defLen);
spnSize.addFocusListener(new FocusListener() {
@Override
public void focusLost(FocusEvent e) {
// TODO Auto-generated method stub
int newSize = PlaylistProfileWindow.this.playlistModel
.updateSize((Integer) spnSize.getValue());
spnSize.setValue(newSize);
}
@Override
public void focusGained(FocusEvent e) {
// TODO Auto-generated method stub
}
});
spnSize.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
// TODO Auto-generated method stub
int newSize = PlaylistProfileWindow.this.playlistModel
.updateSize((Integer) spnSize.getValue());
spnSize.setValue(newSize);
}
});
List<PlaylistGenerator> list = StrategyFactory.getAllStrategies();
cmbStrategy = new JComboBox();
cmbStrategy.setModel(new DefaultComboBoxModel(list
.toArray(new PlaylistGenerator[list.size()])));
cmbStrategy.setRenderer(new ListCellRenderer() {
@Override
public Component getListCellRendererComponent(JList arg0,
Object arg1, int arg2, boolean arg3, boolean arg4) {
JLabel lbl = new JLabel(((PlaylistGenerator) arg1)
.getStrategyDisplayName());
return lbl;
}
});
cmbStrategy.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent event) {
if (event.getStateChange() == ItemEvent.SELECTED) {
PlaylistGenerator item = (PlaylistGenerator) event
.getItem();
onStrategySelectionChange(item);
}
}
});
cmbStrategy.setSelectedIndex(0);
btnSettings = new JButton(new AbstractAction("") {
@Override
public void actionPerformed(ActionEvent e) {
showSettingsFrame();
}
});
btnSettings.setIcon(Utils.getIcon(ResourceConstants.SETTINGS_ICON));
btnFillPlay = new JButton(new AbstractAction("Fill & Play") {
@Override
public void actionPerformed(ActionEvent e) {
try {
generatePlayList(true);
} catch (Exception e1) {
logger.error("Error generating playlist", e1);
}
}
});
chkIsCircular = new JToggleButton(
Utils.getIcon(ResourceConstants.CIRCULAR_ICON));
onStrategySelectionChange((PlaylistGenerator) cmbStrategy.getItemAt(0));
}
protected void onStrategySelectionChange(PlaylistGenerator newStrategy) {
PlaylistProfileWindow.this.currentStrategy = newStrategy;
PlaylistProfileWindow.this.currentStrategySettings = StrategyFactory
.getDefaultOrLastSettings(newStrategy);
}
protected void showSettingsFrame() {
PlaylistGenerator strategy = (PlaylistGenerator) cmbStrategy
.getSelectedItem();
strategySettingsWindow.createUi(strategy, this);
if (currentStrategySettings != null) {
strategySettingsWindow.loadSettings(currentStrategySettings);
}
}
private JMenuBar createMenu() {
JMenuBar menu = new JMenuBar();
JMenu mnuFile = new JMenu("File");
JMenuItem mnuFileNew = new JMenuItem(new AbstractAction(
"New Partial Playlist") {
@Override
public void actionPerformed(ActionEvent e) {
boolean isCancel = saveOnRequest();
if (!isCancel)
newSelectionProfile();
}
});
JMenuItem mnuFileSaveSP = new JMenuItem(new AbstractAction(
"Save Partial Playlist") {
@Override
public void actionPerformed(ActionEvent e) {
saveSelProfile();
}
});
JMenuItem mnuFileLoadSP = new JMenuItem(new AbstractAction(
"Load Partial Playlist") {
@Override
public void actionPerformed(ActionEvent e) {
boolean isCancel = saveOnRequest();
if (!isCancel)
loadSelProfile();
}
});
JMenuItem mnuFileExit = new JMenuItem(new AbstractAction("Close") {
@Override
public void actionPerformed(ActionEvent e) {
close();
}
});
mnuFile.add(mnuFileNew);
mnuFile.add(mnuFileLoadSP);
mnuFile.addSeparator();
mnuFile.add(mnuFileSaveSP);
mnuFile.addSeparator();
mnuFile.add(mnuFileExit);
menu.add(mnuFile);
menu.add(mnuFile);
return menu;
}
protected void generatePlayList(boolean autoPlay)
throws ClassNotFoundException, InstantiationException,
IllegalAccessException, StrategyInitiationException {
StrategyConfiguration config = (currentStrategySettings == null) ? StrategyFactory
.getDefaultOrLastSettings(currentStrategy)
: currentStrategySettings;
List<Song> list = StrategyFactory.getPlaylistByStrategy(
currentStrategy, getSelectionProfile(), config,
chkIsCircular.isSelected(), true, null);
showPlaylist(list, autoPlay);
}
private void showPlaylist(List<Song> list, boolean autoPlay) {
PlaylistPreviewWindow win = PlaylistPreviewWindow.getInstance();
win.init(list, autoPlay, getSelectionProfile());
}
protected void close() {
writeSettings();
playlistModel = new PlaylistProfileModel(playlistModel.getRowCount());
table.setModel(playlistModel);
this.setVisible(false);
}
/**
* Prompt user for save if required.
*
* @return whether user chose to cancel the operation.
*/
protected boolean saveOnRequest() {
if (playlistModel.isEdited()) {
int n = JOptionPane.showOptionDialog(PlaylistProfileWindow.this,
"Do you want to save the current selection profile?",
"Save", JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE, null, new String[] { "Yes",
"No", "Cancel" }, "Yes");
if (n == 0) { // save first
saveSelProfile();
return false;
} else if (n == 2) {
return true;
}
}
return false;
}
protected void loadSelProfile() {
JFileChooser chooser = new JFileChooser();
chooser.setFileFilter(new FileNameExtensionFilter("Partial Playlists",
FileUtils.PARTIAL_PLAYLIST_EXT));
int c = chooser.showOpenDialog(this);
if (c != JFileChooser.APPROVE_OPTION) {
return;
}
File file = chooser.getSelectedFile();
loadSelProfile(file);
}
public void loadSelProfile(File file) {
SongSelectionProfile selProfile = FileUtils.loadSelectionProfile(file);
setSelectedStrategy(selProfile.getStrategy());
spnSize.setValue(selProfile.getSize());
cmbSizeType.setSelectedIndex(selProfile.getSizeType());
currentStrategySettings = selProfile.getStrategyConfig();
chkIsCircular.setSelected(selProfile.isCircular());
playlistModel.loadFromSelProfile(selProfile);
}
private void setSelectedStrategy(PlaylistGenerator strategy) {
cmbStrategy.setSelectedItem(strategy);
}
/**
* New Selection profile
*/
protected void newSelectionProfile() {
playlistModel = new PlaylistProfileModel(DEF_PLAYLIST_LEN);
table.setModel(playlistModel);
}
public SongSelectionProfile getSelectionProfile() {
SongSelectionProfile profile = playlistModel.getSelProfile();
profile.setStrategy(currentStrategy);
profile.setStrategyConfig(currentStrategySettings);
profile.setIsCircular(chkIsCircular.isSelected());
profile.setSizeType(cmbSizeType.getSelectedIndex());
return profile;
}
/**
* Save the selection profile
*
* @return success
*/
protected boolean saveSelProfile() {
// TODO Auto-generated method stub
SongSelectionProfile selProfile = getSelectionProfile();
if (selProfile.getFile() == null) {
// no file set. SaveAs...
JFileChooser chooser = new JFileChooser();
chooser.setFileFilter(new FileNameExtensionFilter(
"Partial Playlists", FileUtils.PARTIAL_PLAYLIST_EXT));
int c = chooser.showSaveDialog(this);
if (c != JFileChooser.APPROVE_OPTION) {
return false;
}
File file = chooser.getSelectedFile();
String fileName = file.toString();
if (!fileName.endsWith("." + FileUtils.PARTIAL_PLAYLIST_EXT))
fileName += "." + FileUtils.PARTIAL_PLAYLIST_EXT;
try {
FileUtils.writeXml(new File(fileName), selProfile.getXml());
playlistModel.saved();
return true;
} catch (IOException e) {
logger.error("Error saving selection profile", e);
return false;
}
} else {
// write to existing file
try {
boolean success = FileUtils.saveSelectionProfile(selProfile,
selProfile.getFile());
if (success)
playlistModel.saved();
return success;
} catch (IOException e) {
logger.error("", e);
return false;
}
}
}
@Override
public void setCurrentProfileSettings(StrategyConfiguration strategySettings) {
this.currentStrategySettings = strategySettings;
StrategyFactory.updateLastSettings(currentStrategy, strategySettings);
}
}