package chatty.gui.components.settings;
import chatty.gui.RegexDocumentFilter;
import chatty.gui.components.settings.SimpleTableEditor.StringMapItem;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.AbstractDocument;
import javax.swing.text.DocumentFilter;
/**
* A map setting with a String as key/value. Implement the abstract method to
* parse the value into the desired type.
*
* @author tduva
*/
public abstract class SimpleTableEditor<T> extends TableEditor<StringMapItem> implements MapSetting<String, T> {
private final MyTableModel data = new MyTableModel();
private final MyItemEditor editor;
public SimpleTableEditor(JDialog owner) {
super(SORTING_MODE_SORTED, false);
setModel(data);
editor = new MyItemEditor(owner);
setItemEditor(editor);
}
public void edit(String item) {
StringMapItem preset = new StringMapItem(item, "");
int index = data.indexOf(preset);
if (index == -1) {
addItem(preset);
} else {
editItem(index);
}
}
protected abstract T valueFromString(String input);
public void setKeyFilter(String p) {
editor.setKeyFilter(new RegexDocumentFilter(p));
}
public void setValueFilter(String p) {
editor.setValueFilter(new RegexDocumentFilter(p));
}
@Override
public Map<String, T> getSettingValue() {
Map<String, T> map = new HashMap<>();
for (StringMapItem item : data.getData()) {
map.put(item.key, valueFromString(item.value));
}
return map;
}
@Override
public void setSettingValue(Map<String, T> values) {
data.clear();
Collection<StringMapItem> items = new ArrayList<>();
for (String key : values.keySet()) {
String value = String.valueOf(values.get(key));
items.add(new StringMapItem(key, value));
}
data.setData(items);
}
private boolean hasKey(String key) {
for (StringMapItem item : data.getData()) {
if (item.key.equals(key)) {
return true;
}
}
return false;
}
/**
* Simple key/value String pair. Counts as equal if the key is equal
* (intended to be used in the context of a table where each key should only
* occur once).
*/
static class StringMapItem {
private final String key;
private final String value;
public StringMapItem(String key, String value) {
this.key = key;
this.value = value;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final StringMapItem other = (StringMapItem) obj;
if (!Objects.equals(this.key, other.key)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.key);
return hash;
}
}
private static class MyTableModel extends ListTableModel<StringMapItem> {
public MyTableModel() {
super(new String[]{"Key", "Value"});
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (columnIndex == 0) {
return get(rowIndex).key;
} else {
return get(rowIndex).value;
}
}
}
private class MyItemEditor implements ItemEditor<StringMapItem> {
private final JDialog dialog;
private final JTextField key = new JTextField();
private final JTextField value = new JTextField();
private final JButton ok = new JButton("Done");
private final JButton cancel = new JButton("Cancel");
private String presetKey;
private boolean save;
public MyItemEditor(JDialog owner) {
dialog = new JDialog(owner);
dialog.setTitle("Edit Item");
dialog.setModal(true);
DocumentListener documentListener = new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
updateButtons();
}
@Override
public void removeUpdate(DocumentEvent e) {
updateButtons();
}
@Override
public void changedUpdate(DocumentEvent e) {
updateButtons();
}
};
key.getDocument().addDocumentListener(documentListener);
value.getDocument().addDocumentListener(documentListener);
ActionListener listener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == ok) {
if (!getKey().equals(presetKey) && hasKey(getKey())) {
JOptionPane.showMessageDialog(dialog,
"An item with the key '"+getKey()+"' already exists");
} else {
dialog.setVisible(false);
save = true;
}
} else if (e.getSource() == cancel) {
dialog.setVisible(false);
}
}
};
ok.addActionListener(listener);
cancel.addActionListener(listener);
dialog.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 0.5;
gbc.insets = new Insets(3, 3, 3, 3);
gbc.gridwidth = 2;
dialog.add(key, gbc);
gbc.gridx = 0;
gbc.gridy = 1;
dialog.add(value, gbc);
gbc.gridwidth = 1;
gbc.gridx = 2;
gbc.gridx = 0;
gbc.gridy = 2;
gbc.insets = new Insets(7, 2, 4, 2);
dialog.add(ok, gbc);
gbc.gridx = 1;
dialog.add(cancel, gbc);
dialog.pack();
dialog.setResizable(false);
}
@Override
public StringMapItem showEditor(StringMapItem preset, Component c, boolean edit) {
save = false;
if (edit) {
dialog.setTitle("Edit item");
} else {
dialog.setTitle("Add item");
}
dialog.setLocationRelativeTo(c);
if (preset != null) {
key.setText(preset.key);
value.setText(preset.value);
presetKey = preset.key;
} else {
key.setText(null);
value.setText(null);
presetKey = null;
}
updateButtons();
key.requestFocusInWindow();
dialog.setVisible(true);
if (save) {
return new StringMapItem(getKey(), value.getText());
}
return null;
}
private String getKey() {
return key.getText();
}
private void updateButtons() {
boolean enabled = !getKey().isEmpty() && !value.getText().isEmpty();
ok.setEnabled(enabled);
}
public void setKeyFilter(DocumentFilter keyFilter) {
((AbstractDocument)key.getDocument()).setDocumentFilter(keyFilter);
}
public void setValueFilter(DocumentFilter keyFilter) {
((AbstractDocument)value.getDocument()).setDocumentFilter(keyFilter);
}
}
}