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.Main;
022import org.openstreetmap.josm.data.preferences.AbstractProperty;
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                Main.parent,
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}