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