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; 012 013import javax.swing.JOptionPane; 014 015import org.openstreetmap.josm.Main; 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.Notification; 023import org.openstreetmap.josm.tools.Shortcut; 024 025/** 026 * Mirror the selected nodes or ways along the vertical axis 027 * 028 * Note: If a ways are selected, their nodes are mirrored 029 * 030 * @author Teemu Koskinen 031 */ 032public final class MirrorAction extends JosmAction { 033 034 /** 035 * Constructs a new {@code MirrorAction}. 036 */ 037 public MirrorAction() { 038 super(tr("Mirror"), "mirror", tr("Mirror selected nodes and ways."), 039 Shortcut.registerShortcut("tools:mirror", tr("Tool: {0}", tr("Mirror")), 040 KeyEvent.VK_M, Shortcut.SHIFT), true); 041 putValue("help", ht("/Action/Mirror")); 042 } 043 044 @Override 045 public void actionPerformed(ActionEvent e) { 046 Collection<OsmPrimitive> sel = getCurrentDataSet().getSelected(); 047 HashSet<Node> nodes = new HashSet<>(); 048 049 for (OsmPrimitive osm : sel) { 050 if (osm instanceof Node) { 051 nodes.add((Node)osm); 052 } else if (osm instanceof Way) { 053 nodes.addAll(((Way)osm).getNodes()); 054 } 055 } 056 057 if (nodes.isEmpty()) { 058 new Notification( 059 tr("Please select at least one node or way.")) 060 .setIcon(JOptionPane.INFORMATION_MESSAGE) 061 .setDuration(Notification.TIME_SHORT) 062 .show(); 063 return; 064 } 065 066 double minEast = 20000000000.0; 067 double maxEast = -20000000000.0; 068 for (Node n : nodes) { 069 double east = n.getEastNorth().east(); 070 minEast = Math.min(minEast, east); 071 maxEast = Math.max(maxEast, east); 072 } 073 double middle = (minEast + maxEast) / 2; 074 075 Collection<Command> cmds = new LinkedList<>(); 076 077 for (Node n : nodes) { 078 cmds.add(new MoveCommand(n, 2 * (middle - n.getEastNorth().east()), 0.0)); 079 } 080 081 Main.main.undoRedo.add(new SequenceCommand(tr("Mirror"), cmds)); 082 Main.map.repaint(); 083 } 084 085 @Override 086 protected void updateEnabledState() { 087 if (getCurrentDataSet() == null) { 088 setEnabled(false); 089 } else { 090 updateEnabledState(getCurrentDataSet().getSelected()); 091 } 092 } 093 094 @Override 095 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) { 096 setEnabled(selection != null && !selection.isEmpty()); 097 } 098}