source: josm/trunk/src/org/openstreetmap/josm/actions/PasteAction.java@ 8666

Last change on this file since 8666 was 8513, checked in by Don-vip, 9 years ago

checkstyle: blocks

  • Property svn:eol-style set to native
File size: 8.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2// Author: David Earl
3package org.openstreetmap.josm.actions;
4
5import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
6import static org.openstreetmap.josm.tools.I18n.tr;
7
8import java.awt.MouseInfo;
9import java.awt.Point;
10import java.awt.event.ActionEvent;
11import java.awt.event.KeyEvent;
12import java.util.ArrayList;
13import java.util.HashMap;
14import java.util.List;
15import java.util.Map;
16
17import org.openstreetmap.josm.Main;
18import org.openstreetmap.josm.command.AddPrimitivesCommand;
19import org.openstreetmap.josm.data.coor.EastNorth;
20import org.openstreetmap.josm.data.osm.NodeData;
21import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
22import org.openstreetmap.josm.data.osm.PrimitiveData;
23import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy;
24import org.openstreetmap.josm.data.osm.PrimitiveDeepCopy.PasteBufferChangedListener;
25import org.openstreetmap.josm.data.osm.RelationData;
26import org.openstreetmap.josm.data.osm.RelationMemberData;
27import org.openstreetmap.josm.data.osm.WayData;
28import org.openstreetmap.josm.gui.ExtendedDialog;
29import org.openstreetmap.josm.gui.layer.Layer;
30import org.openstreetmap.josm.tools.Shortcut;
31
32/**
33 * Paste OSM primitives from clipboard to the current edit layer.
34 * @since 404
35 */
36public final class PasteAction extends JosmAction implements PasteBufferChangedListener {
37
38 /**
39 * Constructs a new {@code PasteAction}.
40 */
41 public PasteAction() {
42 super(tr("Paste"), "paste", tr("Paste contents of paste buffer."),
43 Shortcut.registerShortcut("system:paste", tr("Edit: {0}", tr("Paste")), KeyEvent.VK_V, Shortcut.CTRL), true);
44 putValue("help", ht("/Action/Paste"));
45 // CUA shortcut for paste (https://en.wikipedia.org/wiki/IBM_Common_User_Access#Description)
46 Main.registerActionShortcut(this,
47 Shortcut.registerShortcut("system:paste:cua", tr("Edit: {0}", tr("Paste")), KeyEvent.VK_INSERT, Shortcut.SHIFT));
48 Main.pasteBuffer.addPasteBufferChangedListener(this);
49 }
50
51 @Override
52 public void actionPerformed(ActionEvent e) {
53 if (!isEnabled())
54 return;
55 pasteData(Main.pasteBuffer, Main.pasteSource, e);
56 }
57
58 /**
59 * Paste OSM primitives from the given paste buffer and OSM data layer source to the current edit layer.
60 * @param pasteBuffer The paste buffer containing primitive ids to copy
61 * @param source The OSM data layer used to look for primitive ids
62 * @param e The ActionEvent that triggered this operation
63 */
64 public void pasteData(PrimitiveDeepCopy pasteBuffer, Layer source, ActionEvent e) {
65 /* Find the middle of the pasteBuffer area */
66 double maxEast = -1E100, minEast = 1E100, maxNorth = -1E100, minNorth = 1E100;
67 boolean incomplete = false;
68 for (PrimitiveData data : pasteBuffer.getAll()) {
69 if (data instanceof NodeData) {
70 NodeData n = (NodeData) data;
71 if (n.getEastNorth() != null) {
72 double east = n.getEastNorth().east();
73 double north = n.getEastNorth().north();
74 if (east > maxEast) {
75 maxEast = east;
76 }
77 if (east < minEast) {
78 minEast = east;
79 }
80 if (north > maxNorth) {
81 maxNorth = north;
82 }
83 if (north < minNorth) {
84 minNorth = north;
85 }
86 }
87 }
88 if (data.isIncomplete()) {
89 incomplete = true;
90 }
91 }
92
93 // Allow to cancel paste if there are incomplete primitives
94 if (incomplete) {
95 if (!confirmDeleteIncomplete()) return;
96 }
97
98 // default to paste in center of map (pasted via menu or cursor not in MapView)
99 EastNorth mPosition = Main.map.mapView.getCenter();
100 // We previously checked for modifier to know if the action has been trigerred via shortcut or via menu
101 // But this does not work if the shortcut is changed to a single key (see #9055)
102 // Observed behaviour: getActionCommand() returns Action.NAME when triggered via menu, but shortcut text when triggered with it
103 if (e != null && !getValue(NAME).equals(e.getActionCommand())) {
104 final Point mp = MouseInfo.getPointerInfo().getLocation();
105 final Point tl = Main.map.mapView.getLocationOnScreen();
106 final Point pos = new Point(mp.x-tl.x, mp.y-tl.y);
107 if (Main.map.mapView.contains(pos)) {
108 mPosition = Main.map.mapView.getEastNorth(pos.x, pos.y);
109 }
110 }
111
112 double offsetEast = mPosition.east() - (maxEast + minEast)/2.0;
113 double offsetNorth = mPosition.north() - (maxNorth + minNorth)/2.0;
114
115 // Make a copy of pasteBuffer and map from old id to copied data id
116 List<PrimitiveData> bufferCopy = new ArrayList<>();
117 List<PrimitiveData> toSelect = new ArrayList<>();
118 Map<Long, Long> newNodeIds = new HashMap<>();
119 Map<Long, Long> newWayIds = new HashMap<>();
120 Map<Long, Long> newRelationIds = new HashMap<>();
121 for (PrimitiveData data: pasteBuffer.getAll()) {
122 if (data.isIncomplete()) {
123 continue;
124 }
125 PrimitiveData copy = data.makeCopy();
126 copy.clearOsmMetadata();
127 if (data instanceof NodeData) {
128 newNodeIds.put(data.getUniqueId(), copy.getUniqueId());
129 } else if (data instanceof WayData) {
130 newWayIds.put(data.getUniqueId(), copy.getUniqueId());
131 } else if (data instanceof RelationData) {
132 newRelationIds.put(data.getUniqueId(), copy.getUniqueId());
133 }
134 bufferCopy.add(copy);
135 if (pasteBuffer.getDirectlyAdded().contains(data)) {
136 toSelect.add(copy);
137 }
138 }
139
140 // Update references in copied buffer
141 for (PrimitiveData data:bufferCopy) {
142 if (data instanceof NodeData) {
143 NodeData nodeData = (NodeData) data;
144 if (Main.main.getEditLayer() == source) {
145 nodeData.setEastNorth(nodeData.getEastNorth().add(offsetEast, offsetNorth));
146 }
147 } else if (data instanceof WayData) {
148 List<Long> newNodes = new ArrayList<>();
149 for (Long oldNodeId: ((WayData) data).getNodes()) {
150 Long newNodeId = newNodeIds.get(oldNodeId);
151 if (newNodeId != null) {
152 newNodes.add(newNodeId);
153 }
154 }
155 ((WayData) data).setNodes(newNodes);
156 } else if (data instanceof RelationData) {
157 List<RelationMemberData> newMembers = new ArrayList<>();
158 for (RelationMemberData member: ((RelationData) data).getMembers()) {
159 OsmPrimitiveType memberType = member.getMemberType();
160 Long newId = null;
161 switch (memberType) {
162 case NODE:
163 newId = newNodeIds.get(member.getMemberId());
164 break;
165 case WAY:
166 newId = newWayIds.get(member.getMemberId());
167 break;
168 case RELATION:
169 newId = newRelationIds.get(member.getMemberId());
170 break;
171 }
172 if (newId != null) {
173 newMembers.add(new RelationMemberData(member.getRole(), memberType, newId));
174 }
175 }
176 ((RelationData) data).setMembers(newMembers);
177 }
178 }
179
180 /* Now execute the commands to add the duplicated contents of the paste buffer to the map */
181 Main.main.undoRedo.add(new AddPrimitivesCommand(bufferCopy, toSelect));
182 Main.map.mapView.repaint();
183 }
184
185 protected boolean confirmDeleteIncomplete() {
186 ExtendedDialog ed = new ExtendedDialog(Main.parent,
187 tr("Delete incomplete members?"),
188 new String[] {tr("Paste without incomplete members"), tr("Cancel")});
189 ed.setButtonIcons(new String[] {"dialogs/relation/deletemembers", "cancel"});
190 ed.setContent(tr("The copied data contains incomplete objects. "
191 + "When pasting the incomplete objects are removed. "
192 + "Do you want to paste the data without the incomplete objects?"));
193 ed.showDialog();
194 return ed.getValue() == 1;
195 }
196
197 @Override
198 protected void updateEnabledState() {
199 if (getCurrentDataSet() == null || Main.pasteBuffer == null) {
200 setEnabled(false);
201 return;
202 }
203 setEnabled(!Main.pasteBuffer.isEmpty());
204 }
205
206 @Override
207 public void pasteBufferChanged(PrimitiveDeepCopy pasteBuffer) {
208 updateEnabledState();
209 }
210}
Note: See TracBrowser for help on using the repository browser.