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 }