001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.actions;
003
004import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
005import static org.openstreetmap.josm.tools.I18n.tr;
006
007import java.awt.event.ActionEvent;
008import java.awt.event.KeyEvent;
009import java.util.Collection;
010import java.util.HashSet;
011import java.util.LinkedList;
012import java.util.Set;
013
014import javax.swing.JOptionPane;
015
016import org.openstreetmap.josm.command.Command;
017import org.openstreetmap.josm.command.MoveCommand;
018import org.openstreetmap.josm.command.SequenceCommand;
019import org.openstreetmap.josm.data.osm.Node;
020import org.openstreetmap.josm.data.osm.OsmPrimitive;
021import org.openstreetmap.josm.data.osm.Way;
022import org.openstreetmap.josm.gui.MainApplication;
023import org.openstreetmap.josm.gui.Notification;
024import org.openstreetmap.josm.tools.Shortcut;
025
026/**
027 * Mirror the selected nodes or ways along the vertical axis
028 *
029 * Note: If a ways are selected, their nodes are mirrored
030 *
031 * @author Teemu Koskinen
032 */
033public final class MirrorAction extends JosmAction {
034
035    /**
036     * Constructs a new {@code MirrorAction}.
037     */
038    public MirrorAction() {
039        super(tr("Mirror"), "mirror", tr("Mirror selected nodes and ways."),
040                Shortcut.registerShortcut("tools:mirror", tr("Tool: {0}", tr("Mirror")),
041                        KeyEvent.VK_M, Shortcut.SHIFT), true);
042        putValue("help", ht("/Action/Mirror"));
043    }
044
045    @Override
046    public void actionPerformed(ActionEvent e) {
047        Collection<OsmPrimitive> sel = getLayerManager().getEditDataSet().getSelected();
048        Set<Node> nodes = new HashSet<>();
049
050        for (OsmPrimitive osm : sel) {
051            if (osm instanceof Node) {
052                nodes.add((Node) osm);
053            } else if (osm instanceof Way) {
054                nodes.addAll(((Way) osm).getNodes());
055            }
056        }
057
058        if (nodes.isEmpty()) {
059            new Notification(
060                    tr("Please select at least one node or way."))
061                    .setIcon(JOptionPane.INFORMATION_MESSAGE)
062                    .setDuration(Notification.TIME_SHORT)
063                    .show();
064            return;
065        }
066
067        double minEast = 20000000000.0;
068        double maxEast = -20000000000.0;
069        for (Node n : nodes) {
070            double east = n.getEastNorth().east();
071            minEast = Math.min(minEast, east);
072            maxEast = Math.max(maxEast, east);
073        }
074        double middle = (minEast + maxEast) / 2;
075
076        Collection<Command> cmds = new LinkedList<>();
077
078        for (Node n : nodes) {
079            cmds.add(new MoveCommand(n, 2 * (middle - n.getEastNorth().east()), 0.0));
080        }
081
082        MainApplication.undoRedo.add(new SequenceCommand(tr("Mirror"), cmds));
083    }
084
085    @Override
086    protected void updateEnabledState() {
087        updateEnabledStateOnCurrentSelection();
088    }
089
090    @Override
091    protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
092        updateEnabledStateOnModifiableSelection(selection);
093    }
094}