001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.preferences.advanced;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.Component;
007import java.awt.Dimension;
008import java.awt.GridBagLayout;
009import java.awt.event.ActionEvent;
010
011import javax.swing.AbstractAction;
012import javax.swing.AbstractListModel;
013import javax.swing.DefaultCellEditor;
014import javax.swing.JLabel;
015import javax.swing.JList;
016import javax.swing.JPanel;
017import javax.swing.JScrollPane;
018import javax.swing.JTable;
019import javax.swing.JToolBar;
020import javax.swing.event.ListSelectionEvent;
021import javax.swing.event.ListSelectionListener;
022import javax.swing.table.AbstractTableModel;
023import javax.swing.table.TableCellEditor;
024
025import org.openstreetmap.josm.gui.widgets.JosmTextField;
026import org.openstreetmap.josm.tools.GBC;
027import org.openstreetmap.josm.tools.ImageProvider;
028
029/**
030 * Abstract superclass of {@link ListListEditor} and {@link MapListEditor}.
031 * @param <T> type of elements
032 * @since 9505
033 */
034public abstract class AbstractTableListEditor<T> extends AbstractListEditor<T> {
035
036    protected final AbstractEntryListModel entryModel;
037    protected final JList<String> entryList;
038
039    protected final JTable table;
040    protected final AbstractTableModel tableModel;
041
042    protected Integer entryIdx;
043
044    /**
045     * Constructs a new {@code AbstractListEditor}.
046     * @param parent       The parent element that will be used for position and maximum size
047     * @param title        The text that will be shown in the window titlebar
048     * @param entry        Preference entry
049     */
050    public AbstractTableListEditor(Component parent, String title, PrefEntry entry) {
051        super(parent, title, entry);
052        entryModel = newEntryListModel();
053        entryList = new JList<>(entryModel);
054        entryList.getSelectionModel().addListSelectionListener(new EntryListener());
055        tableModel = newTableModel();
056        table = new JTable(tableModel);
057        setContent(build(), false);
058    }
059
060    protected abstract static class AbstractEntryListModel extends AbstractListModel<String> {
061
062        abstract void add();
063
064        abstract void remove(int idx);
065    }
066
067    protected final class NewEntryAction extends AbstractAction {
068        NewEntryAction() {
069            putValue(NAME, tr("New"));
070            putValue(SHORT_DESCRIPTION, tr("add entry"));
071            putValue(SMALL_ICON, ImageProvider.get("dialogs", "add"));
072        }
073
074        @Override
075        public void actionPerformed(ActionEvent evt) {
076            entryModel.add();
077        }
078    }
079
080    protected final class RemoveEntryAction extends AbstractAction implements ListSelectionListener {
081        RemoveEntryAction() {
082            putValue(NAME, tr("Remove"));
083            putValue(SHORT_DESCRIPTION, tr("Remove the selected entry"));
084            putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete"));
085            updateEnabledState();
086        }
087
088        protected void updateEnabledState() {
089            setEnabled(entryList.getSelectedIndices().length == 1);
090        }
091
092        @Override
093        public void valueChanged(ListSelectionEvent e) {
094            updateEnabledState();
095        }
096
097        @Override
098        public void actionPerformed(ActionEvent e) {
099            entryModel.remove(entryList.getSelectedIndices()[0]);
100        }
101    }
102
103    private class EntryListener implements ListSelectionListener {
104        @Override
105        public void valueChanged(ListSelectionEvent e) {
106            TableCellEditor editor = table.getCellEditor();
107            if (editor != null) {
108                ((DefaultCellEditor) editor).stopCellEditing();
109            }
110            if (entryList.getSelectedIndices().length != 1) {
111                entryIdx = null;
112                table.setEnabled(false);
113            } else {
114                entryIdx = entryList.getSelectedIndices()[0];
115                table.setEnabled(true);
116            }
117            tableModel.fireTableStructureChanged();
118            tableModel.fireTableDataChanged();
119        }
120    }
121
122    @Override
123    protected JPanel build() {
124        JPanel p = new JPanel(new GridBagLayout());
125        p.add(new JLabel(tr("Key: {0}", entry.getKey())), GBC.std(0, 0).span(2).weight(1, 0).insets(0, 0, 5, 10));
126
127        JPanel left = new JPanel(new GridBagLayout());
128
129        JScrollPane scroll = new JScrollPane(entryList);
130        left.add(scroll, GBC.eol().fill());
131
132        JToolBar sideButtonTB = new JToolBar(JToolBar.HORIZONTAL);
133        sideButtonTB.setBorderPainted(false);
134        sideButtonTB.setOpaque(false);
135        sideButtonTB.add(new NewEntryAction());
136        RemoveEntryAction removeEntryAction = new RemoveEntryAction();
137        entryList.getSelectionModel().addListSelectionListener(removeEntryAction);
138        sideButtonTB.add(removeEntryAction);
139        left.add(sideButtonTB, GBC.eol());
140
141        left.setPreferredSize(new Dimension(80, 0));
142
143        p.add(left, GBC.std(0, 1).fill().weight(0.3, 1.0));
144
145        table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
146
147        DefaultCellEditor editor = new DefaultCellEditor(new JosmTextField());
148        editor.setClickCountToStart(1);
149        table.setDefaultEditor(table.getColumnClass(0), editor);
150
151        JScrollPane pane = new JScrollPane(table);
152        pane.setPreferredSize(new Dimension(140, 0));
153        p.add(pane, GBC.std(1, 1).insets(5, 0, 0, 0).fill().weight(0.7, 1.0));
154        return p;
155    }
156
157    protected abstract AbstractEntryListModel newEntryListModel();
158
159    protected abstract AbstractTableModel newTableModel();
160}