001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs.relation; 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.Collection; 010import java.util.Objects; 011import java.util.stream.Collectors; 012 013import javax.swing.JComponent; 014import javax.swing.JTable; 015import javax.swing.TransferHandler; 016 017import org.openstreetmap.josm.data.osm.OsmPrimitive; 018import org.openstreetmap.josm.data.osm.PrimitiveData; 019import org.openstreetmap.josm.data.osm.PrimitiveId; 020import org.openstreetmap.josm.data.osm.RelationMember; 021import org.openstreetmap.josm.data.osm.RelationMemberData; 022import org.openstreetmap.josm.gui.datatransfer.RelationMemberTransferable; 023import org.openstreetmap.josm.gui.datatransfer.data.PrimitiveTransferData; 024import org.openstreetmap.josm.tools.Logging; 025 026/** 027 * A transfer handler that helps with importing / exporting members for relations. 028 * @author Michael Zangl 029 * @since 10604 030 */ 031public class MemberTransferHandler extends TransferHandler { 032 033 @Override 034 public int getSourceActions(JComponent c) { 035 return COPY_OR_MOVE; 036 } 037 038 @Override 039 protected Transferable createTransferable(JComponent c) { 040 final MemberTable source = (MemberTable) c; 041 return new RelationMemberTransferable(source.getMemberTableModel().getSelectedMembers()); 042 } 043 044 @Override 045 public boolean canImport(TransferSupport support) { 046 if (support.isDrop()) { 047 support.setShowDropLocation(true); 048 } 049 return support.isDataFlavorSupported(RelationMemberTransferable.RELATION_MEMBER_DATA) 050 || support.isDataFlavorSupported(PrimitiveTransferData.DATA_FLAVOR); 051 } 052 053 @Override 054 public boolean importData(TransferSupport support) { 055 MemberTable destination = (MemberTable) support.getComponent(); 056 int insertRow = computeInsertionRow(support, destination); 057 058 return importDataAt(support, destination, insertRow); 059 } 060 061 private static int computeInsertionRow(TransferSupport support, MemberTable destination) { 062 final int insertRow; 063 if (support.isDrop()) { 064 DropLocation dl = support.getDropLocation(); 065 if (dl instanceof JTable.DropLocation) { 066 insertRow = ((JTable.DropLocation) dl).getRow(); 067 } else { 068 insertRow = 0; 069 } 070 } else { 071 int selection = destination.getSelectedRow(); 072 if (selection < 0) { 073 // no selection, add at the end. 074 insertRow = destination.getRowCount(); 075 } else { 076 insertRow = selection; 077 } 078 } 079 return insertRow; 080 } 081 082 private boolean importDataAt(TransferSupport support, MemberTable destination, int insertRow) { 083 try { 084 if (support.isDataFlavorSupported(RelationMemberTransferable.RELATION_MEMBER_DATA)) { 085 importRelationMemberData(support, destination, insertRow); 086 return true; 087 } else if (support.isDataFlavorSupported(PrimitiveTransferData.DATA_FLAVOR)) { 088 importPrimitiveData(support, destination, insertRow); 089 return true; 090 } else { 091 return false; 092 } 093 } catch (IOException | UnsupportedFlavorException e) { 094 Logging.warn(e); 095 return false; 096 } 097 } 098 099 protected void importRelationMemberData(TransferSupport support, final MemberTable destination, int insertRow) 100 throws UnsupportedFlavorException, IOException { 101 final RelationMemberTransferable.Data memberData = (RelationMemberTransferable.Data) 102 support.getTransferable().getTransferData(RelationMemberTransferable.RELATION_MEMBER_DATA); 103 importData(destination, insertRow, memberData.getRelationMemberData(), new AbstractRelationMemberConverter<RelationMemberData>() { 104 @Override 105 protected RelationMember getMember(MemberTable destination, RelationMemberData data, OsmPrimitive p) { 106 return new RelationMember(data.getRole(), p); 107 } 108 }); 109 } 110 111 protected void importPrimitiveData(TransferSupport support, final MemberTable destination, int insertRow) 112 throws UnsupportedFlavorException, IOException { 113 final PrimitiveTransferData data = (PrimitiveTransferData) 114 support.getTransferable().getTransferData(PrimitiveTransferData.DATA_FLAVOR); 115 importData(destination, insertRow, data.getDirectlyAdded(), new AbstractRelationMemberConverter<PrimitiveData>() { 116 @Override 117 protected RelationMember getMember(MemberTable destination, PrimitiveData data, OsmPrimitive p) { 118 return destination.getMemberTableModel().getRelationMemberForPrimitive(p); 119 } 120 }); 121 } 122 123 protected <T extends PrimitiveId> void importData(MemberTable destination, int insertRow, 124 Collection<T> memberData, AbstractRelationMemberConverter<T> toMemberFunction) { 125 final Collection<RelationMember> membersToAdd = memberData.stream() 126 .map(data -> toMemberFunction.importPrimitive(destination, data)) 127 .filter(Objects::nonNull) 128 .collect(Collectors.toList()); 129 destination.getMemberTableModel().addMembersAtIndexKeepingOldSelection(membersToAdd, insertRow); 130 } 131 132 @Override 133 protected void exportDone(JComponent sourceComponent, Transferable data, int action) { 134 if (action != MOVE) { 135 return; 136 } 137 final MemberTable source = (MemberTable) sourceComponent; 138 final MemberTableModel model = source.getMemberTableModel(); 139 model.remove(source.getSelectedRows()); 140 model.selectionChanged(null); 141 } 142 143 private abstract static class AbstractRelationMemberConverter<T extends PrimitiveId> { 144 protected RelationMember importPrimitive(MemberTable destination, T data) { 145 final OsmPrimitive p = destination.getLayer().data.getPrimitiveById(data); 146 if (p == null) { 147 Logging.warn(tr("Cannot add {0} since it is not part of dataset", data)); 148 return null; 149 } else { 150 return getMember(destination, data, p); 151 } 152 } 153 154 protected abstract RelationMember getMember(MemberTable destination, T data, OsmPrimitive p); 155 } 156}