source: josm/trunk/src/org/openstreetmap/josm/gui/preferences/display/ColorPreference.java @ 12953

Last change on this file since 12953 was 12953, checked in by bastiK, 14 months ago

see #15410 - highlight entries with modified color; fix "set to default"

  • Property svn:eol-style set to native
File size: 13.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.preferences.display;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.awt.Component;
8import java.awt.Dimension;
9import java.awt.Font;
10import java.awt.GridBagLayout;
11import java.awt.event.MouseAdapter;
12import java.awt.event.MouseEvent;
13import java.text.Collator;
14import java.util.ArrayList;
15import java.util.Collections;
16import java.util.HashMap;
17import java.util.List;
18import java.util.Map;
19import java.util.Objects;
20
21import javax.swing.BorderFactory;
22import javax.swing.Box;
23import javax.swing.JButton;
24import javax.swing.JColorChooser;
25import javax.swing.JLabel;
26import javax.swing.JOptionPane;
27import javax.swing.JPanel;
28import javax.swing.JScrollPane;
29import javax.swing.JTable;
30import javax.swing.ListSelectionModel;
31import javax.swing.event.ListSelectionEvent;
32import javax.swing.table.AbstractTableModel;
33import javax.swing.table.DefaultTableCellRenderer;
34
35import org.openstreetmap.josm.Main;
36import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
37import org.openstreetmap.josm.data.validation.Severity;
38import org.openstreetmap.josm.gui.MapScaler;
39import org.openstreetmap.josm.gui.MapStatus;
40import org.openstreetmap.josm.gui.conflict.ConflictColors;
41import org.openstreetmap.josm.gui.dialogs.ConflictDialog;
42import org.openstreetmap.josm.gui.layer.OsmDataLayer;
43import org.openstreetmap.josm.gui.layer.gpx.GpxDrawHelper;
44import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer;
45import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
46import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
47import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
48import org.openstreetmap.josm.gui.preferences.SubPreferenceSetting;
49import org.openstreetmap.josm.gui.preferences.TabPreferenceSetting;
50import org.openstreetmap.josm.gui.util.GuiHelper;
51import org.openstreetmap.josm.tools.CheckParameterUtil;
52import org.openstreetmap.josm.tools.ColorHelper;
53import org.openstreetmap.josm.tools.GBC;
54import org.openstreetmap.josm.tools.Logging;
55
56/**
57 * Color preferences.
58 */
59public class ColorPreference implements SubPreferenceSetting {
60
61    /**
62     * Factory used to create a new {@code ColorPreference}.
63     */
64    public static class Factory implements PreferenceSettingFactory {
65        @Override
66        public PreferenceSetting createPreferenceSetting() {
67            return new ColorPreference();
68        }
69    }
70
71    private ColorTableModel tableModel;
72    private JTable colors;
73
74    private JButton colorEdit;
75    private JButton defaultSet;
76    private JButton remove;
77
78    private static class ColorEntry implements Comparable<ColorEntry> {
79        String key;
80        Color color;
81
82        public ColorEntry(String key, String colorHtml) {
83            CheckParameterUtil.ensureParameterNotNull(key, "key");
84            this.key = key;
85            this.color = ColorHelper.html2color(colorHtml);
86            if (this.color == null) {
87                Logging.warn("Unable to get color from '"+colorHtml+"' for color preference '"+key+'\'');
88            }
89        }
90
91        public String getDisplay() {
92            return Main.pref.getColorName(key);
93        }
94
95        @Override
96        public int compareTo(ColorEntry o) {
97            if (o == null) return -1;
98            return Collator.getInstance().compare(getDisplay(), o.getDisplay());
99        }
100    }
101
102    private static class ColorTableModel extends AbstractTableModel {
103
104        private final List<ColorEntry> data;
105        private final List<ColorEntry> deleted;
106
107        public ColorTableModel() {
108            this.data = new ArrayList<>();
109            this.deleted = new ArrayList<>();
110        }
111
112        public void addEntry(ColorEntry entry) {
113            data.add(entry);
114        }
115
116        public void removeEntry(int row) {
117            deleted.add(data.get(row));
118            data.remove(row);
119            fireTableDataChanged();
120        }
121
122        public ColorEntry getEntry(int row) {
123            return data.get(row);
124        }
125
126        public List<ColorEntry> getData() {
127            return data;
128        }
129
130        public List<ColorEntry> getDeleted() {
131            return deleted;
132        }
133
134        public void clear() {
135            data.clear();
136            deleted.clear();
137        }
138
139        @Override
140        public int getRowCount() {
141            return data.size();
142        }
143
144        @Override
145        public int getColumnCount() {
146            return 2;
147        }
148
149        @Override
150        public Object getValueAt(int rowIndex, int columnIndex) {
151            return columnIndex == 0 ? data.get(rowIndex) : data.get(rowIndex).color;
152        }
153
154        @Override
155        public String getColumnName(int column) {
156            return column == 0 ? tr("Name") : tr("Color");
157        }
158
159        @Override
160        public boolean isCellEditable(int rowIndex, int columnIndex) {
161            return false;
162        }
163
164        @Override
165        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
166            if (columnIndex == 1 && aValue instanceof Color) {
167                data.get(rowIndex).color = (Color) aValue;
168                fireTableRowsUpdated(rowIndex, rowIndex);
169            }
170        }
171    }
172
173    /**
174     * Set the colors to be shown in the preference table. This method creates a table model if
175     * none exists and overwrites all existing values.
176     * @param colorMap the map holding the colors
177     * (key = color id (without prefixes, so only <code>background</code>; not <code>color.background</code>),
178     * value = html representation of the color.
179     */
180    public void setColorModel(Map<String, String> colorMap) {
181        if (tableModel == null) {
182            tableModel = new ColorTableModel();
183        }
184
185        tableModel.clear();
186        // fill model with colors:
187        List<ColorEntry> colorKeyList = new ArrayList<>();
188        List<ColorEntry> colorKeyListMappaint = new ArrayList<>();
189        List<ColorEntry> colorKeyListLayer = new ArrayList<>();
190        for (Map.Entry<String, String> e : colorMap.entrySet()) {
191            String key = e.getKey();
192            String html = e.getValue();
193            if (key.startsWith("layer.")) {
194                colorKeyListLayer.add(new ColorEntry(key, html));
195            } else if (key.startsWith("mappaint.")) {
196                colorKeyListMappaint.add(new ColorEntry(key, html));
197            } else {
198                colorKeyList.add(new ColorEntry(key, html));
199            }
200        }
201        addColorRows(colorKeyList);
202        addColorRows(colorKeyListMappaint);
203        addColorRows(colorKeyListLayer);
204        if (this.colors != null) {
205            this.colors.repaint();
206        }
207    }
208
209    private void addColorRows(List<ColorEntry> entries) {
210        Collections.sort(entries);
211        entries.forEach(tableModel::addEntry);
212    }
213
214    /**
215     * Returns a map with the colors in the table (key = color name without prefix, value = html color code).
216     * @return a map holding the colors.
217     */
218    public Map<String, String> getColorModel() {
219        Map<String, String> colorMap = new HashMap<>();
220        for (ColorEntry e : tableModel.getData()) {
221            colorMap.put(e.key, ColorHelper.color2html(e.color));
222        }
223        return colorMap;
224    }
225
226    @Override
227    public void addGui(final PreferenceTabbedPane gui) {
228        fixColorPrefixes();
229        setColorModel(Main.pref.getAllColors());
230
231        colorEdit = new JButton(tr("Choose"));
232        colorEdit.addActionListener(e -> {
233            int sel = colors.getSelectedRow();
234            ColorEntry ce = tableModel.getEntry(sel);
235            JColorChooser chooser = new JColorChooser(ce.color);
236            int answer = JOptionPane.showConfirmDialog(
237                    gui, chooser,
238                    tr("Choose a color for {0}", ce.getDisplay()),
239                    JOptionPane.OK_CANCEL_OPTION,
240                    JOptionPane.PLAIN_MESSAGE);
241            if (answer == JOptionPane.OK_OPTION) {
242                colors.setValueAt(chooser.getColor(), sel, 1);
243            }
244        });
245        defaultSet = new JButton(tr("Set to default"));
246        defaultSet.addActionListener(e -> {
247            int sel = colors.getSelectedRow();
248            ColorEntry ce = tableModel.getEntry(sel);
249            Color c = Main.pref.getDefaultColor(ce.key);
250            if (c != null) {
251                colors.setValueAt(c, sel, 1);
252            }
253        });
254        JButton defaultAll = new JButton(tr("Set all to default"));
255        defaultAll.addActionListener(e -> {
256            List<ColorEntry> data = tableModel.getData();
257            for (int i = 0; i < data.size(); ++i) {
258                ColorEntry ce = data.get(i);
259                Color c = Main.pref.getDefaultColor(ce.key);
260                if (c != null) {
261                    colors.setValueAt(c, i, 1);
262                }
263            }
264        });
265        remove = new JButton(tr("Remove"));
266        remove.addActionListener(e -> {
267            int sel = colors.getSelectedRow();
268            tableModel.removeEntry(sel);
269        });
270        remove.setEnabled(false);
271        colorEdit.setEnabled(false);
272        defaultSet.setEnabled(false);
273
274        colors = new JTable(tableModel) {
275            @Override public void valueChanged(ListSelectionEvent e) {
276                super.valueChanged(e);
277                int sel = getSelectedRow();
278                remove.setEnabled(sel >= 0 && isRemoveColor(sel));
279                colorEdit.setEnabled(sel >= 0);
280                defaultSet.setEnabled(sel >= 0);
281            }
282        };
283        colors.addMouseListener(new MouseAdapter() {
284            @Override
285            public void mousePressed(MouseEvent me) {
286                if (me.getClickCount() == 2) {
287                    colorEdit.doClick();
288                }
289            }
290        });
291        colors.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
292        colors.getColumnModel().getColumn(0).setCellRenderer(new DefaultTableCellRenderer() {
293            @Override
294            public Component getTableCellRendererComponent(
295                    JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
296                Component comp = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
297                if (value != null && comp instanceof JLabel) {
298                    JLabel label = (JLabel) comp;
299                    ColorEntry e = (ColorEntry) value;
300                    label.setText(e.getDisplay());
301                    if (!Objects.equals(e.color, Main.pref.getDefaultColor(e.key))) {
302                        label.setFont(label.getFont().deriveFont(Font.BOLD));
303                    } else {
304                        label.setFont(label.getFont().deriveFont(Font.PLAIN));
305                    }
306                    return label;
307                }
308                return comp;
309            }
310        });
311        colors.getColumnModel().getColumn(1).setCellRenderer(new DefaultTableCellRenderer() {
312            @Override
313            public Component getTableCellRendererComponent(
314                    JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
315                Component comp = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
316                if (value != null && comp instanceof JLabel) {
317                    JLabel label = (JLabel) comp;
318                    Color c = (Color) value;
319                    label.setText(ColorHelper.color2html(c));
320                    GuiHelper.setBackgroundReadable(label, c);
321                    label.setOpaque(true);
322                    return label;
323                }
324                return comp;
325            }
326        });
327        colors.getColumnModel().getColumn(1).setWidth(100);
328        colors.setToolTipText(tr("Colors used by different objects in JOSM."));
329        colors.setPreferredScrollableViewportSize(new Dimension(100, 112));
330
331        JPanel panel = new JPanel(new GridBagLayout());
332        panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
333        JScrollPane scrollpane = new JScrollPane(colors);
334        scrollpane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
335        panel.add(scrollpane, GBC.eol().fill(GBC.BOTH));
336        JPanel buttonPanel = new JPanel(new GridBagLayout());
337        panel.add(buttonPanel, GBC.eol().insets(5, 0, 5, 5).fill(GBC.HORIZONTAL));
338        buttonPanel.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL));
339        buttonPanel.add(colorEdit, GBC.std().insets(0, 5, 0, 0));
340        buttonPanel.add(defaultSet, GBC.std().insets(5, 5, 5, 0));
341        buttonPanel.add(defaultAll, GBC.std().insets(0, 5, 0, 0));
342        buttonPanel.add(remove, GBC.std().insets(0, 5, 0, 0));
343        gui.getDisplayPreference().addSubTab(this, tr("Colors"), panel);
344    }
345
346    Boolean isRemoveColor(int row) {
347        return tableModel.getEntry(row).key.startsWith("layer.");
348    }
349
350    /**
351     * Add all missing color entries.
352     */
353    private static void fixColorPrefixes() {
354        PaintColors.values();
355        ConflictColors.getColors();
356        Severity.getColors();
357        MarkerLayer.getGenericColor();
358        GpxDrawHelper.getGenericColor();
359        OsmDataLayer.getOutsideColor();
360        MapScaler.getColor();
361        MapStatus.getColors();
362        ConflictDialog.getColor();
363    }
364
365    @Override
366    public boolean ok() {
367        boolean ret = false;
368        for (ColorEntry d : tableModel.getDeleted()) {
369            Main.pref.putColor(d.key, null);
370        }
371        for (ColorEntry e : tableModel.getData()) {
372            if (Main.pref.putColor(e.key, e.color) && e.key.startsWith("mappaint.")) {
373                ret = true;
374            }
375        }
376        OsmDataLayer.createHatchTexture();
377        return ret;
378    }
379
380    @Override
381    public boolean isExpert() {
382        return false;
383    }
384
385    @Override
386    public TabPreferenceSetting getTabPreferenceSetting(final PreferenceTabbedPane gui) {
387        return gui.getDisplayPreference();
388    }
389}
Note: See TracBrowser for help on using the repository browser.