// 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;
}
}