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 private 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}