001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.layer; 003 004import static org.openstreetmap.josm.gui.help.HelpUtil.ht; 005import static org.openstreetmap.josm.tools.I18n.tr; 006 007import java.awt.Color; 008import java.awt.Component; 009import java.awt.event.ActionEvent; 010import java.util.Collections; 011import java.util.List; 012import java.util.Objects; 013import java.util.stream.Collectors; 014 015import javax.swing.AbstractAction; 016import javax.swing.Action; 017import javax.swing.JColorChooser; 018import javax.swing.JMenuItem; 019import javax.swing.JOptionPane; 020 021import org.openstreetmap.josm.data.preferences.AbstractProperty; 022import org.openstreetmap.josm.gui.MainApplication; 023import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 024import org.openstreetmap.josm.gui.layer.Layer.LayerAction; 025import org.openstreetmap.josm.gui.layer.Layer.MultiLayerAction; 026import org.openstreetmap.josm.tools.CheckParameterUtil; 027import org.openstreetmap.josm.tools.ImageProvider; 028 029/** 030 * Action to show a dialog for picking a color. 031 * 032 * By calling this action, the user can choose a color to customize the painting 033 * of a certain {@link GpxLayer} or {@link org.openstreetmap.josm.gui.layer.markerlayer.MarkerLayer}. 034 */ 035public class CustomizeColor extends AbstractAction implements LayerAction, MultiLayerAction { 036 private final transient List<AbstractProperty<Color>> colors; 037 038 /** 039 * Constructs a new {@code CustomizeColor} for a given list of layers. 040 * @param l list of layers 041 */ 042 public CustomizeColor(List<Layer> l) { 043 super(tr("Customize Color")); 044 new ImageProvider("colorchooser").getResource().attachImageIcon(this, true); 045 colors = l.stream().map(Layer::getColorProperty).collect(Collectors.toList()); 046 CheckParameterUtil.ensureThat(colors.stream().allMatch(Objects::nonNull), "All layers must have colors."); 047 putValue("help", ht("/Action/LayerCustomizeColor")); 048 } 049 050 /** 051 * Constructs a new {@code CustomizeColor} for a single layer. 052 * @param l layer 053 */ 054 public CustomizeColor(Layer l) { 055 this(Collections.singletonList(l)); 056 } 057 058 @Override 059 public boolean supportLayers(List<Layer> layers) { 060 return layers.stream().allMatch(l -> l.getColorProperty() != null); 061 } 062 063 @Override 064 public Component createMenuComponent() { 065 return new JMenuItem(this); 066 } 067 068 @Override 069 public Action getMultiLayerAction(List<Layer> layers) { 070 return new CustomizeColor(layers); 071 } 072 073 @Override 074 public void actionPerformed(ActionEvent e) { 075 Color cl = colors.stream().map(AbstractProperty::get).filter(Objects::nonNull).findAny().orElse(Color.GRAY); 076 JColorChooser c = new JColorChooser(cl); 077 Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")}; 078 int answer = JOptionPane.showOptionDialog( 079 MainApplication.getMainFrame(), 080 c, 081 tr("Choose a color"), 082 JOptionPane.OK_CANCEL_OPTION, 083 JOptionPane.PLAIN_MESSAGE, 084 null, 085 options, 086 options[0] 087 ); 088 switch (answer) { 089 case 0: 090 colors.stream().forEach(prop -> prop.put(c.getColor())); 091 break; 092 case 1: 093 return; 094 case 2: 095 colors.stream().forEach(prop -> prop.put(null)); 096 break; 097 } 098 // TODO: Make the layer dialog listen to property change events so that this is not needed any more. 099 LayerListDialog.getInstance().repaint(); 100 } 101}