001    // License: GPL. Copyright 2007 by Immanuel Scholz and others
002    package org.openstreetmap.josm.gui.preferences.advanced;
003    
004    import static org.openstreetmap.josm.tools.I18n.marktr;
005    import static org.openstreetmap.josm.tools.I18n.tr;
006    
007    import java.awt.Color;
008    import java.awt.Component;
009    import java.awt.Dimension;
010    import java.awt.Font;
011    import java.awt.GridBagLayout;
012    import java.awt.event.ActionEvent;
013    import java.awt.event.ActionListener;
014    import java.awt.event.MouseAdapter;
015    import java.awt.event.MouseEvent;
016    import java.io.File;
017    import java.util.ArrayList;
018    import java.util.Collection;
019    import java.util.Collections;
020    import java.util.Comparator;
021    import java.util.List;
022    import java.util.Map;
023    import java.util.Map.Entry;
024    
025    import javax.swing.Box;
026    import javax.swing.ButtonGroup;
027    import javax.swing.DefaultCellEditor;
028    import javax.swing.JButton;
029    import javax.swing.JFileChooser;
030    import javax.swing.JLabel;
031    import javax.swing.JOptionPane;
032    import javax.swing.JPanel;
033    import javax.swing.JRadioButton;
034    import javax.swing.JScrollPane;
035    import javax.swing.JTable;
036    import javax.swing.JTextField;
037    import javax.swing.event.DocumentEvent;
038    import javax.swing.event.DocumentListener;
039    import javax.swing.filechooser.FileFilter;
040    import javax.swing.table.DefaultTableCellRenderer;
041    import javax.swing.table.DefaultTableModel;
042    
043    import org.openstreetmap.josm.Main;
044    import org.openstreetmap.josm.actions.DiskAccessAction;
045    import org.openstreetmap.josm.data.CustomConfigurator;
046    import org.openstreetmap.josm.data.Preferences;
047    import org.openstreetmap.josm.data.Preferences.ListListSetting;
048    import org.openstreetmap.josm.data.Preferences.ListSetting;
049    import org.openstreetmap.josm.data.Preferences.MapListSetting;
050    import org.openstreetmap.josm.data.Preferences.Setting;
051    import org.openstreetmap.josm.data.Preferences.StringSetting;
052    import org.openstreetmap.josm.gui.ExtendedDialog;
053    import org.openstreetmap.josm.gui.actionsupport.LogShowDialog;
054    import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting;
055    import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
056    import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
057    import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
058    import org.openstreetmap.josm.tools.CheckParameterUtil;
059    import org.openstreetmap.josm.tools.GBC;
060    import org.openstreetmap.josm.tools.Utils;
061    
062    public class AdvancedPreference extends DefaultTabPreferenceSetting {
063    
064        public static class Factory implements PreferenceSettingFactory {
065            public PreferenceSetting createPreferenceSetting() {
066                return new AdvancedPreference();
067            }
068        }
069    
070        private AdvancedPreference() {
071            super("advanced", tr("Advanced Preferences"), tr("Setting Preference entries directly. Use with caution!"));
072        }
073    
074        @Override
075        public boolean isExpert() {
076            return true;
077        }
078    
079        public static class PrefEntry implements Comparable<PrefEntry> {
080            private String key;
081            private Setting value;
082            private Setting defaultValue;
083            private boolean isDefault;
084            private boolean changed;
085    
086            public PrefEntry(String key, Setting value, Setting defaultValue, boolean isDefault) {
087                CheckParameterUtil.ensureParameterNotNull(key);
088                CheckParameterUtil.ensureParameterNotNull(value);
089                CheckParameterUtil.ensureParameterNotNull(defaultValue);
090                this.key = key;
091                this.value = value;
092                this.defaultValue = defaultValue;
093                this.isDefault = isDefault;
094            }
095    
096            public String getKey() {
097                return key;
098            }
099    
100            public Setting getValue() {
101                return value;
102            }
103    
104            public Setting getDefaultValue() {
105                return defaultValue;
106            }
107    
108            public void setValue(Setting value) {
109                this.value = value;
110                changed = true;
111                isDefault = false;
112            }
113    
114            public boolean isDefault() {
115                return isDefault;
116            }
117    
118            public boolean isChanged() {
119                return changed;
120            }
121        
122            private void markAsChanged() {
123                changed = true;
124            }
125        
126            public void reset() {
127                value = defaultValue;
128                changed = true;
129                isDefault = true;
130            }
131    
132            @Override
133            public int compareTo(PrefEntry other) {
134                return key.compareTo(other.key);
135            }
136    
137            @Override
138            public String toString() {
139                return value.toString();
140            }
141        }
142    
143        private AllSettingsTableModel model;
144        protected List<PrefEntry> data;
145        protected List<PrefEntry> displayData;
146        protected JTextField txtFilter;
147    
148        public void addGui(final PreferenceTabbedPane gui) {
149            JPanel p = gui.createPreferenceTab(this);
150    
151            txtFilter = new JTextField();
152            JLabel lbFilter = new JLabel(tr("Search: "));
153            lbFilter.setLabelFor(txtFilter);
154            p.add(lbFilter);
155            p.add(txtFilter, GBC.eol().fill(GBC.HORIZONTAL));
156            txtFilter.getDocument().addDocumentListener(new DocumentListener(){
157                @Override public void changedUpdate(DocumentEvent e) {
158                    action();
159                }
160                @Override public void insertUpdate(DocumentEvent e) {
161                    action();
162                }
163                @Override public void removeUpdate(DocumentEvent e) {
164                    action();
165                }
166                private void action() {
167                    applyFilter();
168                }
169            });
170            readPreferences(Main.pref);
171            model = new AllSettingsTableModel();
172            applyFilter();
173    
174            final JTable list = new JTable(model);
175            list.putClientProperty("terminateEditOnFocusLost", true);
176            list.getColumnModel().getColumn(1).setCellRenderer(new SettingCellRenderer());
177            list.getColumnModel().getColumn(1).setCellEditor(new SettingCellEditor());
178    
179            JScrollPane scroll = new JScrollPane(list);
180            p.add(scroll, GBC.eol().fill(GBC.BOTH));
181            scroll.setPreferredSize(new Dimension(400,200));
182    
183            JButton add = new JButton(tr("Add"));
184            p.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
185            p.add(add, GBC.std().insets(0,5,0,0));
186            add.addActionListener(new ActionListener(){
187                public void actionPerformed(ActionEvent e) {
188                    addPreference(gui);
189                }
190            });
191    
192            JButton edit = new JButton(tr("Edit"));
193            p.add(edit, GBC.std().insets(5,5,5,0));
194            edit.addActionListener(new ActionListener(){
195                public void actionPerformed(ActionEvent e) {
196                    editPreference(gui, list);
197                }
198            });
199    
200            JButton reset = new JButton(tr("Reset"));
201            p.add(reset, GBC.std().insets(0,5,0,0));
202            reset.addActionListener(new ActionListener(){
203                public void actionPerformed(ActionEvent e) {
204                    resetPreference(gui, list);
205                }
206            });
207    
208            JButton read = new JButton(tr("Read from file"));
209            p.add(read, GBC.std().insets(5,5,0,0));
210            read.addActionListener(new ActionListener(){
211                public void actionPerformed(ActionEvent e) {
212                    File[] files = askUserForCustomSettingsFiles(false, tr("Open JOSM customization file"));
213                    if (files.length==0) return;
214                    
215                    Preferences tmpPrefs = CustomConfigurator.clonePreferences(Main.pref);
216                    
217                    StringBuilder log = new StringBuilder();
218                    log.append("<html>");
219                    for (File f: files) {
220                        CustomConfigurator.readXML(f, tmpPrefs);
221                        log.append(CustomConfigurator.getLog());
222                    }
223                    //try { Main.pref.save();  } catch (IOException ex) { }
224                    log.append("</html>");
225                    String msg = log.toString().replace("\n", "<br/>");
226                    
227                    new LogShowDialog(tr("Import log"), tr("<html>Here is file import summary. <br/>"
228                            + "You can reject preferences changes by pressing \"Cancel\" in preferences dialog <br/>"
229                            + "To activate some changes JOSM restart may be needed.</html>"), msg).showDialog();
230                    
231                    //JOptionPane.showMessageDialog(Main.parent,
232                    //   tr("Installed plugins and some changes in preferences will start to work after JOSM restart"), tr("Warning"), JOptionPane.WARNING_MESSAGE);
233    
234                    readPreferences(tmpPrefs);
235                    // sorting after modification - first modified, then non-default, then default entries
236                    Collections.sort(data, new Comparator<PrefEntry>() {
237                        @Override
238                        public int compare(PrefEntry o1, PrefEntry o2) {
239                            if (o1.changed && !o2.changed) return -1;
240                            if (o2.changed && !o1.changed) return 1;
241                            if (!(o1.isDefault) && o2.isDefault) return -1;
242                            if (!(o2.isDefault) && o1.isDefault) return 1;
243                            return o1.key.compareTo(o2.key);
244                        }
245                      });
246    
247                    applyFilter();
248                    ((AllSettingsTableModel) list.getModel()).fireTableDataChanged();
249                }
250    
251            });
252            
253            JButton export = new JButton(tr("Export selected items"));
254            p.add(export, GBC.std().insets(5,5,0,0));
255            export.addActionListener(new ActionListener(){
256                public void actionPerformed(ActionEvent e) {
257                    ArrayList<String> keys = new ArrayList<String>();
258                    boolean hasLists = false;
259                    for (int row : list.getSelectedRows()) {
260                        PrefEntry p = (PrefEntry) model.getValueAt(row, -1);
261                        if (!p.isDefault()) {
262                            // preferences with default values are not saved
263                            if (!(p.getValue() instanceof StringSetting)) hasLists=true; // => append and replace differs
264                            keys.add(p.getKey());
265                        }
266                    }
267                    if (keys.size()==0) {
268                         JOptionPane.showMessageDialog(Main.parent,
269                            tr("Please select some preference keys not marked as default"), tr("Warning"), JOptionPane.WARNING_MESSAGE);
270                         return;
271                    }
272    
273                    File[] files = askUserForCustomSettingsFiles(true, tr("Export preferences keys to JOSM customization file"));
274                    if (files.length==0) return;
275                    
276                    int answer = 0;
277                    if (hasLists) {
278                        answer = JOptionPane.showOptionDialog(
279                                Main.parent, tr("What to do with preference lists when this file is to be imported?"), tr("Question"),
280                                JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null,
281                                new String[]{tr("Append preferences from file to existing values"), tr("Replace existing values")}, 0);
282                    }
283                    CustomConfigurator.exportPreferencesKeysToFile(files[0].getAbsolutePath(), answer==0, keys);
284                }
285            });
286    
287    
288            list.addMouseListener(new MouseAdapter(){
289                @Override public void mouseClicked(MouseEvent e) {
290                    if (e.getClickCount() == 2) {
291                        editPreference(gui, list);
292                    }
293                }
294            });
295        }
296    
297        private void readPreferences(Preferences tmpPrefs) {
298            Map<String, Setting> loaded;
299            Map<String, Setting> orig = Main.pref.getAllSettings();
300            Map<String, Setting> defaults = tmpPrefs.getAllDefaults();
301            orig.remove("osm-server.password");
302            defaults.remove("osm-server.password");
303            if (tmpPrefs != Main.pref) {
304                loaded = tmpPrefs.getAllSettings();
305                // plugins preference keys may be changed directly later, after plugins are downloaded
306                // so we do not want to show it in the table as "changed" now
307                Setting pluginSetting = orig.get("plugins");
308                if (pluginSetting!=null) {
309                    loaded.put("plugins", pluginSetting);
310                }
311            } else {
312                loaded = orig;
313            }
314            prepareData(loaded, orig, defaults);
315        }
316        
317        private File[] askUserForCustomSettingsFiles(boolean saveFileFlag, String title) {
318            FileFilter filter = new FileFilter() {
319                @Override
320                public boolean accept(File f) {
321                    return f.isDirectory() || f.getName().toLowerCase().endsWith(".xml");
322                }
323                @Override
324                public String getDescription() {
325                    return tr("JOSM custom settings files (*.xml)");
326                }
327            };
328            JFileChooser fc = DiskAccessAction.createAndOpenFileChooser(!saveFileFlag, !saveFileFlag, title, filter, JFileChooser.FILES_ONLY, "customsettings.lastDirectory");
329            if (fc != null) {
330                File sel[] = fc.isMultiSelectionEnabled() ? fc.getSelectedFiles() : (new File[]{fc.getSelectedFile()});
331                if (sel.length==1 && !sel[0].getName().contains(".")) sel[0]=new File(sel[0].getAbsolutePath()+".xml");
332                return sel;
333            } 
334            return new File[0];
335        }
336                
337        private void prepareData(Map<String, Setting> loaded, Map<String, Setting> orig, Map<String, Setting> defaults) {
338            data = new ArrayList<PrefEntry>();
339            for (Entry<String, Setting> e : loaded.entrySet()) {
340                Setting value = e.getValue();
341                Setting old = orig.get(e.getKey());
342                Setting def = defaults.get(e.getKey());
343                if (def == null) {
344                    def = value.getNullInstance();
345                }
346                PrefEntry en = new PrefEntry(e.getKey(), value, def, false);
347                // after changes we have nondefault value. Value is changed if is not equal to old value
348                if ( !Preferences.isEqual(old, value) ) {
349                    en.markAsChanged();
350                }
351                data.add(en);
352            }
353            for (Entry<String, Setting> e : defaults.entrySet()) {
354                if (!loaded.containsKey(e.getKey())) {
355                    PrefEntry en = new PrefEntry(e.getKey(), e.getValue(), e.getValue(), true);
356                    // after changes we have default value. So, value is changed if old value is not default
357                    Setting old = orig.get(e.getKey());
358                    if ( old!=null ) {
359                        en.markAsChanged();
360                    }
361                    data.add(en);
362                }
363            }
364            Collections.sort(data);
365            displayData = new ArrayList<PrefEntry>(data);
366        }
367    
368        class AllSettingsTableModel extends DefaultTableModel {
369    
370            public AllSettingsTableModel() {
371                setColumnIdentifiers(new String[]{tr("Key"), tr("Value")});
372            }
373    
374            @Override
375            public boolean isCellEditable(int row, int column) {
376                return column == 1 && (displayData.get(row).getValue() instanceof StringSetting);
377            }
378    
379            @Override
380            public int getRowCount() {
381                return displayData.size();
382            }
383    
384            @Override
385            public Object getValueAt(int row, int column) {
386                if (column == 0)
387                    return displayData.get(row).getKey();
388                else
389                    return displayData.get(row);
390            }
391    
392            @Override
393            public void setValueAt(Object o, int row, int column) {
394                PrefEntry pe = displayData.get(row);
395                String s = (String) o;
396                if (!s.equals(pe.getValue().getValue())) {
397                    pe.setValue(new StringSetting(s));
398                    fireTableCellUpdated(row, column);
399                }
400            }
401        }
402    
403        private static class SettingCellRenderer extends DefaultTableCellRenderer {
404            private Color backgroundColor = Main.pref.getUIColor("Table.background");
405            private Color changedColor = Main.pref.getColor(
406                             marktr("Advanced Background: Changed"),
407                             new Color(200,255,200));
408            private Color foregroundColor = Main.pref.getUIColor("Table.foreground");
409            private Color nonDefaultColor = Main.pref.getColor(
410                                marktr("Advanced Background: NonDefalut"),
411                                new Color(255,255,200));
412            
413            @Override
414            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
415                if (value == null)
416                    return this;
417                PrefEntry pe = (PrefEntry) value;
418                Setting setting = pe.getValue();
419                Object val = setting.getValue();
420                String display = val != null ? val.toString() : "<html><i>&lt;"+tr("unset")+"&gt;</i></html>";
421                
422                JLabel label = (JLabel)super.getTableCellRendererComponent(table,
423                        display, isSelected, hasFocus, row, column);
424    
425                label.setBackground(backgroundColor);
426                if (isSelected) {
427                    label.setForeground(foregroundColor);
428                }
429                if(pe.isChanged()) {
430                    label.setBackground(changedColor);
431                } else if(!pe.isDefault()) {
432                    label.setBackground(nonDefaultColor);
433                }
434    
435                if (!pe.isDefault()) {
436                    label.setFont(label.getFont().deriveFont(Font.BOLD));
437                }
438                val = pe.getDefaultValue().getValue();
439                if(val != null)
440                {
441                    if(pe.isDefault()) {
442                        label.setToolTipText(tr("Current value is default."));
443                    } else {
444                        label.setToolTipText(tr("Default value is ''{0}''.", val));
445                    }
446                } else {
447                    label.setToolTipText(tr("Default value currently unknown (setting has not been used yet)."));
448                }
449                return label;
450            }
451        }
452    
453        private static class SettingCellEditor extends DefaultCellEditor {
454            public SettingCellEditor() {
455                super(new JTextField());
456            }
457    
458            @Override
459            public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
460                PrefEntry pe = (PrefEntry) value;
461                StringSetting stg = (StringSetting) pe.getValue();
462                String s = stg.getValue() == null ? "" : stg.getValue();
463                return super.getTableCellEditorComponent(table, s, isSelected, row, column);
464            }
465        }
466    
467        private void applyFilter() {
468            displayData.clear();
469            for (PrefEntry e : data) {
470    
471                String prefKey = e.getKey();
472                Setting valueSetting = e.getValue();
473                String prefValue = valueSetting.getValue() == null ? "" : valueSetting.getValue().toString();
474    
475                String input[] = txtFilter.getText().split("\\s+");
476                boolean canHas = true;
477    
478                // Make 'wmsplugin cache' search for e.g. 'cache.wmsplugin'
479                final String prefKeyLower = prefKey.toLowerCase();
480                final String prefValueLower = prefValue.toLowerCase();
481                for (String bit : input) {
482                    bit = bit.toLowerCase();
483                    if (!prefKeyLower.contains(bit) && !prefValueLower.contains(bit)) {
484                        canHas = false;
485                        break;
486                    }
487                }
488                if (canHas) {
489                    displayData.add(e);
490                }
491            }
492            model.fireTableDataChanged();
493        }
494    
495        @Override
496        public boolean ok() {
497            for (PrefEntry e : data) {
498                if (e.isChanged()) {
499                    Main.pref.putSetting(e.getKey(), e.getValue());
500                }
501            }
502            return false;
503        }
504    
505        private void resetPreference(final PreferenceTabbedPane gui, final JTable list) {
506            if (list.getSelectedRowCount() == 0) {
507                JOptionPane.showMessageDialog(
508                        gui,
509                        tr("Please select the row to delete."),
510                        tr("Warning"),
511                        JOptionPane.WARNING_MESSAGE
512                        );
513                return;
514            }
515            for (int row : list.getSelectedRows()) {
516                PrefEntry e = displayData.get(row);
517                e.reset();
518            }
519            model.fireTableDataChanged();
520        }
521    
522        private void addPreference(final PreferenceTabbedPane gui) {
523            JPanel p = new JPanel(new GridBagLayout());
524            p.add(new JLabel(tr("Key")), GBC.std().insets(0,0,5,0));
525            JTextField tkey = new JTextField("", 50);
526            p.add(tkey, GBC.eop().insets(5,0,0,0).fill(GBC.HORIZONTAL));
527    
528            p.add(new JLabel(tr("Select Setting Type:")), GBC.eol().insets(5,15,5,0));
529    
530            JRadioButton rbString = new JRadioButton(tr("Simple"));
531            JRadioButton rbList = new JRadioButton(tr("List"));
532            JRadioButton rbListList = new JRadioButton(tr("List of lists"));
533            JRadioButton rbMapList = new JRadioButton(tr("List of maps"));
534    
535            ButtonGroup group = new ButtonGroup();
536            group.add(rbString);
537            group.add(rbList);
538            group.add(rbListList);
539            group.add(rbMapList);
540    
541            p.add(rbString, GBC.eol());
542            p.add(rbList, GBC.eol());
543            p.add(rbListList, GBC.eol());
544            p.add(rbMapList, GBC.eol());
545    
546            rbString.setSelected(true);
547    
548            ExtendedDialog dlg = new ExtendedDialog(gui, tr("Add setting"), new String[] {tr("OK"), tr("Cancel")});
549            dlg.setButtonIcons(new String[] {"ok.png", "cancel.png"});
550            dlg.setContent(p);
551            dlg.showDialog();
552    
553            PrefEntry pe = null;
554            boolean ok = false;
555            if (dlg.getValue() == 1) {
556                if (rbString.isSelected()) {
557                    StringSetting sSetting = new StringSetting(null);
558                    pe = new PrefEntry(tkey.getText(), sSetting, sSetting, false);
559                    StringEditor sEditor = new StringEditor(gui, pe, sSetting);
560                    sEditor.showDialog();
561                    if (sEditor.getValue() == 1) {
562                        String data = sEditor.getData();
563                        if (!Utils.equal(sSetting.getValue(), data)) {
564                            pe.setValue(new StringSetting(data));
565                            ok = true;
566                        }
567                    }
568                } else if (rbList.isSelected()) {
569                    ListSetting lSetting = new ListSetting(null);
570                    pe = new PrefEntry(tkey.getText(), lSetting, lSetting, false);
571                    ListEditor lEditor = new ListEditor(gui, pe, lSetting);
572                    lEditor.showDialog();
573                    if (lEditor.getValue() == 1) {
574                        List<String> data = lEditor.getData();
575                        if (!Preferences.equalCollection(lSetting.getValue(), data)) {
576                            pe.setValue(new ListSetting(data));
577                            ok = true;
578                        }
579                    }
580                } else if (rbListList.isSelected()) {
581                    ListListSetting llSetting = new ListListSetting(null);
582                    pe = new PrefEntry(tkey.getText(), llSetting, llSetting, false);
583                    ListListEditor llEditor = new ListListEditor(gui, pe, llSetting);
584                    llEditor.showDialog();
585                    if (llEditor.getValue() == 1) {
586                        List<List<String>> data = llEditor.getData();
587                        if (!Preferences.equalArray((Collection) llSetting.getValue(), data)) {
588                            pe.setValue(new ListListSetting(data));
589                            ok = true;
590                        }
591                    }
592                } else if (rbMapList.isSelected()) {
593                    MapListSetting mlSetting = new MapListSetting(null);
594                    pe = new PrefEntry(tkey.getText(), mlSetting, mlSetting, false);
595                    MapListEditor mlEditor = new MapListEditor(gui, pe, mlSetting);
596                    mlEditor.showDialog();
597                    if (mlEditor.getValue() == 1) {
598                        List<Map<String, String>> data = mlEditor.getData();
599                        if (!Preferences.equalListOfStructs(mlSetting.getValue(), data)) {
600                            pe.setValue(new MapListSetting(data));
601                            ok = true;
602                        }
603                    }
604                }
605                if (ok) {
606                    data.add(pe);
607                    Collections.sort(data);
608                    applyFilter();
609                }
610            }
611        }
612    
613        private void editPreference(final PreferenceTabbedPane gui, final JTable list) {
614            if (list.getSelectedRowCount() != 1) {
615                JOptionPane.showMessageDialog(
616                        gui,
617                        tr("Please select the row to edit."),
618                        tr("Warning"),
619                        JOptionPane.WARNING_MESSAGE
620                        );
621                return;
622            }
623            final PrefEntry e = (PrefEntry) model.getValueAt(list.getSelectedRow(), 1);
624            Setting stg = e.getValue();
625            if (stg instanceof StringSetting) {
626                list.editCellAt(list.getSelectedRow(), 1);
627                Component editor = list.getEditorComponent();
628                if (editor != null) {
629                    editor.requestFocus();
630                }
631            } else if (stg instanceof ListSetting) {
632                ListSetting lSetting = (ListSetting) stg;
633                ListEditor lEditor = new ListEditor(gui, e, lSetting);
634                lEditor.showDialog();
635                if (lEditor.getValue() == 1) {
636                    List<String> data = lEditor.getData();
637                    if (!Preferences.equalCollection(lSetting.getValue(), data)) {
638                        e.setValue(new ListSetting(data));
639                        applyFilter();
640                    }
641                }
642            } else if (stg instanceof ListListSetting) {
643                ListListEditor llEditor = new ListListEditor(gui, e, (ListListSetting) stg);
644                llEditor.showDialog();
645                if (llEditor.getValue() == 1) {
646                    List<List<String>> data = llEditor.getData();
647                    if (!Preferences.equalArray((Collection) stg.getValue(), data)) {
648                        e.setValue(new ListListSetting(data));
649                        applyFilter();
650                    }
651                }
652            } else if (stg instanceof MapListSetting) {
653                MapListSetting mlSetting = (MapListSetting) stg;
654                MapListEditor mlEditor = new MapListEditor(gui, e, mlSetting);
655                mlEditor.showDialog();
656                if (mlEditor.getValue() == 1) {
657                    List<Map<String, String>> data = mlEditor.getData();
658                    if (!Preferences.equalListOfStructs(mlSetting.getValue(), data)) {
659                        e.setValue(new MapListSetting(data));
660                        applyFilter();
661                    }
662                }
663            }
664        }
665    }