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.Collection; 010import java.util.Collections; 011import java.util.List; 012 013import org.openstreetmap.josm.Main; 014import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 015import org.openstreetmap.josm.gui.layer.Layer; 016import org.openstreetmap.josm.gui.layer.OsmDataLayer; 017import org.openstreetmap.josm.gui.util.GuiHelper; 018import org.openstreetmap.josm.tools.ImageProvider; 019import org.openstreetmap.josm.tools.Shortcut; 020import org.openstreetmap.josm.tools.Utils; 021 022/** 023 * Action that merges two or more OSM data layers. 024 * @since 1890 025 */ 026public class MergeLayerAction extends AbstractMergeAction { 027 028 /** 029 * Constructs a new {@code MergeLayerAction}. 030 */ 031 public MergeLayerAction() { 032 super(tr("Merge layer"), "dialogs/mergedown", 033 tr("Merge the current layer into another layer"), 034 Shortcut.registerShortcut("system:merge", tr("Edit: {0}", 035 tr("Merge")), KeyEvent.VK_M, Shortcut.CTRL), 036 true, "action/mergelayer", true); 037 putValue("help", ht("/Action/MergeLayer")); 038 } 039 040 protected void doMerge(List<Layer> targetLayers, final Collection<Layer> sourceLayers) { 041 final Layer targetLayer = askTargetLayer(targetLayers); 042 if (targetLayer == null) 043 return; 044 final Object actionName = MergeLayerAction.this.getValue(NAME); 045 Main.worker.submit(() -> { 046 final long start = System.currentTimeMillis(); 047 boolean layerMerged = false; 048 for (final Layer sourceLayer: sourceLayers) { 049 if (sourceLayer != null && !sourceLayer.equals(targetLayer)) { 050 if (sourceLayer instanceof OsmDataLayer && targetLayer instanceof OsmDataLayer 051 && ((OsmDataLayer) sourceLayer).isUploadDiscouraged() != ((OsmDataLayer) targetLayer).isUploadDiscouraged() 052 && Boolean.TRUE.equals(GuiHelper.runInEDTAndWaitAndReturn(() -> 053 warnMergingUploadDiscouragedLayers(sourceLayer, targetLayer)))) { 054 break; 055 } 056 targetLayer.mergeFrom(sourceLayer); 057 GuiHelper.runInEDTAndWait(() -> Main.getLayerManager().removeLayer(sourceLayer)); 058 layerMerged = true; 059 } 060 } 061 if (layerMerged) { 062 Main.getLayerManager().setActiveLayer(targetLayer); 063 Main.info(tr("{0} completed in {1}", actionName, Utils.getDurationString(System.currentTimeMillis() - start))); 064 } 065 }); 066 } 067 068 /** 069 * Merges a list of layers together. 070 * @param sourceLayers The layers to merge 071 */ 072 public void merge(List<Layer> sourceLayers) { 073 doMerge(sourceLayers, sourceLayers); 074 } 075 076 /** 077 * Merges the given source layer with another one, determined at runtime. 078 * @param sourceLayer The source layer to merge 079 */ 080 public void merge(Layer sourceLayer) { 081 if (sourceLayer == null) 082 return; 083 List<Layer> targetLayers = LayerListDialog.getInstance().getModel().getPossibleMergeTargets(sourceLayer); 084 if (targetLayers.isEmpty()) { 085 warnNoTargetLayersForSourceLayer(sourceLayer); 086 return; 087 } 088 doMerge(targetLayers, Collections.singleton(sourceLayer)); 089 } 090 091 @Override 092 public void actionPerformed(ActionEvent e) { 093 merge(getSourceLayer()); 094 } 095 096 @Override 097 protected void updateEnabledState() { 098 GuiHelper.runInEDT(() -> { 099 final Layer sourceLayer = getSourceLayer(); 100 if (sourceLayer == null) { 101 setEnabled(false); 102 } else { 103 final List<Layer> possibleMergeTargets = LayerListDialog.getInstance().getModel().getPossibleMergeTargets(sourceLayer); 104 setEnabled(!possibleMergeTargets.isEmpty()); 105 } 106 }); 107 } 108 109 protected Layer getSourceLayer() { 110 return Main.map != null ? Main.getLayerManager().getActiveLayer() : null; 111 } 112 113 /** 114 * Warns about a discouraged merge operation, ask for confirmation. 115 * @param sourceLayer The source layer 116 * @param targetLayer The target layer 117 * @return {@code true} if the user wants to cancel, {@code false} if they want to continue 118 */ 119 public static final boolean warnMergingUploadDiscouragedLayers(Layer sourceLayer, Layer targetLayer) { 120 return GuiHelper.warnUser(tr("Merging layers with different upload policies"), 121 "<html>" + 122 tr("You are about to merge data between layers ''{0}'' and ''{1}''.<br /><br />"+ 123 "These layers have different upload policies and should not been merged as it.<br />"+ 124 "Merging them will result to enforce the stricter policy (upload discouraged) to ''{1}''.<br /><br />"+ 125 "<b>This is not the recommended way of merging such data</b>.<br />"+ 126 "You should instead check and merge each object, one by one, by using ''<i>Merge selection</i>''.<br /><br />"+ 127 "Are you sure you want to continue?", sourceLayer.getName(), targetLayer.getName(), targetLayer.getName())+ 128 "</html>", 129 ImageProvider.get("dialogs", "mergedown"), tr("Ignore this hint and merge anyway")); 130 } 131}