source: josm/trunk/src/org/openstreetmap/josm/actions/MergeLayerAction.java@ 16770

Last change on this file since 16770 was 16509, checked in by GerdP, 4 years ago

see #19296: Actions should avoid to install listeners which are not needed

  • either don't call installAdapters() or overwrite listenToSelectionChange()
  • partly reverts previous changes so that installAdapters() is not overwritten

One has to be careful because installAdapters() also calls initEnabledState()

  • Property svn:eol-style set to native
File size: 8.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.actions;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.event.ActionEvent;
8import java.awt.event.KeyEvent;
9import java.util.ArrayList;
10import java.util.Collection;
11import java.util.Collections;
12import java.util.List;
13import java.util.concurrent.Future;
14
15import org.openstreetmap.josm.gui.MainApplication;
16import org.openstreetmap.josm.gui.dialogs.LayerListDialog;
17import org.openstreetmap.josm.gui.dialogs.layer.MergeGpxLayerDialog;
18import org.openstreetmap.josm.gui.layer.GpxLayer;
19import org.openstreetmap.josm.gui.layer.Layer;
20import org.openstreetmap.josm.gui.layer.OsmDataLayer;
21import org.openstreetmap.josm.gui.util.GuiHelper;
22import org.openstreetmap.josm.spi.preferences.Config;
23import org.openstreetmap.josm.tools.ImageProvider;
24import org.openstreetmap.josm.tools.Logging;
25import org.openstreetmap.josm.tools.Shortcut;
26import org.openstreetmap.josm.tools.Stopwatch;
27import org.openstreetmap.josm.tools.Utils;
28
29/**
30 * Action that merges two or more OSM data layers.
31 * @since 1890
32 */
33public class MergeLayerAction extends AbstractMergeAction {
34
35 /**
36 * Constructs a new {@code MergeLayerAction}.
37 */
38 public MergeLayerAction() {
39 super(tr("Merge layer"), "dialogs/mergedown",
40 tr("Merge the current layer into another layer"),
41 Shortcut.registerShortcut("system:merge", tr("Edit: {0}",
42 tr("Merge")), KeyEvent.VK_M, Shortcut.CTRL),
43 true, "action/mergelayer", true);
44 setHelpId(ht("/Action/MergeLayer"));
45 }
46
47 /**
48 * Submits merge of layers.
49 * @param targetLayers possible target layers
50 * @param sourceLayers source layers
51 * @return a Future representing pending completion of the merge task, or {@code null}
52 * @since 11885 (return type)
53 */
54 protected Future<?> doMerge(List<? extends Layer> targetLayers, final Collection<? extends Layer> sourceLayers) {
55 final boolean onlygpx = targetLayers.stream().noneMatch(l -> !(l instanceof GpxLayer));
56 final TargetLayerDialogResult<Layer> res = askTargetLayer(targetLayers, onlygpx,
57 tr("Cut timewise overlapping parts of tracks"),
58 onlygpx && Config.getPref().getBoolean("mergelayer.gpx.cut", false), tr("Merge layer"));
59 final Layer targetLayer = res.selectedTargetLayer;
60 if (targetLayer == null)
61 return null;
62
63 if (onlygpx) {
64 Config.getPref().putBoolean("mergelayer.gpx.cut", res.checkboxTicked);
65 }
66
67 final Object actionName = getValue(NAME);
68 if (onlygpx && res.checkboxTicked) {
69 List<GpxLayer> layers = new ArrayList<>();
70 layers.add((GpxLayer) targetLayer);
71 for (Layer sl : sourceLayers) {
72 if (sl != null && !sl.equals(targetLayer)) {
73 layers.add((GpxLayer) sl);
74 }
75 }
76 final MergeGpxLayerDialog d = new MergeGpxLayerDialog(MainApplication.getMainFrame(), layers);
77
78 if (d.showDialog().getValue() == 1) {
79
80 final boolean connect = d.connectCuts();
81 final List<GpxLayer> sortedLayers = d.getSortedLayers();
82
83 return MainApplication.worker.submit(() -> {
84 final Stopwatch stopwatch = Stopwatch.createStarted();
85
86 for (int i = sortedLayers.size() - 2; i >= 0; i--) {
87 final GpxLayer lower = sortedLayers.get(i + 1);
88 sortedLayers.get(i).mergeFrom(lower, true, connect);
89 GuiHelper.runInEDTAndWait(() -> getLayerManager().removeLayer(lower));
90 }
91
92 Logging.info(stopwatch.toString(String.valueOf(actionName)));
93 });
94 }
95 }
96
97 return MainApplication.worker.submit(() -> {
98 final Stopwatch stopwatch = Stopwatch.createStarted();
99 boolean layerMerged = false;
100 for (final Layer sourceLayer: sourceLayers) {
101 if (sourceLayer != null && !sourceLayer.equals(targetLayer)) {
102 if (sourceLayer instanceof OsmDataLayer && targetLayer instanceof OsmDataLayer
103 && ((OsmDataLayer) sourceLayer).isUploadDiscouraged() != ((OsmDataLayer) targetLayer).isUploadDiscouraged()
104 && Boolean.TRUE.equals(GuiHelper.runInEDTAndWaitAndReturn(() ->
105 warnMergingUploadDiscouragedLayers(sourceLayer, targetLayer)))) {
106 break;
107 }
108 targetLayer.mergeFrom(sourceLayer);
109 GuiHelper.runInEDTAndWait(() -> getLayerManager().removeLayer(sourceLayer));
110 layerMerged = true;
111 }
112 }
113
114 if (layerMerged) {
115 getLayerManager().setActiveLayer(targetLayer);
116 Logging.info(stopwatch.toString(String.valueOf(actionName)));
117 }
118 });
119 }
120
121 /**
122 * Merges a list of layers together.
123 * @param sourceLayers The layers to merge
124 * @return a Future representing pending completion of the merge task, or {@code null}
125 * @since 11885 (return type)
126 */
127 public Future<?> merge(List<? extends Layer> sourceLayers) {
128 return doMerge(sourceLayers, sourceLayers);
129 }
130
131 /**
132 * Merges the given source layer with another one, determined at runtime.
133 * @param sourceLayer The source layer to merge
134 * @return a Future representing pending completion of the merge task, or {@code null}
135 * @since 11885 (return type)
136 */
137 public Future<?> merge(Layer sourceLayer) {
138 if (sourceLayer == null)
139 return null;
140 List<Layer> targetLayers = LayerListDialog.getInstance().getModel().getPossibleMergeTargets(sourceLayer);
141 if (targetLayers.isEmpty()) {
142 warnNoTargetLayersForSourceLayer(sourceLayer);
143 return null;
144 }
145 return doMerge(targetLayers, Collections.singleton(sourceLayer));
146 }
147
148 @Override
149 public void actionPerformed(ActionEvent e) {
150 merge(getSourceLayer());
151 }
152
153 @Override
154 protected boolean listenToSelectionChange() {
155 return false;
156 }
157
158 @Override
159 protected void updateEnabledState() {
160 GuiHelper.runInEDT(() -> {
161 final Layer sourceLayer = getSourceLayer();
162 if (sourceLayer == null) {
163 setEnabled(false);
164 } else {
165 try {
166 setEnabled(!LayerListDialog.getInstance().getModel().getPossibleMergeTargets(sourceLayer).isEmpty());
167 } catch (IllegalStateException e) {
168 // May occur when destroying last layer / exiting JOSM, see #14476
169 setEnabled(false);
170 Logging.error(e);
171 }
172 }
173 });
174 }
175
176 /**
177 * Returns the source layer.
178 * @return the source layer
179 */
180 protected Layer getSourceLayer() {
181 return getLayerManager().getActiveLayer();
182 }
183
184 /**
185 * Warns about a discouraged merge operation, ask for confirmation.
186 * @param sourceLayer The source layer
187 * @param targetLayer The target layer
188 * @return {@code true} if the user wants to cancel, {@code false} if they want to continue
189 */
190 public static final boolean warnMergingUploadDiscouragedLayers(Layer sourceLayer, Layer targetLayer) {
191 return GuiHelper.warnUser(tr("Merging layers with different upload policies"),
192 "<html>" +
193 tr("You are about to merge data between layers ''{0}'' and ''{1}''.<br /><br />"+
194 "These layers have different upload policies and should not been merged as it.<br />"+
195 "Merging them will result to enforce the stricter policy (upload discouraged) to ''{1}''.<br /><br />"+
196 "<b>This is not the recommended way of merging such data</b>.<br />"+
197 "You should instead check and merge each object, one by one, by using ''<i>Merge selection</i>''.<br /><br />"+
198 "Are you sure you want to continue?",
199 Utils.escapeReservedCharactersHTML(sourceLayer.getName()),
200 Utils.escapeReservedCharactersHTML(targetLayer.getName()),
201 Utils.escapeReservedCharactersHTML(targetLayer.getName()))+
202 "</html>",
203 ImageProvider.get("dialogs", "mergedown"), tr("Ignore this hint and merge anyway"));
204 }
205}
Note: See TracBrowser for help on using the repository browser.