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; 012 013import javax.swing.JComponent; 014import javax.swing.JTable; 015import javax.swing.TransferHandler; 016 017import org.openstreetmap.josm.Main; 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; 023 024/** 025 * This class allows the user to transfer layers using drag+drop. 026 * <p> 027 * It supports copy (duplication) of layers, simple moves and linking layers to a new layer manager. 028 * 029 * @author Michael Zangl 030 * @since 10605 031 */ 032public class LayerListTransferHandler extends TransferHandler { 033 @Override 034 public int getSourceActions(JComponent c) { 035 // we know that the source is a layer list, so don't check c. 036 LayerListModel tableModel = (LayerListModel) ((JTable) c).getModel(); 037 if (tableModel.getSelectedLayers().isEmpty()) { 038 return 0; 039 } 040 int actions = MOVE; 041 if (onlyDataLayersSelected(tableModel)) { 042 actions |= COPY; 043 } 044 return actions /* soon: | LINK*/; 045 } 046 047 private static boolean onlyDataLayersSelected(LayerListModel tableModel) { 048 for (Layer l : tableModel.getSelectedLayers()) { 049 if (!(l instanceof OsmDataLayer)) { 050 return false; 051 } 052 } 053 return true; 054 } 055 056 @Override 057 protected Transferable createTransferable(JComponent c) { 058 LayerListModel tableModel = (LayerListModel) ((JTable) c).getModel(); 059 return new LayerTransferable(tableModel.getLayerManager(), tableModel.getSelectedLayers()); 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 if (support.getDropAction() == LINK) { 073 // cannot link yet. 074 return false; 075 } 076 077 return true; 078 } 079 080 @Override 081 public boolean importData(TransferSupport support) { 082 try { 083 LayerListModel tableModel = (LayerListModel) ((JTable) support.getComponent()).getModel(); 084 085 LayerTransferable.Data layers = (LayerTransferable.Data) support.getTransferable() 086 .getTransferData(LayerTransferable.LAYER_DATA); 087 088 int dropLocation; 089 if (support.isDrop()) { 090 JTable.DropLocation dl = (JTable.DropLocation) support.getDropLocation(); 091 dropLocation = dl.getRow(); 092 } else { 093 dropLocation = layers.getLayers().get(0).getDefaultLayerPosition().getPosition(layers.getManager()); 094 } 095 096 boolean isSameLayerManager = tableModel.getLayerManager() == layers.getManager(); 097 098 if (support.getDropAction() == MOVE && isSameLayerManager) { 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 Main.warn("Flavor not supported", e); 123 return false; 124 } catch (IOException e) { 125 Main.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).data), 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 List<String> layerNames = new ArrayList<>(); 158 for (Layer l: namesToAvoid) { 159 layerNames.add(l.getName()); 160 } 161 return layerNames; 162 } 163 164 private static String suggestNewLayerName(String name, Collection<String> layerNames) { 165 // Translators: "Copy of {layer name}" 166 String newName = tr("Copy of {0}", name); 167 int i = 2; 168 while (layerNames.contains(newName)) { 169 // Translators: "Copy {number} of {layer name}" 170 newName = tr("Copy {1} of {0}", name, i); 171 i++; 172 } 173 return newName; 174 } 175}