// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.turnrestrictions.editor; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.swing.AbstractListModel; import javax.swing.DefaultListSelectionModel; import javax.swing.ListSelectionModel; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.data.SelectionChangedListener; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.data.osm.PrimitiveId; import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent; import org.openstreetmap.josm.data.osm.event.DataChangedEvent; import org.openstreetmap.josm.data.osm.event.DataSetListener; import org.openstreetmap.josm.data.osm.event.NodeMovedEvent; import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent; import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent; import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent; import org.openstreetmap.josm.data.osm.event.TagsChangedEvent; import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent; import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent; import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener; import org.openstreetmap.josm.gui.layer.OsmDataLayer; import org.openstreetmap.josm.plugins.turnrestrictions.dnd.PrimitiveIdListProvider; import org.openstreetmap.josm.tools.CheckParameterUtil; /** * JosmSelectionListModel is the model for a list which displays the currently selected * objects in the current edit layer. * */ public class JosmSelectionListModel extends AbstractListModel<OsmPrimitive> implements ActiveLayerChangeListener, SelectionChangedListener, DataSetListener, PrimitiveIdListProvider { private final List<OsmPrimitive> selection = new ArrayList<>(); private final DefaultListSelectionModel selectionModel = new DefaultListSelectionModel(); private OsmDataLayer layer; /** * Constructor * * @param selectionModel the selection model used in the list. Must not be null. * @param layer the layer this model is displaying the selection from. Must not be null. * @throws IllegalArgumentException thrown if {@code layer} is null */ public JosmSelectionListModel(OsmDataLayer layer) { CheckParameterUtil.ensureParameterNotNull(layer, "layer"); this.layer = layer; setJOSMSelection(layer.data.getSelected()); } @Override public OsmPrimitive getElementAt(int index) { return selection.get(index); } @Override public int getSize() { return selection.size(); } /** * Replies the collection of OSM primitives currently selected in the view * of this model * * @return the selected primitives */ public Collection<OsmPrimitive> getSelected() { Set<OsmPrimitive> sel = new HashSet<>(); for (int i = 0; i < getSize(); i++) { if (selectionModel.isSelectedIndex(i)) { sel.add(selection.get(i)); } } return sel; } /** * Sets the OSM primitives to be selected in the view of this model * * @param sel the collection of primitives to select */ public void setSelected(Collection<OsmPrimitive> sel) { selectionModel.clearSelection(); if (sel == null) return; for (OsmPrimitive p: sel) { int i = selection.indexOf(p); if (i >= 0) { selectionModel.addSelectionInterval(i, i); } } } @Override protected void fireContentsChanged(Object source, int index0, int index1) { Collection<OsmPrimitive> sel = getSelected(); super.fireContentsChanged(source, index0, index1); setSelected(sel); } /** * Sets the collection of currently selected OSM objects * * @param selection the collection of currently selected OSM objects */ public void setJOSMSelection(Collection<? extends OsmPrimitive> selection) { Collection<OsmPrimitive> sel = getSelected(); this.selection.clear(); if (selection == null) { fireContentsChanged(this, 0, getSize()); return; } this.selection.addAll(selection); fireContentsChanged(this, 0, getSize()); setSelected(sel); // if the user selects exactly one primitive (i.e. a way), we automatically // select it in the list of selected JOSM objects too. if (getSelected().isEmpty() && this.selection.size() == 1) { setSelected(this.selection); } } /** * Triggers a refresh of the view for all primitives in {@code toUpdate} * which are currently displayed in the view * * @param toUpdate the collection of primitives to update */ public void update(Collection<? extends OsmPrimitive> toUpdate) { if (toUpdate == null) return; if (toUpdate.isEmpty()) return; Collection<OsmPrimitive> sel = getSelected(); for (OsmPrimitive p: toUpdate) { int i = selection.indexOf(p); if (i >= 0) { super.fireContentsChanged(this, i, i); } } setSelected(sel); } public ListSelectionModel getListSelectionModel() { return selectionModel; } /* ------------------------------------------------------------------------ */ /* interface ActiveLayerChangeListener */ /* ------------------------------------------------------------------------ */ @Override public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) { OsmDataLayer newLayer = Main.getLayerManager().getEditLayer(); if (newLayer == null) { // don't show a JOSM selection if we don't have a data layer setJOSMSelection(null); } else if (newLayer != layer) { // don't show a JOSM selection if this turn restriction editor doesn't // manipulate data in the current data layer setJOSMSelection(null); } else { setJOSMSelection(newLayer.data.getSelected()); } } /* ------------------------------------------------------------------------ */ /* interface SelectionChangeListener */ /* ------------------------------------------------------------------------ */ @Override public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { // only update the JOSM selection if it is changed in the same data layer // this turn restriction editor is working on OsmDataLayer layer = Main.getLayerManager().getEditLayer(); if (layer == null) return; if (layer != this.layer) return; setJOSMSelection(newSelection); } /* ------------------------------------------------------------------------ */ /* interface DataSetListener */ /* ------------------------------------------------------------------------ */ @Override public void dataChanged(DataChangedEvent event) { if (event.getDataset() != layer.data) return; fireContentsChanged(this, 0, getSize()); } @Override public void nodeMoved(NodeMovedEvent event) { if (event.getDataset() != layer.data) return; // may influence the display name of primitives, update the data update(event.getPrimitives()); } @Override public void otherDatasetChange(AbstractDatasetChangedEvent event) { if (event.getDataset() != layer.data) return; // may influence the display name of primitives, update the data update(event.getPrimitives()); } @Override public void relationMembersChanged(RelationMembersChangedEvent event) { if (event.getDataset() != layer.data) return; // may influence the display name of primitives, update the data update(event.getPrimitives()); } @Override public void tagsChanged(TagsChangedEvent event) { if (event.getDataset() != layer.data) return; // may influence the display name of primitives, update the data update(event.getPrimitives()); } @Override public void wayNodesChanged(WayNodesChangedEvent event) { if (event.getDataset() != layer.data) return; // may influence the display name of primitives, update the data update(event.getPrimitives()); } @Override public void primitivesAdded(PrimitivesAddedEvent event) {/* ignored - handled by SelectionChangeListener */} @Override public void primitivesRemoved(PrimitivesRemovedEvent event) {/* ignored - handled by SelectionChangeListener*/} /* ------------------------------------------------------------------------ */ /* interface PrimitiveIdListProvider */ /* ------------------------------------------------------------------------ */ @Override public List<PrimitiveId> getSelectedPrimitiveIds() { List<PrimitiveId> ret = new ArrayList<>(getSelected().size()); for (int i = 0; i < selection.size(); i++) { if (selectionModel.isSelectedIndex(i)) { ret.add(selection.get(i).getPrimitiveId()); } } return ret; } }