001// License: GPL. See LICENSE file for details. 002package org.openstreetmap.josm.gui.preferences.advanced; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Dimension; 007import java.awt.GridBagLayout; 008import java.awt.event.ActionEvent; 009import java.util.ArrayList; 010import java.util.Arrays; 011import java.util.Collections; 012import java.util.LinkedHashMap; 013import java.util.List; 014import java.util.Map; 015import java.util.Map.Entry; 016 017import javax.swing.AbstractAction; 018import javax.swing.AbstractListModel; 019import javax.swing.DefaultCellEditor; 020import javax.swing.JComponent; 021import javax.swing.JLabel; 022import javax.swing.JList; 023import javax.swing.JPanel; 024import javax.swing.JScrollPane; 025import javax.swing.JTable; 026import javax.swing.JToolBar; 027import javax.swing.event.ListSelectionEvent; 028import javax.swing.event.ListSelectionListener; 029import javax.swing.table.AbstractTableModel; 030import javax.swing.table.TableCellEditor; 031 032import org.openstreetmap.josm.data.Preferences.MapListSetting; 033import org.openstreetmap.josm.gui.ExtendedDialog; 034import org.openstreetmap.josm.gui.widgets.JosmTextField; 035import org.openstreetmap.josm.tools.GBC; 036import org.openstreetmap.josm.tools.ImageProvider; 037import org.openstreetmap.josm.tools.WindowGeometry; 038 039public class MapListEditor extends ExtendedDialog { 040 041 EntryListModel entryModel; 042 PrefEntry entry; 043 044 JList entryList; 045 JTable table; 046 MapTableModel tableModel; 047 048 List<List<String>> dataKeys; 049 List<List<String>> dataValues; 050 Integer entryIdx; 051 052 public MapListEditor(JComponent gui, PrefEntry entry, MapListSetting setting) { 053 super(gui, tr("Change list of maps setting"), new String[] {tr("OK"), tr("Cancel")}); 054 this.entry = entry; 055 List<Map<String, String>> orig = setting.getValue(); 056 057 dataKeys = new ArrayList<List<String>>(); 058 dataValues = new ArrayList<List<String>>(); 059 if (orig != null) { 060 for (Map<String, String> m : orig) { 061 List<String> keys = new ArrayList<String>(); 062 List<String> values = new ArrayList<String>(); 063 for (Entry<String, String> e : m.entrySet()) { 064 keys.add(e.getKey()); 065 values.add(e.getValue()); 066 } 067 dataKeys.add(keys); 068 dataValues.add(values); 069 } 070 } 071 setButtonIcons(new String[] {"ok.png", "cancel.png"}); 072 setRememberWindowGeometry(getClass().getName() + ".geometry", WindowGeometry.centerInWindow(gui, new Dimension(500, 350))); 073 setContent(build(), false); 074 } 075 076 public List<Map<String,String>> getData() { 077 List<Map<String,String>> data = new ArrayList<Map<String,String>>(); 078 for (int i=0; i < dataKeys.size(); ++i) { 079 Map<String,String> m = new LinkedHashMap<String, String>(); 080 for (int j=0; j < dataKeys.get(i).size(); ++j) { 081 m.put(dataKeys.get(i).get(j), dataValues.get(i).get(j)); 082 } 083 data.add(m); 084 } 085 return data; 086 } 087 088 protected JPanel build() { 089 JPanel p = new JPanel(new GridBagLayout()); 090 p.add(new JLabel(tr("Key: {0}", entry.getKey())), GBC.std(0,0).span(2).weight(1, 0).insets(0,0,5,10)); 091 092 JPanel left = new JPanel(new GridBagLayout()); 093 094 entryModel = new EntryListModel(); 095 entryList = new JList(entryModel); 096 entryList.getSelectionModel().addListSelectionListener(new EntryListener()); 097 JScrollPane scroll = new JScrollPane(entryList); 098 left.add(scroll, GBC.eol().fill()); 099 100 JToolBar sideButtonTB = new JToolBar(JToolBar.HORIZONTAL); 101 sideButtonTB.setBorderPainted(false); 102 sideButtonTB.setOpaque(false); 103 sideButtonTB.add(new NewEntryAction()); 104 RemoveEntryAction removeEntryAction = new RemoveEntryAction(); 105 entryList.getSelectionModel().addListSelectionListener(removeEntryAction); 106 sideButtonTB.add(removeEntryAction); 107 left.add(sideButtonTB, GBC.eol()); 108 109 left.setPreferredSize(new Dimension(80, 0)); 110 111 p.add(left, GBC.std(0,1).fill().weight(0.3, 1.0)); 112 113 tableModel = new MapTableModel(); 114 table = new JTable(tableModel); 115 table.putClientProperty("terminateEditOnFocusLost", true); 116 table.getTableHeader().getColumnModel().getColumn(0).setHeaderValue(tr("Key")); 117 table.getTableHeader().getColumnModel().getColumn(1).setHeaderValue(tr("Value")); 118 DefaultCellEditor editor = new DefaultCellEditor(new JosmTextField()); 119 editor.setClickCountToStart(1); 120 table.setDefaultEditor(table.getColumnClass(0), editor); 121 122 JScrollPane pane = new JScrollPane(table); 123 pane.setPreferredSize(new Dimension(140, 0)); 124 p.add(pane, GBC.std(1,1).insets(5,0,0,0).fill().weight(0.7, 1.0)); 125 return p; 126 } 127 128 class EntryListModel extends AbstractListModel { 129 @Override 130 public Object getElementAt(int index) { 131 return tr("Entry {0}", index+1); 132 } 133 134 @Override 135 public int getSize() { 136 return dataKeys.size(); 137 } 138 139 public void add() { 140 dataKeys.add(new ArrayList<String>()); 141 dataValues.add(new ArrayList<String>()); 142 fireIntervalAdded(this, getSize() - 1, getSize() - 1); 143 } 144 145 public void remove(int idx) { 146 dataKeys.remove(idx); 147 dataValues.remove(idx); 148 fireIntervalRemoved(this, idx, idx); 149 } 150 } 151 152 class NewEntryAction extends AbstractAction { 153 public NewEntryAction() { 154 putValue(NAME, tr("New")); 155 putValue(SHORT_DESCRIPTION, tr("add entry")); 156 putValue(SMALL_ICON, ImageProvider.get("dialogs", "add")); 157 } 158 159 @Override 160 public void actionPerformed(ActionEvent evt) { 161 entryModel.add(); 162 } 163 } 164 165 class RemoveEntryAction extends AbstractAction implements ListSelectionListener { 166 public RemoveEntryAction() { 167 putValue(NAME, tr("Remove")); 168 putValue(SHORT_DESCRIPTION, tr("Remove the selected entry")); 169 putValue(SMALL_ICON, ImageProvider.get("dialogs", "delete")); 170 updateEnabledState(); 171 } 172 173 protected void updateEnabledState() { 174 setEnabled(entryList.getSelectedIndices().length == 1); 175 } 176 177 @Override 178 public void valueChanged(ListSelectionEvent e) { 179 updateEnabledState(); 180 } 181 182 @Override 183 public void actionPerformed(ActionEvent e) { 184 int idx = entryList.getSelectedIndices()[0]; 185 entryModel.remove(idx); 186 } 187 } 188 189 class EntryListener implements ListSelectionListener { 190 @Override 191 public void valueChanged(ListSelectionEvent e) { 192 TableCellEditor editor = table.getCellEditor(); 193 if (editor != null) { 194 ((DefaultCellEditor) editor).stopCellEditing(); 195 } 196 if (entryList.getSelectedIndices().length != 1) { 197 entryIdx = null; 198 table.setEnabled(false); 199 } else { 200 entryIdx = entryList.getSelectedIndices()[0]; 201 table.setEnabled(true); 202 } 203 tableModel.fireTableDataChanged(); 204 } 205 } 206 207 class MapTableModel extends AbstractTableModel { 208 private List<List<String>> data() { 209 if (entryIdx == null) return Collections.emptyList(); 210 @SuppressWarnings("unchecked") 211 List<List<String>> result = Arrays.asList(dataKeys.get(entryIdx), dataValues.get(entryIdx)); 212 return result; 213 } 214 215 private int size() { 216 if (entryIdx == null) return 0; 217 return dataKeys.get(entryIdx).size(); 218 } 219 220 @Override 221 public int getRowCount() { 222 return entryIdx == null ? 0 : size() + 1; 223 } 224 225 @Override 226 public int getColumnCount() { 227 return 2; 228 } 229 230 @Override 231 public String getColumnName(int column) { 232 return column == 0 ? tr("Key") : tr("Value"); 233 } 234 235 @Override 236 public Object getValueAt(int row, int column) { 237 return size() == row ? "" : data().get(column).get(row); 238 } 239 240 @Override 241 public void setValueAt(Object o, int row, int column) { 242 String s = (String) o; 243 if (row == size()) { 244 data().get(0).add(""); 245 data().get(1).add(""); 246 data().get(column).set(row, s); 247 fireTableRowsInserted(row+1, row+1); 248 } else { 249 data().get(column).set(row, s); 250 fireTableCellUpdated(row, column); 251 } 252 } 253 254 @Override 255 public boolean isCellEditable(int row, int column) { 256 return true; 257 } 258 } 259 260}