package comeon.ui.add;
import com.google.common.base.Charsets;
import com.google.common.base.Strings;
import comeon.core.Core;
import comeon.model.Template;
import comeon.ui.UI;
import comeon.ui.actions.BaseAction;
import javax.swing.*;
import javax.swing.GroupLayout.Alignment;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.MaskFormatter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import java.nio.charset.Charset;
import java.nio.file.Paths;
import java.text.ParseException;
class AddMediaPanel extends JPanel {
private static final int MEDIUM_PROTOTYPE_LENGTH = 20;
private static final String PROTOTYPE_CHAR = "x";
private static final int LONG_PROTOTYPE_LENGTH = 30;
private static final long serialVersionUID = 1L;
private final JFileChooser mediaFilesChooser;
private final JComboBox<Template> templates;
private final JCheckBox metatadataCheckbox;
private final JFileChooser metadataFileChooser;
private final JLabel metadataFileLabel;
private final JTextField metadataFileLocation;
private final JButton pickMetadataFileButton;
private final JLabel metadataMatchLabel;
private final JTextField mediaExpression;
private final JLabel metadataMatchSymbol;
private final JComboBox<String> metadataExpression;
private final JLabel mediaRegexpLabel;
private final JTextField mediaRegexp;
private final JLabel mediaSubstitutionLabel;
private final JTextField mediaSubstitution;
private final AddController controller;
public AddMediaPanel(final AddController controller) {
this.controller = controller;
final GroupLayout layout = new GroupLayout(this);
layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);
this.setLayout(layout);
this.mediaFilesChooser = new JFileChooser();
mediaFilesChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
mediaFilesChooser.setMultiSelectionEnabled(true);
final FileFilter picturesFileFilter = new FileNameExtensionFilter(UI.BUNDLE.getString("action.addmedia.filter.pictures"), Core.PICTURE_EXTENSIONS);
final FileFilter audioFileFilter = new FileNameExtensionFilter(UI.BUNDLE.getString("action.addmedia.filter.audio"), Core.AUDIO_EXTENSIONS);
mediaFilesChooser.setAcceptAllFileFilterUsed(false);
mediaFilesChooser.addChoosableFileFilter(picturesFileFilter);
mediaFilesChooser.addChoosableFileFilter(audioFileFilter);
this.metadataFileChooser = new JFileChooser();
metadataFileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
metadataFileChooser.setMultiSelectionEnabled(false);
metadataFileChooser.setFileFilter(new FileNameExtensionFilter(UI.BUNDLE.getString("addmedia.metadata.filter"), "csv"));
metadataFileChooser.setAccessory(new CSVAccessoryPanel());
final JLabel filesListLabel = new JLabel(UI.BUNDLE.getString("addmedia.media.label"));
final JList<File> filesList = new JList<>(controller.getMediaListModel());
filesList.setPrototypeCellValue(new File(Strings.repeat(PROTOTYPE_CHAR, LONG_PROTOTYPE_LENGTH)));
final JScrollPane filesListPanel = new JScrollPane(filesList, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
final JButton pickMediaFilesButton = new JButton(new PickMediaFilesAction(controller));
final JLabel templatesLabel = new JLabel(UI.BUNDLE.getString("addmedia.template.label"));
this.templates = new JComboBox<>(controller.getTemplateModel());
this.templates.setRenderer(new DefaultListCellRenderer() {
private static final long serialVersionUID = 1L;
@Override
public Component getListCellRendererComponent(final JList<?> list, final Object value, final int index, final boolean isSelected,
final boolean cellHasFocus) {
final JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
final Template template = (Template) value;
label.setText(template.getName());
return label;
}
});
this.templates.addActionListener(this::templateChanged);
final UseExternalMetadataCheckboxHandler checkboxHandler = new UseExternalMetadataCheckboxHandler(controller);
this.metatadataCheckbox = new JCheckBox(checkboxHandler);
metatadataCheckbox.addItemListener(checkboxHandler);
this.metadataFileLabel = new JLabel(UI.BUNDLE.getString("addmedia.metadata.label"));
this.metadataFileLocation = new JTextField(LONG_PROTOTYPE_LENGTH);
this.metadataFileLocation.setEditable(false);
this.pickMetadataFileButton = new JButton(new PickMetadataFileAction());
this.metadataMatchLabel = new JLabel(UI.BUNDLE.getString("addmedia.metadata.match.label"));
this.metadataExpression = new JComboBox<>(controller.getMetadataExpressionModel());
this.metadataExpression.setPrototypeDisplayValue(Strings.repeat(PROTOTYPE_CHAR, MEDIUM_PROTOTYPE_LENGTH));
this.metadataExpression.setToolTipText(UI.BUNDLE.getString("addmedia.metadata.match.metadata"));
this.metadataExpression.addActionListener(this::metadataExpressionChanged);
this.mediaExpression = new JTextField(MEDIUM_PROTOTYPE_LENGTH);
this.mediaExpression.setToolTipText(UI.BUNDLE.getString("addmedia.metadata.match.media"));
this.mediaExpression.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void removeUpdate(final DocumentEvent e) {
mediaExpressionChanged(e);
}
@Override
public void insertUpdate(final DocumentEvent e) {
mediaExpressionChanged(e);
}
@Override
public void changedUpdate(final DocumentEvent e) {
mediaExpressionChanged(e);
}
});
this.metadataMatchSymbol = new JLabel(UI.BUNDLE.getString("addmedia.metadata.match.symbol"));
this.mediaRegexpLabel = new JLabel(UI.BUNDLE.getString("addmedia.metadata.regexp.label"));
this.mediaRegexp = new JTextField(controller.getMediaRegexp(), MEDIUM_PROTOTYPE_LENGTH);
this.mediaRegexp.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void removeUpdate(DocumentEvent e) {
mediaRegexpChanged(e);
}
@Override
public void insertUpdate(DocumentEvent e) {
mediaRegexpChanged(e);
}
@Override
public void changedUpdate(DocumentEvent e) {
mediaRegexpChanged(e);
}
});
this.mediaSubstitutionLabel = new JLabel(UI.BUNDLE.getString("addmedia.metadata.substitution.label"));
this.mediaSubstitution = new JTextField(controller.getMediaSubstitution(), MEDIUM_PROTOTYPE_LENGTH);
this.mediaSubstitution.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void removeUpdate(DocumentEvent e) {
mediaSubstitutionChanged(e);
}
@Override
public void insertUpdate(DocumentEvent e) {
mediaSubstitutionChanged(e);
}
@Override
public void changedUpdate(DocumentEvent e) {
mediaSubstitutionChanged(e);
}
});
layout.setVerticalGroup(
layout.createSequentialGroup()
.addComponent(filesListLabel)
.addGroup(
layout.createParallelGroup(Alignment.BASELINE)
.addComponent(filesListPanel)
.addComponent(pickMediaFilesButton)
)
.addGroup(
layout.createParallelGroup(Alignment.BASELINE)
.addComponent(templatesLabel)
.addComponent(templates)
)
.addComponent(metatadataCheckbox)
.addGroup(
layout.createParallelGroup(Alignment.BASELINE)
.addComponent(metadataFileLabel)
.addComponent(metadataFileLocation)
.addComponent(pickMetadataFileButton)
)
.addGroup(
layout.createParallelGroup(Alignment.BASELINE)
.addComponent(metadataMatchLabel)
.addComponent(mediaExpression)
.addComponent(metadataMatchSymbol)
.addComponent(metadataExpression)
)
.addGroup(
layout.createParallelGroup(Alignment.BASELINE)
.addComponent(mediaRegexpLabel)
.addComponent(mediaRegexp)
)
.addGroup(
layout.createParallelGroup(Alignment.BASELINE)
.addComponent(mediaSubstitutionLabel)
.addComponent(mediaSubstitution)
)
);
layout.setHorizontalGroup(
layout.createSequentialGroup()
.addGroup(
layout.createParallelGroup(Alignment.LEADING)
.addComponent(filesListLabel)
.addComponent(filesListPanel)
.addComponent(metatadataCheckbox)
.addGroup(
layout.createSequentialGroup()
.addGroup(
layout.createParallelGroup()
.addComponent(templatesLabel)
.addComponent(metadataFileLabel)
.addComponent(metadataMatchLabel)
.addComponent(mediaRegexpLabel)
.addComponent(mediaSubstitutionLabel)
)
.addGroup(
layout.createParallelGroup()
.addComponent(metadataFileLocation)
.addComponent(templates)
.addGroup(
layout.createSequentialGroup()
.addComponent(metadataExpression)
.addComponent(metadataMatchSymbol)
.addComponent(mediaExpression)
)
.addGroup(
layout.createSequentialGroup()
.addComponent(mediaRegexp)
)
.addGroup(
layout.createSequentialGroup()
.addComponent(mediaSubstitution)
)
)
)
)
.addGroup(
layout.createParallelGroup(Alignment.LEADING)
.addComponent(pickMediaFilesButton)
.addComponent(pickMetadataFileButton)
)
);
this.deactivateMetadataZone();
}
private void templateChanged(final ActionEvent e) {
final Template selectedTemplate = (Template) this.templates.getModel().getSelectedItem();
controller.setTemplate(selectedTemplate);
}
private void mediaExpressionChanged(final DocumentEvent e) {
final Document document = e.getDocument();
try {
controller.setMediaExpression(document.getText(0, document.getLength()));
} catch (final BadLocationException e1) {
}
}
private void mediaRegexpChanged(final DocumentEvent e) {
final Document document = e.getDocument();
try {
controller.setMediaRegexp(document.getText(0, document.getLength()));
} catch (final BadLocationException e1) {
}
}
private void mediaSubstitutionChanged(final DocumentEvent e) {
final Document document = e.getDocument();
try {
controller.setMediaSubstitution(document.getText(0, document.getLength()));
} catch (final BadLocationException e1) {
}
}
private void metadataExpressionChanged(final ActionEvent e) {
final String selectedValue = (String) this.metadataExpression.getModel().getSelectedItem();
controller.setMetadataExpression(selectedValue);
}
private void toggleMetadataZone(final boolean state) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
pickMetadataFileButton.setEnabled(state);
metadataFileLabel.setEnabled(state);
metadataFileLocation.setEnabled(state);
metadataMatchLabel.setEnabled(state);
metadataExpression.setEnabled(state);
metadataMatchSymbol.setEnabled(state);
mediaExpression.setEnabled(state);
mediaRegexp.setEnabled(state);
mediaRegexpLabel.setEnabled(state);
mediaSubstitution.setEnabled(state);
mediaSubstitutionLabel.setEnabled(state);
}
});
}
public void activateMetadataZone() {
this.toggleMetadataZone(true);
}
public void deactivateMetadataZone() {
this.toggleMetadataZone(false);
}
public void updateMetadataFileLocation(final String location) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
metadataFileLocation.setText(location);
}
});
}
private class PickMediaFilesAction extends BaseAction {
private static final long serialVersionUID = 1L;
private final AddController controller;
public PickMediaFilesAction(final AddController controller) {
super("pickmedia");
this.controller = controller;
}
@Override
public void actionPerformed(final ActionEvent e) {
final int returnVal = mediaFilesChooser.showOpenDialog(JOptionPane.getRootFrame());
if (returnVal == JFileChooser.APPROVE_OPTION && mediaFilesChooser.getSelectedFiles().length > 0) {
controller.setMediaFiles(mediaFilesChooser.getSelectedFiles());
}
}
}
private class UseExternalMetadataCheckboxHandler extends AbstractAction implements ItemListener {
private static final long serialVersionUID = 1L;
private final AddController controller;
public UseExternalMetadataCheckboxHandler(final AddController controller) {
super(UI.BUNDLE.getString("addmedia.metadata.use"));
this.controller = controller;
}
@Override
public void actionPerformed(final ActionEvent e) {
}
@Override
public void itemStateChanged(final ItemEvent e) {
SwingUtilities.invokeLater(() -> {
switch (e.getStateChange()) {
case ItemEvent.SELECTED:
controller.setUseMetadata(true);
break;
case ItemEvent.DESELECTED:
controller.setUseMetadata(false);
break;
}
});
}
}
private class PickMetadataFileAction extends BaseAction {
private static final long serialVersionUID = 1L;
public PickMetadataFileAction() {
super("pickmetadata");
}
@Override
public void actionPerformed(final ActionEvent e) {
final int returnVal = metadataFileChooser.showOpenDialog(JOptionPane.getRootFrame());
if (returnVal == JFileChooser.APPROVE_OPTION) {
controller.setMetadataFile(Paths.get(metadataFileChooser.getSelectedFile().toURI()));
controller.updateMetadataFileHeader();
}
}
}
private class CSVAccessoryPanel extends JPanel {
private static final long serialVersionUID = 1L;
public CSVAccessoryPanel() {
final GroupLayout layout = new GroupLayout(this);
layout.setAutoCreateContainerGaps(true);
layout.setAutoCreateGaps(true);
this.setLayout(layout);
this.setBorder(BorderFactory.createTitledBorder(UI.BUNDLE.getString("addmedia.metadata.csv.title")));
final MaskFormatter singleCharFormatter = new MaskFormatter();
try {
singleCharFormatter.setMask("*");
singleCharFormatter.setAllowsInvalid(false);
singleCharFormatter.setCommitsOnValidEdit(false);
} catch (final ParseException e) {
throw new Error("Bad formatter", e);
}
final JLabel separatorLabel = new JLabel(UI.BUNDLE.getString("addmedia.metadata.csv.separator"));
final JFormattedTextField separatorField = new JFormattedTextField(singleCharFormatter);
separatorLabel.setLabelFor(separatorField);
separatorField.setToolTipText(UI.BUNDLE.getString("addmedia.metadata.csv.separator.tooltip"));
separatorField.setText(String.valueOf(controller.getSeparator()));
separatorField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void removeUpdate(final DocumentEvent e) {
separatorChanged(e);
}
@Override
public void insertUpdate(final DocumentEvent e) {
separatorChanged(e);
}
@Override
public void changedUpdate(final DocumentEvent e) {
separatorChanged(e);
}
});
final JLabel quoteLabel = new JLabel(UI.BUNDLE.getString("addmedia.metadata.csv.quote"));
final JFormattedTextField quoteField = new JFormattedTextField(singleCharFormatter);
quoteLabel.setLabelFor(quoteField);
quoteField.setToolTipText(UI.BUNDLE.getString("addmedia.metadata.csv.quote.tooltip"));
quoteField.setText(String.valueOf(controller.getQuote()));
quoteField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void removeUpdate(final DocumentEvent e) {
quoteChanged(e);
}
@Override
public void insertUpdate(final DocumentEvent e) {
quoteChanged(e);
}
@Override
public void changedUpdate(final DocumentEvent e) {
quoteChanged(e);
}
});
final JLabel escapeLabel = new JLabel(UI.BUNDLE.getString("addmedia.metadata.csv.escape"));
final JFormattedTextField escapeField = new JFormattedTextField(singleCharFormatter);
escapeLabel.setLabelFor(escapeField);
escapeField.setToolTipText(UI.BUNDLE.getString("addmedia.metadata.csv.escape.tooltip"));
escapeField.setText(String.valueOf(controller.getEscape()));
escapeField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void removeUpdate(final DocumentEvent e) {
escapeChanged(e);
}
@Override
public void insertUpdate(final DocumentEvent e) {
escapeChanged(e);
}
@Override
public void changedUpdate(final DocumentEvent e) {
escapeChanged(e);
}
});
final JLabel skipLinesLabel = new JLabel(UI.BUNDLE.getString("addmedia.metadata.csv.skipLines"));
final JSpinner skipLinesField = new JSpinner(new SpinnerNumberModel(controller.getSkipLines(), 0, Integer.MAX_VALUE, 1) {
private static final long serialVersionUID = 1L;
@Override
public void setValue(final Object value) {
controller.setSkipLines((int) value);
super.setValue(value);
}
});
skipLinesLabel.setLabelFor(skipLinesField);
skipLinesField.setToolTipText(UI.BUNDLE.getString("addmedia.metadata.csv.skipLines.tooltip"));
final JCheckBox strictQuotesBox = new JCheckBox(UI.BUNDLE.getString("addmedia.metadata.csv.strictQuotes"), controller.isStrictQuotes());
strictQuotesBox.addChangeListener(e -> controller.setStrictQuotes(strictQuotesBox.isSelected()));
strictQuotesBox.setToolTipText(UI.BUNDLE.getString("addmedia.metadata.csv.strictQuotes.tooltip"));
final JCheckBox ignoreLeadingWhiteSpaceBox = new JCheckBox(UI.BUNDLE.getString("addmedia.metadata.csv.ignoreLeadingWhiteSpace"), controller.isIgnoreLeadingWhiteSpace());
ignoreLeadingWhiteSpaceBox.addChangeListener(e -> controller.setIgnoreLeadingWhiteSpace(ignoreLeadingWhiteSpaceBox.isSelected()));
ignoreLeadingWhiteSpaceBox.setToolTipText(UI.BUNDLE.getString("addmedia.metadata.csv.ignoreLeadingWhiteSpace.tooltip"));
final JLabel charsetLabel = new JLabel(UI.BUNDLE.getString("addmedia.metadata.csv.charset"));
final JComboBox<Charset> charsetField = new JComboBox<>(new Charset[]{
Charsets.ISO_8859_1,
Charsets.UTF_8,
Charsets.US_ASCII
});
charsetLabel.setLabelFor(charsetField);
charsetField.setEditable(true);
charsetField.setSelectedItem(controller.getCharset());
charsetField.addActionListener(e -> {
final Object item = charsetField.getSelectedItem();
if (item instanceof Charset) {
controller.setCharset((Charset) item);
}
});
layout.setVerticalGroup(
layout.createSequentialGroup()
.addComponent(separatorLabel)
.addComponent(separatorField)
.addComponent(quoteLabel)
.addComponent(quoteField)
.addComponent(escapeLabel)
.addComponent(escapeField)
.addComponent(skipLinesLabel)
.addComponent(skipLinesField)
.addComponent(strictQuotesBox)
.addComponent(ignoreLeadingWhiteSpaceBox)
.addComponent(charsetLabel)
.addComponent(charsetField)
);
layout.setHorizontalGroup(
layout.createParallelGroup()
.addComponent(separatorLabel)
.addComponent(separatorField)
.addComponent(quoteLabel)
.addComponent(quoteField)
.addComponent(escapeLabel)
.addComponent(escapeField)
.addComponent(skipLinesLabel)
.addComponent(skipLinesField)
.addComponent(strictQuotesBox)
.addComponent(ignoreLeadingWhiteSpaceBox)
.addComponent(charsetLabel)
.addComponent(charsetField)
);
}
private void separatorChanged(final DocumentEvent e) {
controller.setSeparator(getFirstChar(e));
}
private void quoteChanged(final DocumentEvent e) {
controller.setQuote(getFirstChar(e));
}
private void escapeChanged(final DocumentEvent e) {
controller.setEscape(getFirstChar(e));
}
private char getFirstChar(final DocumentEvent e) {
final Document document = e.getDocument();
try {
return document.getLength() == 0 ? 0 : document.getText(0, document.getLength()).charAt(0);
} catch (final BadLocationException e1) {
return 0;
}
}
}
}