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.data.osm.DataSet; 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.UserCancelException; 030import org.openstreetmap.josm.tools.Utils; 031 032public final class ReverseWayAction extends JosmAction { 033 034 public static class ReverseWayResult { 035 private final Way newWay; 036 private final Collection<Command> tagCorrectionCommands; 037 private final Command reverseCommand; 038 039 public ReverseWayResult(Way newWay, Collection<Command> tagCorrectionCommands, Command reverseCommand) { 040 this.newWay = newWay; 041 this.tagCorrectionCommands = tagCorrectionCommands; 042 this.reverseCommand = reverseCommand; 043 } 044 045 public Way getNewWay() { 046 return newWay; 047 } 048 049 public Collection<Command> getCommands() { 050 List<Command> c = new ArrayList<>(); 051 c.addAll(tagCorrectionCommands); 052 c.add(reverseCommand); 053 return c; 054 } 055 056 public Command getAsSequenceCommand() { 057 return new SequenceCommand(tr("Reverse way"), getCommands()); 058 } 059 060 public Command getReverseCommand() { 061 return reverseCommand; 062 } 063 064 public Collection<Command> getTagCorrectionCommands() { 065 return tagCorrectionCommands; 066 } 067 } 068 069 public ReverseWayAction() { 070 super(tr("Reverse Ways"), "wayflip", tr("Reverse the direction of all selected ways."), 071 Shortcut.registerShortcut("tools:reverse", tr("Tool: {0}", tr("Reverse Ways")), KeyEvent.VK_R, Shortcut.DIRECT), true); 072 putValue("help", ht("/Action/ReverseWays")); 073 } 074 075 @Override 076 public void actionPerformed(ActionEvent e) { 077 DataSet ds = getLayerManager().getEditDataSet(); 078 if (!isEnabled() || ds == null) 079 return; 080 081 final Collection<Way> sel = ds.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 Main.trace(ex); 099 return; 100 } 101 c.addAll(revResult.getCommands()); 102 propertiesUpdated |= !revResult.getTagCorrectionCommands().isEmpty(); 103 } 104 Main.main.undoRedo.add(new SequenceCommand(tr("Reverse ways"), c)); 105 // FIXME: This should be handled by undoRedo. 106 if (propertiesUpdated) { 107 ds.fireSelectionChanged(); 108 } 109 } 110 111 /** 112 * @param w the way 113 * @return the reverse command and the tag correction commands 114 * @throws UserCancelException if user cancels a reverse warning dialog 115 */ 116 public static ReverseWayResult reverseWay(Way w) throws UserCancelException { 117 ReverseWayNoTagCorrector.checkAndConfirmReverseWay(w); 118 Way wnew = new Way(w); 119 List<Node> nodesCopy = wnew.getNodes(); 120 Collections.reverse(nodesCopy); 121 wnew.setNodes(nodesCopy); 122 123 Collection<Command> corrCmds = Collections.<Command>emptyList(); 124 if (Main.pref.getBoolean("tag-correction.reverse-way", true)) { 125 corrCmds = (new ReverseWayTagCorrector()).execute(w, wnew); 126 } 127 return new ReverseWayResult(wnew, corrCmds, new ChangeCommand(w, wnew)); 128 } 129 130 @Override 131 protected void updateEnabledState() { 132 DataSet ds = getLayerManager().getEditDataSet(); 133 if (ds == null) { 134 setEnabled(false); 135 } else { 136 updateEnabledState(ds.getSelected()); 137 } 138 } 139 140 @Override 141 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) { 142 setEnabled(Utils.exists(selection, OsmPrimitive.wayPredicate)); 143 } 144}