001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.preferences.display; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Color; 007import java.awt.Dimension; 008import java.awt.GridBagLayout; 009import java.awt.event.MouseAdapter; 010import java.awt.event.MouseEvent; 011import java.util.ArrayList; 012import java.util.HashMap; 013import java.util.List; 014import java.util.Map; 015import java.util.TreeMap; 016import java.util.Vector; 017 018import javax.swing.BorderFactory; 019import javax.swing.Box; 020import javax.swing.JButton; 021import javax.swing.JColorChooser; 022import javax.swing.JLabel; 023import javax.swing.JOptionPane; 024import javax.swing.JPanel; 025import javax.swing.JScrollPane; 026import javax.swing.JTable; 027import javax.swing.ListSelectionModel; 028import javax.swing.event.ListSelectionEvent; 029import javax.swing.table.DefaultTableModel; 030import javax.swing.table.TableCellRenderer; 031 032import org.openstreetmap.josm.Main; 033import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors; 034import org.openstreetmap.josm.data.validation.Severity; 035import org.openstreetmap.josm.gui.MapScaler; 036import org.openstreetmap.josm.gui.MapStatus; 037import org.openstreetmap.josm.gui.conflict.ConflictColors; 038import org.openstreetmap.josm.gui.dialogs.ConflictDialog; 039import org.openstreetmap.josm.gui.layer.ImageryLayer; 040import org.openstreetmap.josm.gui.layer.OsmDataLayer; 041import org.openstreetmap.josm.gui.layer.gpx.GpxDrawHelper; 042import org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer; 043import org.openstreetmap.josm.gui.preferences.PreferenceSetting; 044import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory; 045import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; 046import org.openstreetmap.josm.gui.preferences.SubPreferenceSetting; 047import org.openstreetmap.josm.gui.preferences.TabPreferenceSetting; 048import org.openstreetmap.josm.gui.util.GuiHelper; 049import org.openstreetmap.josm.tools.ColorHelper; 050import org.openstreetmap.josm.tools.GBC; 051 052/** 053 * Color preferences. 054 */ 055public class ColorPreference implements SubPreferenceSetting { 056 057 /** 058 * Factory used to create a new {@code ColorPreference}. 059 */ 060 public static class Factory implements PreferenceSettingFactory { 061 @Override 062 public PreferenceSetting createPreferenceSetting() { 063 return new ColorPreference(); 064 } 065 } 066 067 private DefaultTableModel tableModel; 068 private JTable colors; 069 private final List<String> del = new ArrayList<>(); 070 071 private JButton colorEdit; 072 private JButton defaultSet; 073 private JButton remove; 074 075 /** 076 * Set the colors to be shown in the preference table. This method creates a table model if 077 * none exists and overwrites all existing values. 078 * @param colorMap the map holding the colors 079 * (key = color id (without prefixes, so only <code>background</code>; not <code>color.background</code>), 080 * value = html representation of the color. 081 */ 082 public void setColorModel(Map<String, String> colorMap) { 083 if (tableModel == null) { 084 tableModel = new DefaultTableModel(); 085 tableModel.addColumn(tr("Name")); 086 tableModel.addColumn(tr("Color")); 087 } 088 089 // clear old model: 090 while (tableModel.getRowCount() > 0) { 091 tableModel.removeRow(0); 092 } 093 // fill model with colors: 094 Map<String, String> colorKeyList = new TreeMap<>(); 095 Map<String, String> colorKeyListMappaint = new TreeMap<>(); 096 Map<String, String> colorKeyListLayer = new TreeMap<>(); 097 for (String key : colorMap.keySet()) { 098 if (key.startsWith("layer ")) { 099 colorKeyListLayer.put(getName(key), key); 100 } else if (key.startsWith("mappaint.")) { 101 // use getName(key)+key, as getName() may be ambiguous 102 colorKeyListMappaint.put(getName(key)+key, key); 103 } else { 104 colorKeyList.put(getName(key), key); 105 } 106 } 107 addColorRows(colorMap, colorKeyList); 108 addColorRows(colorMap, colorKeyListMappaint); 109 addColorRows(colorMap, colorKeyListLayer); 110 if (this.colors != null) { 111 this.colors.repaint(); 112 } 113 } 114 115 private void addColorRows(Map<String, String> colorMap, Map<String, String> keyMap) { 116 for (String value : keyMap.values()) { 117 Vector<Object> row = new Vector<>(2); 118 String html = colorMap.get(value); 119 Color color = ColorHelper.html2color(html); 120 if (color == null) { 121 Main.warn("Unable to get color from '"+html+"' for color preference '"+value+'\''); 122 } 123 row.add(value); 124 row.add(color); 125 tableModel.addRow(row); 126 } 127 } 128 129 /** 130 * Returns a map with the colors in the table (key = color name without prefix, value = html color code). 131 * @return a map holding the colors. 132 */ 133 public Map<String, String> getColorModel() { 134 String key; 135 String value; 136 Map<String, String> colorMap = new HashMap<>(); 137 for (int row = 0; row < tableModel.getRowCount(); ++row) { 138 key = (String) tableModel.getValueAt(row, 0); 139 value = ColorHelper.color2html((Color) tableModel.getValueAt(row, 1)); 140 colorMap.put(key, value); 141 } 142 return colorMap; 143 } 144 145 private static String getName(String o) { 146 return Main.pref.getColorName(o); 147 } 148 149 @Override 150 public void addGui(final PreferenceTabbedPane gui) { 151 fixColorPrefixes(); 152 setColorModel(Main.pref.getAllColors()); 153 154 colorEdit = new JButton(tr("Choose")); 155 colorEdit.addActionListener(e -> { 156 int sel = colors.getSelectedRow(); 157 JColorChooser chooser = new JColorChooser((Color) colors.getValueAt(sel, 1)); 158 int answer = JOptionPane.showConfirmDialog( 159 gui, chooser, 160 tr("Choose a color for {0}", getName((String) colors.getValueAt(sel, 0))), 161 JOptionPane.OK_CANCEL_OPTION, 162 JOptionPane.PLAIN_MESSAGE); 163 if (answer == JOptionPane.OK_OPTION) { 164 colors.setValueAt(chooser.getColor(), sel, 1); 165 } 166 }); 167 defaultSet = new JButton(tr("Set to default")); 168 defaultSet.addActionListener(e -> { 169 int sel = colors.getSelectedRow(); 170 String name = (String) colors.getValueAt(sel, 0); 171 Color c = Main.pref.getDefaultColor(name); 172 if (c != null) { 173 colors.setValueAt(c, sel, 1); 174 } 175 }); 176 JButton defaultAll = new JButton(tr("Set all to default")); 177 defaultAll.addActionListener(e -> { 178 for (int i = 0; i < colors.getRowCount(); ++i) { 179 String name = (String) colors.getValueAt(i, 0); 180 Color c = Main.pref.getDefaultColor(name); 181 if (c != null) { 182 colors.setValueAt(c, i, 1); 183 } 184 } 185 }); 186 remove = new JButton(tr("Remove")); 187 remove.addActionListener(e -> { 188 int sel = colors.getSelectedRow(); 189 del.add((String) colors.getValueAt(sel, 0)); 190 tableModel.removeRow(sel); 191 }); 192 remove.setEnabled(false); 193 colorEdit.setEnabled(false); 194 defaultSet.setEnabled(false); 195 196 colors = new JTable(tableModel) { 197 @Override 198 public boolean isCellEditable(int row, int column) { 199 return false; 200 } 201 202 @Override public void valueChanged(ListSelectionEvent e) { 203 super.valueChanged(e); 204 int sel = getSelectedRow(); 205 remove.setEnabled(sel >= 0 && isRemoveColor(sel)); 206 colorEdit.setEnabled(sel >= 0); 207 defaultSet.setEnabled(sel >= 0); 208 } 209 }; 210 colors.addMouseListener(new MouseAdapter() { 211 @Override 212 public void mousePressed(MouseEvent me) { 213 if (me.getClickCount() == 2) { 214 colorEdit.doClick(); 215 } 216 } 217 }); 218 colors.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 219 final TableCellRenderer oldColorsRenderer = colors.getDefaultRenderer(Object.class); 220 colors.setDefaultRenderer(Object.class, (t, o, selected, focus, row, column) -> { 221 if (o == null) 222 return new JLabel(); 223 if (column == 1) { 224 Color c = (Color) o; 225 JLabel l = new JLabel(ColorHelper.color2html(c)); 226 GuiHelper.setBackgroundReadable(l, c); 227 l.setOpaque(true); 228 return l; 229 } 230 return oldColorsRenderer.getTableCellRendererComponent(t, getName(o.toString()), selected, focus, row, column); 231 }); 232 colors.getColumnModel().getColumn(1).setWidth(100); 233 colors.setToolTipText(tr("Colors used by different objects in JOSM.")); 234 colors.setPreferredScrollableViewportSize(new Dimension(100, 112)); 235 236 JPanel panel = new JPanel(new GridBagLayout()); 237 panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); 238 JScrollPane scrollpane = new JScrollPane(colors); 239 scrollpane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); 240 panel.add(scrollpane, GBC.eol().fill(GBC.BOTH)); 241 JPanel buttonPanel = new JPanel(new GridBagLayout()); 242 panel.add(buttonPanel, GBC.eol().insets(5, 0, 5, 5).fill(GBC.HORIZONTAL)); 243 buttonPanel.add(Box.createHorizontalGlue(), GBC.std().fill(GBC.HORIZONTAL)); 244 buttonPanel.add(colorEdit, GBC.std().insets(0, 5, 0, 0)); 245 buttonPanel.add(defaultSet, GBC.std().insets(5, 5, 5, 0)); 246 buttonPanel.add(defaultAll, GBC.std().insets(0, 5, 0, 0)); 247 buttonPanel.add(remove, GBC.std().insets(0, 5, 0, 0)); 248 gui.getDisplayPreference().addSubTab(this, tr("Colors"), panel); 249 } 250 251 Boolean isRemoveColor(int row) { 252 return ((String) colors.getValueAt(row, 0)).startsWith("layer "); 253 } 254 255 /** 256 * Add all missing color entries. 257 */ 258 private static void fixColorPrefixes() { 259 PaintColors.values(); 260 ConflictColors.getColors(); 261 Severity.getColors(); 262 MarkerLayer.getGenericColor(); 263 GpxDrawHelper.getGenericColor(); 264 OsmDataLayer.getOutsideColor(); 265 ImageryLayer.getFadeColor(); 266 MapScaler.getColor(); 267 MapStatus.getColors(); 268 ConflictDialog.getColor(); 269 } 270 271 @Override 272 public boolean ok() { 273 boolean ret = false; 274 for (String d : del) { 275 Main.pref.put("color."+d, null); 276 } 277 for (int i = 0; i < colors.getRowCount(); ++i) { 278 String key = (String) colors.getValueAt(i, 0); 279 if (Main.pref.putColor(key, (Color) colors.getValueAt(i, 1)) && key.startsWith("mappaint.")) { 280 ret = true; 281 } 282 } 283 OsmDataLayer.createHatchTexture(); 284 return ret; 285 } 286 287 @Override 288 public boolean isExpert() { 289 return false; 290 } 291 292 @Override 293 public TabPreferenceSetting getTabPreferenceSetting(final PreferenceTabbedPane gui) { 294 return gui.getDisplayPreference(); 295 } 296}