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(new Runnable() { 044 @Override 045 public void run() { 046 boolean layerMerged = false; 047 for (final Layer sourceLayer: sourceLayers) { 048 if (sourceLayer != null && !sourceLayer.equals(targetLayer)) { 049 if (sourceLayer instanceof OsmDataLayer && targetLayer instanceof OsmDataLayer 050 && ((OsmDataLayer) sourceLayer).isUploadDiscouraged() != ((OsmDataLayer) targetLayer).isUploadDiscouraged()) { 051 if (warnMergingUploadDiscouragedLayers(sourceLayer, targetLayer)) { 052 break; 053 } 054 } 055 targetLayer.mergeFrom(sourceLayer); 056 GuiHelper.runInEDTAndWait(new Runnable() { 057 @Override 058 public void run() { 059 Main.main.removeLayer(sourceLayer); 060 } 061 }); 062 layerMerged = true; 063 } 064 } 065 if (layerMerged) { 066 Main.map.mapView.setActiveLayer(targetLayer); 067 } 068 } 069 }); 070 } 071 072 /** 073 * Merges a list of layers together. 074 * @param sourceLayers The layers to merge 075 */ 076 public void merge(List<Layer> sourceLayers) { 077 doMerge(sourceLayers, sourceLayers); 078 } 079 080 /** 081 * Merges the given source layer with another one, determined at runtime. 082 * @param sourceLayer The source layer to merge 083 */ 084 public void merge(Layer sourceLayer) { 085 if (sourceLayer == null) 086 return; 087 List<Layer> targetLayers = LayerListDialog.getInstance().getModel().getPossibleMergeTargets(sourceLayer); 088 if (targetLayers.isEmpty()) { 089 warnNoTargetLayersForSourceLayer(sourceLayer); 090 return; 091 } 092 doMerge(targetLayers, Collections.singleton(sourceLayer)); 093 } 094 095 @Override 096 public void actionPerformed(ActionEvent e) { 097 merge(getSourceLayer()); 098 } 099 100 @Override 101 protected void updateEnabledState() { 102 GuiHelper.runInEDT(new Runnable() { 103 @Override 104 public void run() { 105 final Layer sourceLayer = getSourceLayer(); 106 if (sourceLayer == null) { 107 setEnabled(false); 108 } else { 109 final List<Layer> possibleMergeTargets = LayerListDialog.getInstance().getModel().getPossibleMergeTargets(sourceLayer); 110 setEnabled(!possibleMergeTargets.isEmpty()); 111 } 112 } 113 }); 114 } 115 116 protected Layer getSourceLayer() { 117 return Main.map != null ? Main.main.getActiveLayer() : null; 118 } 119 120 /** 121 * Warns about a discouraged merge operation, ask for confirmation. 122 * @param sourceLayer The source layer 123 * @param targetLayer The target layer 124 * @return {@code true} if the user wants to cancel, {@code false} if they want to continue 125 */ 126 public static final boolean warnMergingUploadDiscouragedLayers(Layer sourceLayer, Layer targetLayer) { 127 return GuiHelper.warnUser(tr("Merging layers with different upload policies"), 128 "<html>" + 129 tr("You are about to merge data between layers ''{0}'' and ''{1}''.<br /><br />"+ 130 "These layers have different upload policies and should not been merged as it.<br />"+ 131 "Merging them will result to enforce the stricter policy (upload discouraged) to ''{1}''.<br /><br />"+ 132 "<b>This is not the recommended way of merging such data</b>.<br />"+ 133 "You should instead check and merge each object, one by one, by using ''<i>Merge selection</i>''.<br /><br />"+ 134 "Are you sure you want to continue?", sourceLayer.getName(), targetLayer.getName(), targetLayer.getName())+ 135 "</html>", 136 ImageProvider.get("dialogs", "mergedown"), tr("Ignore this hint and merge anyway")); 137 } 138}