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.data.osm.DataSet; 018import org.openstreetmap.josm.gui.datatransfer.LayerTransferable; 019import org.openstreetmap.josm.gui.dialogs.LayerListDialog.LayerListModel; 020import org.openstreetmap.josm.gui.layer.Layer; 021import org.openstreetmap.josm.gui.layer.OsmDataLayer; 022import org.openstreetmap.josm.tools.Logging; 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 if (c instanceof JTable) { 036 LayerListModel tableModel = (LayerListModel) ((JTable) c).getModel(); 037 if (!tableModel.getSelectedLayers().isEmpty()) { 038 int actions = MOVE; 039 if (onlyDataLayersSelected(tableModel)) { 040 actions |= COPY; 041 } 042 return actions /* soon: | LINK*/; 043 } 044 } 045 return NONE; 046 } 047 048 private static boolean onlyDataLayersSelected(LayerListModel tableModel) { 049 for (Layer l : tableModel.getSelectedLayers()) { 050 if (!(l instanceof OsmDataLayer)) { 051 return false; 052 } 053 } 054 return true; 055 } 056 057 @Override 058 protected Transferable createTransferable(JComponent c) { 059 if (c instanceof JTable) { 060 LayerListModel tableModel = (LayerListModel) ((JTable) c).getModel(); 061 return new LayerTransferable(tableModel.getLayerManager(), tableModel.getSelectedLayers()); 062 } 063 return null; 064 } 065 066 @Override 067 public boolean canImport(TransferSupport support) { 068 if (support.isDrop()) { 069 support.setShowDropLocation(true); 070 } 071 072 if (!support.isDataFlavorSupported(LayerTransferable.LAYER_DATA)) { 073 return false; 074 } 075 076 // cannot link yet. 077 return support.getDropAction() != LINK; 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 DropLocation dl = support.getDropLocation(); 091 if (dl instanceof JTable.DropLocation) { 092 dropLocation = ((JTable.DropLocation) dl).getRow(); 093 } else { 094 dropLocation = 0; 095 } 096 } else { 097 dropLocation = layers.getLayers().get(0).getDefaultLayerPosition().getPosition(layers.getManager()); 098 } 099 100 boolean isSameLayerManager = tableModel.getLayerManager() == layers.getManager(); 101 102 if (isSameLayerManager && support.getDropAction() == MOVE) { 103 for (Layer layer : layers.getLayers()) { 104 boolean wasBeforeInsert = layers.getManager().getLayers().indexOf(layer) <= dropLocation; 105 if (wasBeforeInsert) { 106 // need to move insertion point one down to preserve order 107 dropLocation--; 108 } 109 layers.getManager().moveLayer(layer, dropLocation); 110 dropLocation++; 111 } 112 } else { 113 List<Layer> layersToUse = layers.getLayers(); 114 if (support.getDropAction() == COPY) { 115 layersToUse = createCopy(layersToUse, layers.getManager().getLayers()); 116 } 117 for (Layer layer : layersToUse) { 118 layers.getManager().addLayer(layer); 119 layers.getManager().moveLayer(layer, dropLocation); 120 dropLocation++; 121 } 122 } 123 124 return true; 125 } catch (UnsupportedFlavorException e) { 126 Logging.warn("Flavor not supported", e); 127 return false; 128 } catch (IOException e) { 129 Logging.warn("Error while pasting layer", e); 130 return false; 131 } 132 } 133 134 private static List<Layer> createCopy(List<Layer> layersToUse, List<Layer> namesToAvoid) { 135 Collection<String> layerNames = getNames(namesToAvoid); 136 ArrayList<Layer> layers = new ArrayList<>(); 137 for (Layer layer : layersToUse) { 138 if (layer instanceof OsmDataLayer) { 139 String newName = suggestNewLayerName(layer.getName(), layerNames); 140 OsmDataLayer newLayer = new OsmDataLayer(new DataSet(((OsmDataLayer) layer).getDataSet()), newName, null); 141 layers.add(newLayer); 142 layerNames.add(newName); 143 } 144 } 145 return layers; 146 } 147 148 /** 149 * Suggests a new name in the form "copy of name" 150 * @param name The base name 151 * @param namesToAvoid The list of layers to use to avoid duplicate names. 152 * @return The new name 153 */ 154 public static String suggestNewLayerName(String name, List<Layer> namesToAvoid) { 155 Collection<String> layerNames = getNames(namesToAvoid); 156 157 return suggestNewLayerName(name, layerNames); 158 } 159 160 private static List<String> getNames(List<Layer> namesToAvoid) { 161 List<String> layerNames = new ArrayList<>(); 162 for (Layer l: namesToAvoid) { 163 layerNames.add(l.getName()); 164 } 165 return layerNames; 166 } 167 168 private static String suggestNewLayerName(String name, Collection<String> layerNames) { 169 // Translators: "Copy of {layer name}" 170 String newName = tr("Copy of {0}", name); 171 int i = 2; 172 while (layerNames.contains(newName)) { 173 // Translators: "Copy {number} of {layer name}" 174 newName = tr("Copy {1} of {0}", name, i); 175 i++; 176 } 177 return newName; 178 } 179}