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