001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs.layer; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.datatransfer.Transferable; 007import java.awt.datatransfer.UnsupportedFlavorException; 008import java.io.IOException; 009import java.util.ArrayList; 010import java.util.Collection; 011import java.util.List; 012import java.util.stream.Collectors; 013 014import javax.swing.JComponent; 015import javax.swing.JTable; 016import javax.swing.TransferHandler; 017 018import org.openstreetmap.josm.data.osm.DataSet; 019import org.openstreetmap.josm.gui.datatransfer.LayerTransferable; 020import org.openstreetmap.josm.gui.dialogs.LayerListDialog.LayerListModel; 021import org.openstreetmap.josm.gui.layer.Layer; 022import org.openstreetmap.josm.gui.layer.OsmDataLayer; 023import org.openstreetmap.josm.tools.Logging; 024 025/** 026 * This class allows the user to transfer layers using drag+drop. 027 * <p> 028 * It supports copy (duplication) of layers, simple moves and linking layers to a new layer manager. 029 * 030 * @author Michael Zangl 031 * @since 10605 032 */ 033public class LayerListTransferHandler extends TransferHandler { 034 @Override 035 public int getSourceActions(JComponent c) { 036 if (c instanceof JTable) { 037 LayerListModel tableModel = (LayerListModel) ((JTable) c).getModel(); 038 if (!tableModel.getSelectedLayers().isEmpty()) { 039 int actions = MOVE; 040 if (onlyDataLayersSelected(tableModel)) { 041 actions |= COPY; 042 } 043 return actions /* soon: | LINK*/; 044 } 045 } 046 return NONE; 047 } 048 049 private static boolean onlyDataLayersSelected(LayerListModel tableModel) { 050 return tableModel.getSelectedLayers().stream().allMatch(l -> l instanceof OsmDataLayer); 051 } 052 053 @Override 054 protected Transferable createTransferable(JComponent c) { 055 if (c instanceof JTable) { 056 LayerListModel tableModel = (LayerListModel) ((JTable) c).getModel(); 057 return new LayerTransferable(tableModel.getLayerManager(), tableModel.getSelectedLayers()); 058 } 059 return null; 060 } 061 062 @Override 063 public boolean canImport(TransferSupport support) { 064 if (support.isDrop()) { 065 support.setShowDropLocation(true); 066 } 067 068 if (!support.isDataFlavorSupported(LayerTransferable.LAYER_DATA)) { 069 return false; 070 } 071 072 // cannot link yet. 073 return support.getDropAction() != LINK; 074 } 075 076 @Override 077 public boolean importData(TransferSupport support) { 078 try { 079 LayerListModel tableModel = (LayerListModel) ((JTable) support.getComponent()).getModel(); 080 081 LayerTransferable.Data layers = (LayerTransferable.Data) support.getTransferable() 082 .getTransferData(LayerTransferable.LAYER_DATA); 083 084 int dropLocation; 085 if (support.isDrop()) { 086 DropLocation dl = support.getDropLocation(); 087 if (dl instanceof JTable.DropLocation) { 088 dropLocation = ((JTable.DropLocation) dl).getRow(); 089 } else { 090 dropLocation = 0; 091 } 092 } else { 093 dropLocation = layers.getLayers().get(0).getDefaultLayerPosition().getPosition(layers.getManager()); 094 } 095 096 boolean isSameLayerManager = tableModel.getLayerManager() == layers.getManager(); 097 098 if (isSameLayerManager && support.getDropAction() == MOVE) { 099 for (Layer layer : layers.getLayers()) { 100 boolean wasBeforeInsert = layers.getManager().getLayers().indexOf(layer) <= dropLocation; 101 if (wasBeforeInsert) { 102 // need to move insertion point one down to preserve order 103 dropLocation--; 104 } 105 layers.getManager().moveLayer(layer, dropLocation); 106 dropLocation++; 107 } 108 } else { 109 List<Layer> layersToUse = layers.getLayers(); 110 if (support.getDropAction() == COPY) { 111 layersToUse = createCopy(layersToUse, layers.getManager().getLayers()); 112 } 113 for (Layer layer : layersToUse) { 114 layers.getManager().addLayer(layer); 115 layers.getManager().moveLayer(layer, dropLocation); 116 dropLocation++; 117 } 118 } 119 120 return true; 121 } catch (UnsupportedFlavorException e) { 122 Logging.warn("Flavor not supported", e); 123 return false; 124 } catch (IOException e) { 125 Logging.warn("Error while pasting layer", e); 126 return false; 127 } 128 } 129 130 private static List<Layer> createCopy(List<Layer> layersToUse, List<Layer> namesToAvoid) { 131 Collection<String> layerNames = getNames(namesToAvoid); 132 ArrayList<Layer> layers = new ArrayList<>(); 133 for (Layer layer : layersToUse) { 134 if (layer instanceof OsmDataLayer) { 135 String newName = suggestNewLayerName(layer.getName(), layerNames); 136 OsmDataLayer newLayer = new OsmDataLayer(new DataSet(((OsmDataLayer) layer).getDataSet()), newName, null); 137 layers.add(newLayer); 138 layerNames.add(newName); 139 } 140 } 141 return layers; 142 } 143 144 /** 145 * Suggests a new name in the form "copy of name" 146 * @param name The base name 147 * @param namesToAvoid The list of layers to use to avoid duplicate names. 148 * @return The new name 149 */ 150 public static String suggestNewLayerName(String name, List<Layer> namesToAvoid) { 151 Collection<String> layerNames = getNames(namesToAvoid); 152 153 return suggestNewLayerName(name, layerNames); 154 } 155 156 private static List<String> getNames(List<Layer> namesToAvoid) { 157 return namesToAvoid.stream().map(Layer::getName).collect(Collectors.toList()); 158 } 159 160 private static String suggestNewLayerName(String name, Collection<String> layerNames) { 161 // Translators: "Copy of {layer name}" 162 String newName = tr("Copy of {0}", name); 163 int i = 2; 164 while (layerNames.contains(newName)) { 165 // Translators: "Copy {number} of {layer name}" 166 newName = tr("Copy {1} of {0}", name, i); 167 i++; 168 } 169 return newName; 170 } 171}