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