001// License: GPL. See LICENSE file for details. 002package org.openstreetmap.josm.gui.preferences.advanced; 003 004import java.awt.Color; 005import java.awt.Component; 006import java.awt.Font; 007import java.awt.GridBagLayout; 008import java.awt.event.MouseAdapter; 009import java.awt.event.MouseEvent; 010import java.util.ArrayList; 011import java.util.Collection; 012import java.util.List; 013import java.util.Map; 014import javax.swing.ButtonGroup; 015import javax.swing.DefaultCellEditor; 016import javax.swing.JComponent; 017import javax.swing.JLabel; 018import javax.swing.JOptionPane; 019import javax.swing.JPanel; 020import javax.swing.JRadioButton; 021import javax.swing.JTable; 022import javax.swing.table.DefaultTableCellRenderer; 023import javax.swing.table.DefaultTableModel; 024import org.openstreetmap.josm.Main; 025import org.openstreetmap.josm.data.Preferences; 026import org.openstreetmap.josm.gui.ExtendedDialog; 027import org.openstreetmap.josm.gui.widgets.JosmTextField; 028import org.openstreetmap.josm.tools.GBC; 029import static org.openstreetmap.josm.tools.I18n.marktr; 030import static org.openstreetmap.josm.tools.I18n.tr; 031import org.openstreetmap.josm.tools.Utils; 032 033/** 034 * Component for editing list of preferences as a table 035 * @since 6021 : extracted from AdvancedPreference class 036 */ 037public class PreferencesTable extends JTable { 038 private AllSettingsTableModel model; 039 private final List<PrefEntry> displayData; 040 041 public PreferencesTable(List<PrefEntry> displayData) { 042 this.displayData = displayData; 043 model = new AllSettingsTableModel(); 044 setModel(model); 045 putClientProperty("terminateEditOnFocusLost", true); 046 getColumnModel().getColumn(1).setCellRenderer(new SettingCellRenderer()); 047 getColumnModel().getColumn(1).setCellEditor(new SettingCellEditor()); 048 049 addMouseListener(new MouseAdapter(){ 050 @Override public void mouseClicked(MouseEvent e) { 051 if (e.getClickCount() == 2) { 052 editPreference(PreferencesTable.this); 053 } 054 } 055 }); 056 } 057 058 /** 059 * This method should be called when displayed data was changed form external code 060 */ 061 public void fireDataChanged() { 062 model.fireTableDataChanged(); 063 } 064 065 066 /** 067 * The list of currently selected rows 068 * @return newly created list of PrefEntry 069 */ 070 public List<PrefEntry> getSelectedItems() { 071 List<PrefEntry> entries = new ArrayList<PrefEntry>(); 072 for (int row : getSelectedRows()) { 073 PrefEntry p = (PrefEntry) model.getValueAt(row, -1); 074 entries.add(p); 075 } 076 return entries; 077 } 078 079 /** 080 * Call this to edit selected row in preferences table 081 * @param gui - parent component for messagebox 082 * @return true if editing was actually performed during this call 083 */ 084 public boolean editPreference(final JComponent gui) { 085 if (getSelectedRowCount() != 1) { 086 JOptionPane.showMessageDialog( 087 gui, 088 tr("Please select the row to edit."), 089 tr("Warning"), 090 JOptionPane.WARNING_MESSAGE 091 ); 092 return false; 093 } 094 final PrefEntry e = (PrefEntry) model.getValueAt(getSelectedRow(), 1); 095 Preferences.Setting stg = e.getValue(); 096 if (stg instanceof Preferences.StringSetting) { 097 editCellAt(getSelectedRow(), 1); 098 Component editor = getEditorComponent(); 099 if (editor != null) { 100 editor.requestFocus(); 101 } 102 } else if (stg instanceof Preferences.ListSetting) { 103 Preferences.ListSetting lSetting = (Preferences.ListSetting) stg; 104 ListEditor lEditor = new ListEditor(gui, e, lSetting); 105 lEditor.showDialog(); 106 if (lEditor.getValue() == 1) { 107 List<String> data = lEditor.getData(); 108 if (!Preferences.equalCollection(lSetting.getValue(), data)) { 109 e.setValue(new Preferences.ListSetting(data)); 110 return true; 111 } 112 } 113 } else if (stg instanceof Preferences.ListListSetting) { 114 ListListEditor llEditor = new ListListEditor(gui, e, (Preferences.ListListSetting) stg); 115 llEditor.showDialog(); 116 if (llEditor.getValue() == 1) { 117 List<List<String>> data = llEditor.getData(); 118 @SuppressWarnings("unchecked") 119 Collection<Collection<String>> stgValue = (Collection<Collection<String>>) stg.getValue(); 120 if (!Preferences.equalArray(stgValue, data)) { 121 e.setValue(new Preferences.ListListSetting(data)); 122 return true; 123 } 124 } 125 } else if (stg instanceof Preferences.MapListSetting) { 126 Preferences.MapListSetting mlSetting = (Preferences.MapListSetting) stg; 127 MapListEditor mlEditor = new MapListEditor(gui, e, mlSetting); 128 mlEditor.showDialog(); 129 if (mlEditor.getValue() == 1) { 130 List<Map<String, String>> data = mlEditor.getData(); 131 if (!Preferences.equalListOfStructs(mlSetting.getValue(), data)) { 132 e.setValue(new Preferences.MapListSetting(data)); 133 return true; 134 } 135 } 136 } 137 return false; 138 } 139 140 /** 141 * Add new preference to the table 142 * @param gui - parent component for asking dialogs 143 * @return newly created entry or null if adding was cancelled 144 */ 145 public PrefEntry addPreference(final JComponent gui) { 146 JPanel p = new JPanel(new GridBagLayout()); 147 p.add(new JLabel(tr("Key")), GBC.std().insets(0,0,5,0)); 148 JosmTextField tkey = new JosmTextField("", 50); 149 p.add(tkey, GBC.eop().insets(5,0,0,0).fill(GBC.HORIZONTAL)); 150 151 p.add(new JLabel(tr("Select Setting Type:")), GBC.eol().insets(5,15,5,0)); 152 153 JRadioButton rbString = new JRadioButton(tr("Simple")); 154 JRadioButton rbList = new JRadioButton(tr("List")); 155 JRadioButton rbListList = new JRadioButton(tr("List of lists")); 156 JRadioButton rbMapList = new JRadioButton(tr("List of maps")); 157 158 ButtonGroup group = new ButtonGroup(); 159 group.add(rbString); 160 group.add(rbList); 161 group.add(rbListList); 162 group.add(rbMapList); 163 164 p.add(rbString, GBC.eol()); 165 p.add(rbList, GBC.eol()); 166 p.add(rbListList, GBC.eol()); 167 p.add(rbMapList, GBC.eol()); 168 169 rbString.setSelected(true); 170 171 ExtendedDialog dlg = new ExtendedDialog(gui, tr("Add setting"), new String[] {tr("OK"), tr("Cancel")}); 172 dlg.setButtonIcons(new String[] {"ok.png", "cancel.png"}); 173 dlg.setContent(p); 174 dlg.showDialog(); 175 176 PrefEntry pe = null; 177 boolean ok = false; 178 if (dlg.getValue() == 1) { 179 if (rbString.isSelected()) { 180 Preferences.StringSetting sSetting = new Preferences.StringSetting(null); 181 pe = new PrefEntry(tkey.getText(), sSetting, sSetting, false); 182 StringEditor sEditor = new StringEditor(gui, pe, sSetting); 183 sEditor.showDialog(); 184 if (sEditor.getValue() == 1) { 185 String data = sEditor.getData(); 186 if (!Utils.equal(sSetting.getValue(), data)) { 187 pe.setValue(new Preferences.StringSetting(data)); 188 ok = true; 189 } 190 } 191 } else if (rbList.isSelected()) { 192 Preferences.ListSetting lSetting = new Preferences.ListSetting(null); 193 pe = new PrefEntry(tkey.getText(), lSetting, lSetting, false); 194 ListEditor lEditor = new ListEditor(gui, pe, lSetting); 195 lEditor.showDialog(); 196 if (lEditor.getValue() == 1) { 197 List<String> data = lEditor.getData(); 198 if (!Preferences.equalCollection(lSetting.getValue(), data)) { 199 pe.setValue(new Preferences.ListSetting(data)); 200 ok = true; 201 } 202 } 203 } else if (rbListList.isSelected()) { 204 Preferences.ListListSetting llSetting = new Preferences.ListListSetting(null); 205 pe = new PrefEntry(tkey.getText(), llSetting, llSetting, false); 206 ListListEditor llEditor = new ListListEditor(gui, pe, llSetting); 207 llEditor.showDialog(); 208 if (llEditor.getValue() == 1) { 209 List<List<String>> data = llEditor.getData(); 210 @SuppressWarnings("unchecked") 211 Collection<Collection<String>> llSettingValue = (Collection) llSetting.getValue(); 212 if (!Preferences.equalArray(llSettingValue, data)) { 213 pe.setValue(new Preferences.ListListSetting(data)); 214 ok = true; 215 } 216 } 217 } else if (rbMapList.isSelected()) { 218 Preferences.MapListSetting mlSetting = new Preferences.MapListSetting(null); 219 pe = new PrefEntry(tkey.getText(), mlSetting, mlSetting, false); 220 MapListEditor mlEditor = new MapListEditor(gui, pe, mlSetting); 221 mlEditor.showDialog(); 222 if (mlEditor.getValue() == 1) { 223 List<Map<String, String>> data = mlEditor.getData(); 224 if (!Preferences.equalListOfStructs(mlSetting.getValue(), data)) { 225 pe.setValue(new Preferences.MapListSetting(data)); 226 ok = true; 227 } 228 } 229 } 230 } 231 if (ok) 232 return pe; 233 else 234 return null; 235 } 236 237 /** 238 * Reset selected preferences to their default values 239 * @param gui - parent component to display warning messages 240 */ 241 public void resetPreferences(final JComponent gui) { 242 if (getSelectedRowCount() == 0) { 243 JOptionPane.showMessageDialog( 244 gui, 245 tr("Please select the row to delete."), 246 tr("Warning"), 247 JOptionPane.WARNING_MESSAGE 248 ); 249 return; 250 } 251 for (int row : getSelectedRows()) { 252 PrefEntry e = displayData.get(row); 253 e.reset(); 254 } 255 fireDataChanged(); 256 } 257 258 private class AllSettingsTableModel extends DefaultTableModel { 259 260 public AllSettingsTableModel() { 261 setColumnIdentifiers(new String[]{tr("Key"), tr("Value")}); 262 } 263 264 @Override 265 public boolean isCellEditable(int row, int column) { 266 return column == 1 && (displayData.get(row).getValue() instanceof Preferences.StringSetting); 267 } 268 269 @Override 270 public int getRowCount() { 271 return displayData.size(); 272 } 273 274 @Override 275 public Object getValueAt(int row, int column) { 276 if (column == 0) 277 return displayData.get(row).getKey(); 278 else 279 return displayData.get(row); 280 } 281 282 @Override 283 public void setValueAt(Object o, int row, int column) { 284 PrefEntry pe = displayData.get(row); 285 String s = (String) o; 286 if (!s.equals(pe.getValue().getValue())) { 287 pe.setValue(new Preferences.StringSetting(s)); 288 fireTableCellUpdated(row, column); 289 } 290 } 291 } 292 293 private static class SettingCellRenderer extends DefaultTableCellRenderer { 294 private Color backgroundColor = Main.pref.getUIColor("Table.background"); 295 private Color changedColor = Main.pref.getColor( 296 marktr("Advanced Background: Changed"), 297 new Color(200,255,200)); 298 private Color foregroundColor = Main.pref.getUIColor("Table.foreground"); 299 private Color nonDefaultColor = Main.pref.getColor( 300 marktr("Advanced Background: NonDefault"), 301 new Color(255,255,200)); 302 303 @Override 304 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { 305 if (value == null) 306 return this; 307 PrefEntry pe = (PrefEntry) value; 308 Preferences.Setting setting = pe.getValue(); 309 Object val = setting.getValue(); 310 String display = val != null ? val.toString() : "<html><i><"+tr("unset")+"></i></html>"; 311 312 JLabel label = (JLabel)super.getTableCellRendererComponent(table, 313 display, isSelected, hasFocus, row, column); 314 315 label.setBackground(backgroundColor); 316 if (isSelected) { 317 label.setForeground(foregroundColor); 318 } 319 if(pe.isChanged()) { 320 label.setBackground(changedColor); 321 } else if(!pe.isDefault()) { 322 label.setBackground(nonDefaultColor); 323 } 324 325 if (!pe.isDefault()) { 326 label.setFont(label.getFont().deriveFont(Font.BOLD)); 327 } 328 val = pe.getDefaultValue().getValue(); 329 if(val != null) 330 { 331 if(pe.isDefault()) { 332 label.setToolTipText(tr("Current value is default.")); 333 } else { 334 label.setToolTipText(tr("Default value is ''{0}''.", val)); 335 } 336 } else { 337 label.setToolTipText(tr("Default value currently unknown (setting has not been used yet).")); 338 } 339 return label; 340 } 341 } 342 343 private static class SettingCellEditor extends DefaultCellEditor { 344 public SettingCellEditor() { 345 super(new JosmTextField()); 346 } 347 348 @Override 349 public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { 350 PrefEntry pe = (PrefEntry) value; 351 Preferences.StringSetting stg = (Preferences.StringSetting) pe.getValue(); 352 String s = stg.getValue() == null ? "" : stg.getValue(); 353 return super.getTableCellEditorComponent(table, s, isSelected, row, column); 354 } 355 } 356}