package org.lysty.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ProgressMonitor;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
import javax.swing.text.Document;
import net.miginfocom.swing.MigLayout;
import org.lysty.core.ExtractorManager;
import org.lysty.dao.Song;
import org.lysty.db.DBHandler;
import org.lysty.util.Utils;
import sas.samples.AutoCompleteDocument;
public class MetaDataEditor extends LFrame {
private static final int ROW_HEIGHT = 25;
private static MetaDataEditor self = null;
public static MetaDataEditor getInstance() {
if (self == null) {
self = new MetaDataEditor("Meta Data Editor");
}
return self;
}
private List<Song> songs;
private JCheckBox chkOverwrite;
private MetaDataEditor(String title) {
super(title);
}
public void createUI() {
List<String> features = features = ExtractorManager
.getSupportedFeatures();
songs = DBHandler.getInstance().getSongs(null);
final LTableModel model = new LTableModel(features, songs);
model.setCurrentFolder(null);
JTable table = new JTable();
JScrollPane scroller = new JScrollPane(table);
scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
table.setModel(model);
final Border eBorder = new EmptyBorder(5, 2, 5, 2);
table.setDefaultRenderer(File.class, new TableCellRenderer() {
@Override
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
JLabel label;
if (value instanceof File) {
String name = ((File) value).getName();
if (!Utils.stringNotNullOrEmpty(name)) {
name = ((File) value).getAbsolutePath();
}
label = new JLabel(name, Utils.getIcon(((File) value)
.isDirectory() ? ResourceConstants.FOLDER_ICON
: ResourceConstants.SONG_ICON), SwingConstants.LEFT);
label.setBorder(eBorder);
if (((File) value).isFile()) {
int index = songs.indexOf(new Song((File) value));
if (index < 0) {
// not indexed;
label.setForeground(Color.GRAY);
}
}
return label;
}
label = new JLabel(value.toString());
return label;
}
});
JTextField txtField = new JTextField();
AutoCompletionService completionService = new AutoCompletionService();
model.setCompletionService(completionService);
completionService.createMapFromSongList(DBHandler.getInstance()
.getAllSongs());
table.setDefaultEditor(String.class, new LCellEditor(txtField,
completionService, features));
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() >= 2) {
JTable target = (JTable) e.getSource();
int row = target.getSelectedRow();
LTableModel lModel = (LTableModel) target.getModel();
File file = (File) lModel.getValueAt(row, 0);
if (file.getName().equalsIgnoreCase("..")) {
if (lModel.getCurrentFolder() != null) {
lModel.setCurrentFolder(lModel.getCurrentFolder()
.getParentFile());
}
} else if (file.isDirectory()) {
lModel.setCurrentFolder(file);
}
}
}
});
TableColumnModel cModel = table.getColumnModel();
cModel.getColumn(0).setMinWidth(100);
table.setRowHeight(ROW_HEIGHT);
JButton btnCommit = new JButton(new AbstractAction("Commit Changes") {
@Override
public void actionPerformed(ActionEvent arg0) {
final List<Modification> changes = model.getChanges();
final ProgressMonitor monitor = new ProgressMonitor(
MetaDataEditor.this, "Committing Changes",
"Writing the changes to the DB", 0, changes.size());
new Thread() {
@Override
public void run() {
DBHandler.getInstance().applyModifications(changes,
monitor);
cleanChanges();
MetaDataEditor.this.setVisible(false);
}
}.start();
}
});
java.awt.Font oldFont = btnCommit.getFont();
java.awt.Font newFont = new java.awt.Font(oldFont.getName(),
java.awt.Font.BOLD, oldFont.getSize());
btnCommit.setFont(newFont);
JButton btnCancel = new JButton(new AbstractAction("Cancel") {
@Override
public void actionPerformed(ActionEvent arg0) {
disposeChanges();
MetaDataEditor.this.setVisible(false);
}
});
JLabel lblHelp = new JLabel(
"<html>You can set a value to all songs in a folder by specifing the value at the folder level. By default, it will not overwrite existing values. To overwrite, tick the checkbox below</html>");
// JLabel lblNote = new JLabel(
// "Note: Once the value is set for all songs recursively, the value entered at the folder level will disappear.");
chkOverwrite = new JCheckBox(new AbstractAction(
"Overwrite existing values if required") {
@Override
public void actionPerformed(ActionEvent e) {
model.setDoOverwrite(chkOverwrite.isSelected());
}
});
table.setPreferredScrollableViewportSize(new Dimension(1000, 500));
JPanel panel = new JPanel(new MigLayout("", "[grow]", "[][][grow][]"));
JPanel controlPanel = new JPanel(new MigLayout("", "push[][]", "[]"));
controlPanel.add(btnCommit, "flowx,push,align right");
controlPanel.add(btnCancel, "flowx");
panel.add(lblHelp, "span");
// panel.add(lblNote, "span");
panel.add(chkOverwrite, "span");
panel.add(scroller, "span");
panel.add(controlPanel, "span,grow");
this.setContentPane(panel);
this.setVisible(true);
this.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
this.pack();
}
private void cleanChanges() {
}
protected void disposeChanges() {
songs = DBHandler.getInstance().getSongs(null);
}
class LTableModel extends DefaultTableModel {
private File currentFolder;
private List<String> features;
private File[] currentFiles;
private FileFilter fileFilter;
private List<Modification> changeList;
private List<Song> songs;
private AutoCompletionService completionService;
private boolean doOverwrite;
public void setDoOverwrite(boolean doOverwrite) {
this.doOverwrite = doOverwrite;
}
public void setCompletionService(AutoCompletionService completionService) {
this.completionService = completionService;
}
public LTableModel(List<String> features, List<Song> songs) {
this.features = features;
currentFiles = new File[0];
changeList = new ArrayList<Modification>();
this.songs = songs;
final String[] formats = ExtractorManager.getSupportedFormats();
fileFilter = new FileFilter() {
@Override
public boolean accept(File file) {
if (file.isDirectory())
return true;
for (String format : formats) {
if (file.getName().toLowerCase().endsWith("." + format))
return true;
}
return false;
}
};
}
public File getCurrentFolder() {
return currentFolder;
}
public void setCurrentFolder(File folder) {
this.currentFolder = folder;
if (folder == null) {
currentFiles = File.listRoots();
} else {
currentFiles = folder.listFiles(fileFilter);
}
sortFiles();
fireTableDataChanged();
}
private void sortFiles() {
Comparator<? super File> comparator = new Comparator<File>() {
@Override
public int compare(File o1, File o2) {
if ((o1.isDirectory() && o2.isDirectory())
|| (!o1.isDirectory() && !o2.isDirectory())) {
return o1.getAbsolutePath().compareTo(
o2.getAbsolutePath());
}
if (o1.isDirectory())
return -1;
if (o2.isDirectory())
return 1;
return 0;
}
};
Arrays.sort(currentFiles, comparator);
}
public void setFeatureList(List<String> features) {
this.features = features;
}
@Override
public Class<?> getColumnClass(int col) {
if (col == 0)
return File.class;
return String.class;
}
@Override
public int getColumnCount() {
if (features == null)
return 1;
return features.size() + 1;
}
@Override
public String getColumnName(int arg0) {
if (arg0 == 0)
return "File";
return features.get(arg0 - 1);
}
@Override
public int getRowCount() {
if (currentFiles == null)
return 1;
return currentFiles.length + 1;
}
@Override
public Object getValueAt(int row, int col) {
if (col == 0 && row == 0) {
return new File("..");
}
if (row == 0)
return "";
if (col == 0) {
return currentFiles[row - 1];
}
Song song = getSong(currentFiles[row - 1]);
String feature = features.get(col - 1);
if (song == null)
return "";
return song.getAttribute(feature);
}
@Override
public boolean isCellEditable(int row, int col) {
return (col != 0 && row != 0);
}
@Override
public void setValueAt(Object val, int row, int col) {
File file = currentFiles[row - 1];
String feature = features.get(col - 1);
if (file.isDirectory()) {
setAttributeToChildren(file, feature, val.toString());
} else {
setFeature(file, feature, val.toString());
}
completionService.addValueToMap(feature, val.toString());
fireTableDataChanged();
}
private void setAttributeToChildren(File file, String feature,
String value) {
if (file.isDirectory()) {
File[] files = file.listFiles(fileFilter);
if (files != null) {
for (File f : files) {
setAttributeToChildren(f, feature, value);
}
}
}
boolean shouldWriteValue = false;
Song song = getSong(file);
if (song == null || doOverwrite
|| !Utils.stringNotNullOrEmpty(song.getAttribute(feature)))
shouldWriteValue = true;
if (shouldWriteValue)
setFeature(file, feature, value);
}
public List<Modification> getChanges() {
return changeList;
}
private Song getSong(File file) {
Song song = null;
int songIndex = songs.indexOf(new Song(file));
if (songIndex >= 0) {
song = songs.get(songIndex);
}
return song;
}
private void setFeature(File file, String feature, String value) {
Song song = getSong(file);
String oldValue = null;
if (song == null) {
song = new Song(file);
songs.add(song);
Modification mod = new Modification(song);
changeList.add(mod);
}
oldValue = song.getAttribute(feature);
song.setAttribute(feature, value);
if (!value.equals(oldValue)) {
Modification change = new Modification(getSong(file), feature,
value, oldValue);
changeList.add(change);
}
}
}
class LCellEditor extends DefaultCellEditor {
private AutoCompletionService nameService;
private JTextField field;
private List<String> features;
public LCellEditor(JTextField txtField,
AutoCompletionService completionService, List<String> features) {
super(txtField);
this.field = txtField;
this.nameService = completionService;
this.features = features;
}
public Component getTableCellEditorComponent(JTable table,
Object value, boolean isSelected, int row, int column) {
// TODO Auto-generated method stub
Document autoCompleteDocument = new AutoCompleteDocument(
nameService, field);
nameService.setCurrentField(features.get(column - 1));
field.setDocument(autoCompleteDocument);
return field;
}
}
}