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.ArrayList; 010import java.util.Collection; 011import java.util.Collections; 012import java.util.LinkedList; 013import java.util.List; 014 015import javax.swing.JOptionPane; 016 017import org.openstreetmap.josm.Main; 018import org.openstreetmap.josm.command.ChangeCommand; 019import org.openstreetmap.josm.command.Command; 020import org.openstreetmap.josm.command.SequenceCommand; 021import org.openstreetmap.josm.corrector.ReverseWayNoTagCorrector; 022import org.openstreetmap.josm.corrector.ReverseWayTagCorrector; 023import org.openstreetmap.josm.corrector.UserCancelException; 024import org.openstreetmap.josm.data.osm.Node; 025import org.openstreetmap.josm.data.osm.OsmPrimitive; 026import org.openstreetmap.josm.data.osm.Way; 027import org.openstreetmap.josm.gui.Notification; 028import org.openstreetmap.josm.tools.Shortcut; 029import org.openstreetmap.josm.tools.Utils; 030 031public final class ReverseWayAction extends JosmAction { 032 033 public static class ReverseWayResult { 034 private Way newWay; 035 private Collection<Command> tagCorrectionCommands; 036 private Command reverseCommand; 037 038 public ReverseWayResult(Way newWay, Collection<Command> tagCorrectionCommands, Command reverseCommand) { 039 this.newWay = newWay; 040 this.tagCorrectionCommands = tagCorrectionCommands; 041 this.reverseCommand = reverseCommand; 042 } 043 044 public Way getNewWay() { 045 return newWay; 046 } 047 048 public Collection<Command> getCommands() { 049 List<Command> c = new ArrayList<>(); 050 c.addAll(tagCorrectionCommands); 051 c.add(reverseCommand); 052 return c; 053 } 054 055 public Command getAsSequenceCommand() { 056 return new SequenceCommand(tr("Reverse way"), getCommands()); 057 } 058 059 public Command getReverseCommand() { 060 return reverseCommand; 061 } 062 063 public Collection<Command> getTagCorrectionCommands() { 064 return tagCorrectionCommands; 065 } 066 } 067 068 public ReverseWayAction() { 069 super(tr("Reverse Ways"), "wayflip", tr("Reverse the direction of all selected ways."), 070 Shortcut.registerShortcut("tools:reverse", tr("Tool: {0}", tr("Reverse Ways")), KeyEvent.VK_R, Shortcut.DIRECT), true); 071 putValue("help", ht("/Action/ReverseWays")); 072 } 073 074 @Override 075 public void actionPerformed(ActionEvent e) { 076 if (! isEnabled()) 077 return; 078 if (getCurrentDataSet() == null) 079 return; 080 081 final Collection<Way> sel = getCurrentDataSet().getSelectedWays(); 082 if (sel.isEmpty()) { 083 new Notification( 084 tr("Please select at least one way.")) 085 .setIcon(JOptionPane.INFORMATION_MESSAGE) 086 .setDuration(Notification.TIME_SHORT) 087 .show(); 088 return; 089 } 090 091 boolean propertiesUpdated = false; 092 Collection<Command> c = new LinkedList<>(); 093 for (Way w : sel) { 094 ReverseWayResult revResult; 095 try { 096 revResult = reverseWay(w); 097 } catch (UserCancelException ex) { 098 return; 099 } 100 c.addAll(revResult.getCommands()); 101 propertiesUpdated |= !revResult.getTagCorrectionCommands().isEmpty(); 102 } 103 Main.main.undoRedo.add(new SequenceCommand(tr("Reverse ways"), c)); 104 if (propertiesUpdated) { 105 getCurrentDataSet().fireSelectionChanged(); 106 } 107 Main.map.repaint(); 108 } 109 110 /** 111 * @param w the way 112 * @return the reverse command and the tag correction commands 113 * @throws UserCancelException if user cancels a reverse warning dialog 114 */ 115 public static ReverseWayResult reverseWay(Way w) throws UserCancelException { 116 ReverseWayNoTagCorrector.checkAndConfirmReverseWay(w); 117 Way wnew = new Way(w); 118 List<Node> nodesCopy = wnew.getNodes(); 119 Collections.reverse(nodesCopy); 120 wnew.setNodes(nodesCopy); 121 122 Collection<Command> corrCmds = Collections.<Command>emptyList(); 123 if (Main.pref.getBoolean("tag-correction.reverse-way", true)) { 124 corrCmds = (new ReverseWayTagCorrector()).execute(w, wnew); 125 } 126 return new ReverseWayResult(wnew, corrCmds, new ChangeCommand(w, wnew)); 127 } 128 129 protected int getNumWaysInSelection() { 130 if (getCurrentDataSet() == null) return 0; 131 int ret = 0; 132 for (OsmPrimitive primitive : getCurrentDataSet().getSelected()) { 133 if (primitive instanceof Way) { 134 ret++; 135 } 136 } 137 return ret; 138 } 139 140 @Override 141 protected void updateEnabledState() { 142 if (getCurrentDataSet() == null) { 143 setEnabled(false); 144 } else { 145 updateEnabledState(getCurrentDataSet().getSelected()); 146 } 147 } 148 149 @Override 150 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) { 151 setEnabled(Utils.exists(selection, OsmPrimitive.wayPredicate)); 152 } 153}