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