source: osm/applications/editors/josm/plugins/merge-overlap/src/mergeoverlap/MergeOverlapAction.java@ 30712

Last change on this file since 30712 was 30712, checked in by donvip, 11 years ago

[josm_mege_overlap] move duplicated code to "hack" package and move inner classes into separate files + code alignment of MyTagConflictResolver* to latest version of JOSM classes

File size: 20.9 KB
Line 
1package mergeoverlap;
2
3import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.combineTigerTags;
4import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.completeTagCollectionForEditing;
5import static org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing;
6import static org.openstreetmap.josm.tools.I18n.tr;
7
8import java.awt.event.ActionEvent;
9import java.awt.event.KeyEvent;
10import java.util.ArrayList;
11import java.util.Arrays;
12import java.util.Collection;
13import java.util.Collections;
14import java.util.HashMap;
15import java.util.HashSet;
16import java.util.Iterator;
17import java.util.LinkedHashSet;
18import java.util.LinkedList;
19import java.util.List;
20import java.util.Map;
21import java.util.Set;
22
23import javax.swing.JOptionPane;
24
25import mergeoverlap.hack.MyCombinePrimitiveResolverDialog;
26
27import org.openstreetmap.josm.Main;
28import org.openstreetmap.josm.actions.JosmAction;
29import org.openstreetmap.josm.actions.SplitWayAction;
30import org.openstreetmap.josm.actions.CombineWayAction.NodeGraph;
31import org.openstreetmap.josm.actions.SplitWayAction.SplitWayResult;
32import org.openstreetmap.josm.command.AddCommand;
33import org.openstreetmap.josm.command.ChangeCommand;
34import org.openstreetmap.josm.command.Command;
35import org.openstreetmap.josm.command.DeleteCommand;
36import org.openstreetmap.josm.command.SequenceCommand;
37import org.openstreetmap.josm.corrector.ReverseWayTagCorrector;
38import org.openstreetmap.josm.corrector.UserCancelException;
39import org.openstreetmap.josm.data.osm.Node;
40import org.openstreetmap.josm.data.osm.OsmPrimitive;
41import org.openstreetmap.josm.data.osm.Relation;
42import org.openstreetmap.josm.data.osm.RelationMember;
43import org.openstreetmap.josm.data.osm.TagCollection;
44import org.openstreetmap.josm.data.osm.Way;
45import org.openstreetmap.josm.gui.layer.OsmDataLayer;
46import org.openstreetmap.josm.tools.Pair;
47import org.openstreetmap.josm.tools.Shortcut;
48
49/**
50 * Merge overlapping part of ways.
51 */
52@SuppressWarnings("serial")
53public class MergeOverlapAction extends JosmAction {
54
55 public MergeOverlapAction() {
56 super(tr("Merge overlap"), "merge_overlap",
57 tr("Merge overlap of ways."),
58 Shortcut.registerShortcut("tools:mergeoverlap",tr("Tool: {0}", tr("Merge overlap")), KeyEvent.VK_O,
59 Shortcut.ALT_CTRL)
60 , true);
61 }
62
63 Map<Way, List<Relation>> relations = new HashMap<Way, List<Relation>>();
64 Map<Way, Way> oldWays = new HashMap<Way, Way>();
65 Map<Relation, Relation> newRelations = new HashMap<Relation, Relation>();
66 Set<Way> deletes = new HashSet<Way>();
67
68 /**
69 * The action button has been clicked
70 *
71 * @param e
72 * Action Event
73 */
74 @Override
75 public void actionPerformed(ActionEvent e) {
76
77 // List of selected ways
78 List<Way> ways = new ArrayList<Way>();
79 relations.clear();
80 newRelations.clear();
81
82 // For every selected way
83 for (OsmPrimitive osm : Main.main.getCurrentDataSet().getSelected()) {
84 if (osm instanceof Way && !osm.isDeleted()) {
85 Way way = (Way) osm;
86 ways.add(way);
87 List<Relation> rels = new ArrayList<Relation>();
88 for (Relation r : OsmPrimitive.getFilteredList(way
89 .getReferrers(), Relation.class)) {
90 rels.add(r);
91 }
92 relations.put(way, rels);
93 }
94 }
95
96 List<Way> sel = new ArrayList<Way>(ways);
97 Collection<Command> cmds = new LinkedList<Command>();
98
99 // *****
100 // split
101 // *****
102 for (Way way : ways) {
103 Set<Node> nodes = new HashSet<Node>();
104 for (Way opositWay : ways) {
105 if (way != opositWay) {
106 List<NodePos> nodesPos = new LinkedList<NodePos>();
107
108 int pos = 0;
109 for (Node node : way.getNodes()) {
110 int opositPos = 0;
111 for (Node opositNode : opositWay.getNodes()) {
112 if (node == opositNode) {
113 if (opositWay.isClosed()) {
114 opositPos %= opositWay.getNodesCount() - 1;
115 }
116 nodesPos.add(new NodePos(node, pos, opositPos));
117 break;
118 }
119 opositPos++;
120 }
121 pos++;
122 }
123
124 NodePos start = null;
125 NodePos end = null;
126 int increment = 0;
127
128 boolean hasFirst = false;
129 for (NodePos node : nodesPos) {
130 if (start == null) {
131 start = node;
132 } else {
133 if (end == null) {
134 if (follows(way, opositWay, start, node, 1)) {
135 end = node;
136 increment = +1;
137 } else if (follows(way, opositWay, start, node,
138 -1)) {
139 end = node;
140 increment = -1;
141 } else {
142 start = node;
143 end = null;
144 }
145 } else {
146 if (follows(way, opositWay, end, node,
147 increment)) {
148 end = node;
149 } else {
150 hasFirst = addNodes(start, end, way, nodes,
151 hasFirst);
152 start = node;
153 end = null;
154 }
155 }
156 }
157 }
158
159 if (start != null && end != null) {
160 hasFirst = addNodes(start, end, way, nodes, hasFirst);
161 start = null;
162 end = null;
163 }
164 }
165 }
166 if (!nodes.isEmpty() && !way.isClosed() || nodes.size() >= 2) {
167 List<List<Node>> wayChunks = SplitWayAction.buildSplitChunks(
168 way, new ArrayList<Node>(nodes));
169 SplitWayResult result = splitWay(getEditLayer(), way, wayChunks);
170
171 cmds.add(result.getCommand());
172 sel.remove(way);
173 sel.add(result.getOriginalWay());
174 sel.addAll(result.getNewWays());
175 List<Relation> rels = relations.remove(way);
176 relations.put(result.getOriginalWay(), rels);
177 for (Way w : result.getNewWays()) {
178 relations.put(w, rels);
179 }
180 }
181 }
182
183 // *****
184 // merge
185 // *****
186 ways = new ArrayList<Way>(sel);
187 while (!ways.isEmpty()) {
188 Way way = ways.get(0);
189 List<Way> combine = new ArrayList<Way>();
190 combine.add(way);
191 for (Way opositWay : ways) {
192 if (way != opositWay
193 && way.getNodesCount() == opositWay.getNodesCount()) {
194 boolean equals1 = true;
195 for (int i = 0; i < way.getNodesCount(); i++) {
196 if (way.getNode(i) != opositWay.getNode(i)) {
197 equals1 = false;
198 break;
199 }
200 }
201 boolean equals2 = true;
202 for (int i = 0; i < way.getNodesCount(); i++) {
203 if (way.getNode(i) != opositWay.getNode(way
204 .getNodesCount()
205 - i - 1)) {
206 equals2 = false;
207 break;
208 }
209 }
210 if (equals1 || equals2) {
211 combine.add(opositWay);
212 }
213 }
214 }
215 ways.removeAll(combine);
216 if (combine.size() > 1) {
217 sel.removeAll(combine);
218 // combine
219 Pair<Way, List<Command>> combineResult;
220 try {
221 combineResult = combineWaysWorker(combine);
222 } catch (UserCancelException e1) {
223 return;
224 }
225 sel.add(combineResult.a);
226 cmds.addAll(combineResult.b);
227 }
228 }
229
230 for (Relation old : newRelations.keySet()) {
231 cmds.add(new ChangeCommand(old, newRelations.get(old)));
232 }
233
234 List<Way> del = new LinkedList<Way>();
235 for (Way w : deletes) {
236 if (!w.isDeleted()) {
237 del.add(w);
238 }
239 }
240 if (!del.isEmpty()) {
241 cmds.add(new DeleteCommand(del));
242 }
243
244 // Commit
245 Main.main.undoRedo.add(new SequenceCommand(
246 tr("Merge Overlap (combine)"), cmds));
247 getCurrentDataSet().setSelected(sel);
248 Main.map.repaint();
249
250 relations.clear();
251 newRelations.clear();
252 oldWays.clear();
253 }
254
255 private class NodePos {
256 Node node;
257 int pos;
258 int opositPos;
259
260 NodePos(Node n, int p, int op) {
261 node = n;
262 pos = p;
263 opositPos = op;
264 }
265
266 @Override
267 public String toString() {
268 return "NodePos: " + pos + ", " + opositPos + ", " + node;
269 }
270 }
271
272 private boolean addNodes(NodePos start, NodePos end, Way way,
273 Set<Node> nodes, boolean hasFirst) {
274 if (way.isClosed()
275 || (start.node != way.getNode(0) && start.node != way
276 .getNode(way.getNodesCount() - 1))) {
277 hasFirst = hasFirst || start.node == way.getNode(0);
278 nodes.add(start.node);
279 }
280 if (way.isClosed()
281 || (end.node != way.getNode(0) && end.node != way.getNode(way
282 .getNodesCount() - 1))) {
283 if (hasFirst && (end.node == way.getNode(way.getNodesCount() - 1))) {
284 nodes.remove(way.getNode(0));
285 } else {
286 nodes.add(end.node);
287 }
288 }
289 return hasFirst;
290 }
291
292 private boolean follows(Way way1, Way way2, NodePos np1, NodePos np2,
293 int incr) {
294 if (way2.isClosed() && incr == 1
295 && np1.opositPos == way2.getNodesCount() - 2) {
296 return np2.pos == np1.pos + 1 && np2.opositPos == 0;
297 } else if (way2.isClosed() && incr == 1 && np1.opositPos == 0) {
298 return np2.pos == np1.pos && np2.opositPos == 0
299 || np2.pos == np1.pos + 1 && np2.opositPos == 1;
300 } else if (way2.isClosed() && incr == -1 && np1.opositPos == 0) {
301 return np2.pos == np1.pos && np2.opositPos == 0
302 || np2.pos == np1.pos + 1
303 && np2.opositPos == way2.getNodesCount() - 2;
304 } else {
305 return np2.pos == np1.pos + 1
306 && np2.opositPos == np1.opositPos + incr;
307 }
308 }
309
310 /**
311 * Splits a way
312 *
313 * @param layer
314 * @param way
315 * @param wayChunks
316 * @return
317 */
318 private SplitWayResult splitWay(OsmDataLayer layer, Way way,
319 List<List<Node>> wayChunks) {
320 // build a list of commands, and also a new selection list
321 Collection<Command> commandList = new ArrayList<Command>(wayChunks
322 .size());
323
324 Iterator<List<Node>> chunkIt = wayChunks.iterator();
325 Collection<String> nowarnroles = Main.pref.getCollection(
326 "way.split.roles.nowarn", Arrays.asList(new String[] { "outer",
327 "inner", "forward", "backward" }));
328
329 // First, change the original way
330 Way changedWay = new Way(way);
331 oldWays.put(changedWay, way);
332 changedWay.setNodes(chunkIt.next());
333 commandList.add(new ChangeCommand(way, changedWay));
334
335 List<Way> newWays = new ArrayList<Way>();
336 // Second, create new ways
337 while (chunkIt.hasNext()) {
338 Way wayToAdd = new Way();
339 wayToAdd.setKeys(way.getKeys());
340 newWays.add(wayToAdd);
341 wayToAdd.setNodes(chunkIt.next());
342 commandList.add(new AddCommand(layer, wayToAdd));
343 }
344 boolean warnmerole = false;
345 boolean warnme = false;
346 // now copy all relations to new way also
347
348 for (Relation r : getParentRelations(way)) {
349 if (!r.isUsable()) {
350 continue;
351 }
352 Relation c = null;
353 String type = r.get("type");
354 if (type == null) {
355 type = "";
356 }
357
358 int i_c = 0, i_r = 0;
359 List<RelationMember> relationMembers = r.getMembers();
360 for (RelationMember rm : relationMembers) {
361 if (rm.isWay() && rm.getMember() == way) {
362 boolean insert = true;
363 if ("restriction".equals(type)) {
364 /*
365 * this code assumes the restriction is correct. No real
366 * error checking done
367 */
368 String role = rm.getRole();
369 if ("from".equals(role) || "to".equals(role)) {
370 OsmPrimitive via = null;
371 for (RelationMember rmv : r.getMembers()) {
372 if ("via".equals(rmv.getRole())) {
373 via = rmv.getMember();
374 }
375 }
376 List<Node> nodes = new ArrayList<Node>();
377 if (via != null) {
378 if (via instanceof Node) {
379 nodes.add((Node) via);
380 } else if (via instanceof Way) {
381 nodes.add(((Way) via).lastNode());
382 nodes.add(((Way) via).firstNode());
383 }
384 }
385 Way res = null;
386 for (Node n : nodes) {
387 if (changedWay.isFirstLastNode(n)) {
388 res = way;
389 }
390 }
391 if (res == null) {
392 for (Way wayToAdd : newWays) {
393 for (Node n : nodes) {
394 if (wayToAdd.isFirstLastNode(n)) {
395 res = wayToAdd;
396 }
397 }
398 }
399 if (res != null) {
400 if (c == null) {
401 c = getNew(r);
402 }
403 c.addMember(new RelationMember(role, res));
404 c.removeMembersFor(way);
405 insert = false;
406 }
407 } else {
408 insert = false;
409 }
410 } else if (!"via".equals(role)) {
411 warnme = true;
412 }
413 } else if (!("route".equals(type))
414 && !("multipolygon".equals(type))) {
415 warnme = true;
416 }
417 if (c == null) {
418 c = getNew(r);
419 }
420
421 if (insert) {
422 if (rm.hasRole() && !nowarnroles.contains(rm.getRole())) {
423 warnmerole = true;
424 }
425
426 Boolean backwards = null;
427 int k = 1;
428 while (i_r - k >= 0 || i_r + k < relationMembers.size()) {
429 if ((i_r - k >= 0)
430 && relationMembers.get(i_r - k).isWay()) {
431 Way w = relationMembers.get(i_r - k).getWay();
432 if ((w.lastNode() == way.firstNode())
433 || w.firstNode() == way.firstNode()) {
434 backwards = false;
435 } else if ((w.firstNode() == way.lastNode())
436 || w.lastNode() == way.lastNode()) {
437 backwards = true;
438 }
439 break;
440 }
441 if ((i_r + k < relationMembers.size())
442 && relationMembers.get(i_r + k).isWay()) {
443 Way w = relationMembers.get(i_r + k).getWay();
444 if ((w.lastNode() == way.firstNode())
445 || w.firstNode() == way.firstNode()) {
446 backwards = true;
447 } else if ((w.firstNode() == way.lastNode())
448 || w.lastNode() == way.lastNode()) {
449 backwards = false;
450 }
451 break;
452 }
453 k++;
454 }
455
456 int j = i_c;
457 for (Way wayToAdd : newWays) {
458 RelationMember em = new RelationMember(
459 rm.getRole(), wayToAdd);
460 j++;
461 if ((backwards != null) && backwards) {
462 c.addMember(i_c, em);
463 } else {
464 c.addMember(j, em);
465 }
466 }
467 i_c = j;
468 }
469 }
470 i_c++;
471 i_r++;
472 }
473
474 if (c != null) {
475 // commandList.add(new ChangeCommand(layer, r, c));
476 newRelations.put(r, c);
477 }
478 }
479 if (warnmerole) {
480 JOptionPane
481 .showMessageDialog(
482 Main.parent,
483 tr("<html>A role based relation membership was copied to all new ways.<br>You should verify this and correct it when necessary.</html>"),
484 tr("Warning"), JOptionPane.WARNING_MESSAGE);
485 } else if (warnme) {
486 JOptionPane
487 .showMessageDialog(
488 Main.parent,
489 tr("<html>A relation membership was copied to all new ways.<br>You should verify this and correct it when necessary.</html>"),
490 tr("Warning"), JOptionPane.WARNING_MESSAGE);
491 }
492
493 return new SplitWayResult(
494 new SequenceCommand(tr("Split way"), commandList), null,
495 changedWay, newWays);
496 }
497
498 /**
499 * @param ways
500 * @return null if ways cannot be combined. Otherwise returns the combined
501 * ways and the commands to combine
502 * @throws UserCancelException
503 */
504 private Pair<Way, List<Command>> combineWaysWorker(Collection<Way> ways)
505 throws UserCancelException {
506
507 // prepare and clean the list of ways to combine
508 if (ways == null || ways.isEmpty())
509 return null;
510 ways.remove(null); // just in case - remove all null ways from the
511 // collection
512
513 // remove duplicates, preserving order
514 ways = new LinkedHashSet<Way>(ways);
515
516 // try to build a new way which includes all the combined ways
517 NodeGraph graph = NodeGraph.createUndirectedGraphFromNodeWays(ways);
518 List<Node> path = graph.buildSpanningPath();
519
520 // check whether any ways have been reversed in the process
521 // and build the collection of tags used by the ways to combine
522 TagCollection wayTags = TagCollection.unionOfAllPrimitives(ways);
523
524 List<Way> reversedWays = new LinkedList<Way>();
525 List<Way> unreversedWays = new LinkedList<Way>();
526 for (Way w : ways) {
527 if ((path.indexOf(w.getNode(0)) + 1) == path.lastIndexOf(w
528 .getNode(1))) {
529 unreversedWays.add(w);
530 } else {
531 reversedWays.add(w);
532 }
533 }
534 // reverse path if all ways have been reversed
535 if (unreversedWays.isEmpty()) {
536 Collections.reverse(path);
537 unreversedWays = reversedWays;
538 reversedWays = null;
539 }
540 if ((reversedWays != null) && !reversedWays.isEmpty()) {
541 // filter out ways that have no direction-dependent tags
542 unreversedWays = ReverseWayTagCorrector
543 .irreversibleWays(unreversedWays);
544 reversedWays = ReverseWayTagCorrector
545 .irreversibleWays(reversedWays);
546 // reverse path if there are more reversed than unreversed ways with
547 // direction-dependent tags
548 if (reversedWays.size() > unreversedWays.size()) {
549 Collections.reverse(path);
550 List<Way> tempWays = unreversedWays;
551 unreversedWays = reversedWays;
552 reversedWays = tempWays;
553 }
554 // if there are still reversed ways with direction-dependent tags,
555 // reverse their tags
556 if (!reversedWays.isEmpty()) {
557 List<Way> unreversedTagWays = new ArrayList<Way>(ways);
558 unreversedTagWays.removeAll(reversedWays);
559 ReverseWayTagCorrector reverseWayTagCorrector = new ReverseWayTagCorrector();
560 List<Way> reversedTagWays = new ArrayList<Way>();
561 Collection<Command> changePropertyCommands = null;
562 for (Way w : reversedWays) {
563 Way wnew = new Way(w);
564 reversedTagWays.add(wnew);
565 changePropertyCommands = reverseWayTagCorrector.execute(w,
566 wnew);
567 }
568 if ((changePropertyCommands != null)
569 && !changePropertyCommands.isEmpty()) {
570 for (Command c : changePropertyCommands) {
571 c.executeCommand();
572 }
573 }
574 wayTags = TagCollection.unionOfAllPrimitives(reversedTagWays);
575 wayTags.add(TagCollection
576 .unionOfAllPrimitives(unreversedTagWays));
577 }
578 }
579
580 // create the new way and apply the new node list
581 Way targetWay = getTargetWay(ways);
582 Way modifiedTargetWay = new Way(targetWay);
583 modifiedTargetWay.setNodes(path);
584
585 TagCollection completeWayTags = new TagCollection(wayTags);
586 combineTigerTags(completeWayTags);
587 normalizeTagCollectionBeforeEditing(completeWayTags, ways);
588 TagCollection tagsToEdit = new TagCollection(completeWayTags);
589 completeTagCollectionForEditing(tagsToEdit);
590
591 MyCombinePrimitiveResolverDialog dialog = MyCombinePrimitiveResolverDialog
592 .getInstance();
593 dialog.getTagConflictResolverModel().populate(tagsToEdit,
594 completeWayTags.getKeysWithMultipleValues());
595 dialog.setTargetPrimitive(targetWay);
596 Set<Relation> parentRelations = getParentRelations(ways);
597 dialog.getRelationMemberConflictResolverModel().populate(
598 parentRelations, ways, oldWays);
599 dialog.prepareDefaultDecisions();
600
601 // resolve tag conflicts if necessary
602 if (askForMergeTag(ways) || duplicateParentRelations(ways)) {
603 dialog.setVisible(true);
604 if (dialog.isCancelled())
605 throw new UserCancelException();
606 }
607
608 LinkedList<Command> cmds = new LinkedList<Command>();
609 deletes.addAll(ways);
610 deletes.remove(targetWay);
611
612 cmds.add(new ChangeCommand(targetWay, modifiedTargetWay));
613 cmds.addAll(dialog.buildWayResolutionCommands());
614 dialog.buildRelationCorrespondance(newRelations, oldWays);
615
616 return new Pair<Way, List<Command>>(targetWay, cmds);
617 }
618
619 private static Way getTargetWay(Collection<Way> combinedWays) {
620 // init with an arbitrary way
621 Way targetWay = combinedWays.iterator().next();
622
623 // look for the first way already existing on
624 // the server
625 for (Way w : combinedWays) {
626 targetWay = w;
627 if (!w.isNew()) {
628 break;
629 }
630 }
631 return targetWay;
632 }
633
634 /**
635 * @return has tag to be merged (=> ask)
636 */
637 private static boolean askForMergeTag(Collection<Way> ways) {
638 for (Way way : ways) {
639 for (Way oposite : ways) {
640 for (String key : way.getKeys().keySet()) {
641 if (!"source".equals(key) && oposite.hasKey(key)
642 && !way.get(key).equals(oposite.get(key))) {
643 return true;
644 }
645 }
646 }
647 }
648 return false;
649 }
650
651 /**
652 * @return has duplicate parent relation
653 */
654 private boolean duplicateParentRelations(Collection<Way> ways) {
655 Set<Relation> relations = new HashSet<Relation>();
656 for (Way w : ways) {
657 List<Relation> rs = getParentRelations(w);
658 for (Relation r : rs) {
659 if (relations.contains(r)) {
660 return true;
661 }
662 }
663 relations.addAll(rs);
664 }
665 return false;
666 }
667
668 /**
669 * Replies the set of referring relations
670 *
671 * @return the set of referring relations
672 */
673 private List<Relation> getParentRelations(Way way) {
674 List<Relation> rels = new ArrayList<Relation>();
675 for (Relation r : relations.get(way)) {
676 if (newRelations.containsKey(r)) {
677 rels.add(newRelations.get(r));
678 } else {
679 rels.add(r);
680 }
681 }
682 return rels;
683 }
684
685 private Relation getNew(Relation r) {
686 return getNew(r, newRelations);
687 }
688
689 public static Relation getNew(Relation r,
690 Map<Relation, Relation> newRelations) {
691 if (newRelations.containsValue(r)) {
692 return r;
693 } else {
694 Relation c = new Relation(r);
695 newRelations.put(r, c);
696 return c;
697 }
698 }
699/*
700 private Way getOld(Way r) {
701 return getOld(r, oldWays);
702 }*/
703
704 public static Way getOld(Way w, Map<Way, Way> oldWays) {
705 if (oldWays.containsKey(w)) {
706 return oldWays.get(w);
707 } else {
708 return w;
709 }
710 }
711
712 /**
713 * Replies the set of referring relations
714 *
715 * @return the set of referring relations
716 */
717 private Set<Relation> getParentRelations(Collection<Way> ways) {
718 HashSet<Relation> ret = new HashSet<Relation>();
719 for (Way w : ways) {
720 ret.addAll(getParentRelations(w));
721 }
722 return ret;
723 }
724
725 /** Enable this action only if something is selected */
726 @Override
727 protected void updateEnabledState() {
728 if (getCurrentDataSet() == null) {
729 setEnabled(false);
730 } else {
731 updateEnabledState(getCurrentDataSet().getSelected());
732 }
733 }
734
735 /** Enable this action only if something is selected */
736 @Override
737 protected void updateEnabledState(
738 Collection<? extends OsmPrimitive> selection) {
739 if (selection == null) {
740 setEnabled(false);
741 return;
742 }
743 for (OsmPrimitive primitive : selection) {
744 if (!(primitive instanceof Way) || primitive.isDeleted()) {
745 setEnabled(false);
746 return;
747 }
748 }
749 setEnabled(selection.size() >= 2);
750 }
751}
Note: See TracBrowser for help on using the repository browser.