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