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