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