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