// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.turnrestrictions.editor;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import javax.swing.AbstractListModel;
import javax.swing.DefaultListSelectionModel;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.PrimitiveId;
import org.openstreetmap.josm.plugins.turnrestrictions.dnd.PrimitiveIdListProvider;
import org.openstreetmap.josm.tools.CheckParameterUtil;
/**
* ViaListModel is a model for the list of 'via' objects of a turn restriction.
*
*/
public class ViaListModel extends AbstractListModel<OsmPrimitive> implements PrimitiveIdListProvider, Observer {
private DefaultListSelectionModel selectionModel;
private final ArrayList<OsmPrimitive> vias = new ArrayList<>();
private TurnRestrictionEditorModel model;
/**
* Constructor
*
* @param model the turn restriction editor model. Must not be null.
* @param selectionModel the selection model. Must not be null.
* @throws IllegalArgumentException thrown if model is null
* @throws IllegalArgumentException thrown if selectionModel is null
*/
public ViaListModel(TurnRestrictionEditorModel model, DefaultListSelectionModel selectionModel) {
CheckParameterUtil.ensureParameterNotNull(model, "model");
CheckParameterUtil.ensureParameterNotNull(selectionModel, "selectionModel");
this.model = model;
this.selectionModel = selectionModel;
model.addObserver(this);
refresh();
}
/**
* Replies the list of currently selected vias
*
* @return the list of currently selected vias
*/
public List<OsmPrimitive> getSelectedVias() {
ArrayList<OsmPrimitive> ret = new ArrayList<>();
for (int i = 0; i < getSize(); i++) {
if (selectionModel.isSelectedIndex(i)) {
ret.add(vias.get(i));
}
}
return ret;
}
/**
* Sets the collection of currently selected vias
*
* @param vias a collection of vias
*/
public void setSelectedVias(Collection<OsmPrimitive> vias) {
selectionModel.clearSelection();
if (vias == null) return;
for (OsmPrimitive via: vias) {
int idx = this.vias.indexOf(via);
if (idx < 0) continue;
selectionModel.addSelectionInterval(idx, idx);
}
}
/**
* Replies the list of selected rows
*
* @return the list of selected rows
*/
public List<Integer> getSelectedRows() {
ArrayList<Integer> ret = new ArrayList<>();
for (int i = 0; i < getSize(); i++) {
if (selectionModel.isSelectedIndex(i)) {
ret.add(i);
}
}
return ret;
}
protected List<Integer> moveUp(List<Integer> rows, int targetRow) {
List<Integer> ret = new ArrayList<>(rows.size());
int delta = rows.get(0) - targetRow;
for (int row: rows) {
OsmPrimitive via = vias.remove(row);
vias.add(row - delta, via);
ret.add(row - delta);
}
return ret;
}
protected List<Integer> moveDown(List<Integer> rows, int targetRow) {
List<Integer> ret = new ArrayList<>(rows.size());
int delta = targetRow - rows.get(0);
for (int i = rows.size()-1; i >= 0; i--) {
int row = rows.get(i);
OsmPrimitive via = vias.remove(row);
vias.add(row + delta, via);
ret.add(row + delta);
}
return ret;
}
public void moveVias(List<Integer> selectedRows, int targetRow) {
if (selectedRows == null) return;
if (selectedRows.size() == 1) {
int sourceRow = selectedRows.get(0);
if (sourceRow == targetRow) return;
OsmPrimitive via = vias.remove(sourceRow);
vias.add(targetRow, via);
fireContentsChanged(this, 0, getSize());
selectionModel.setSelectionInterval(targetRow, targetRow);
return;
}
int min = selectedRows.get(0);
int max = selectedRows.get(selectedRows.size()-1);
if (targetRow < min) {
selectedRows = moveUp(selectedRows, targetRow);
} else if (targetRow == min) {
// do nothing
} else if (targetRow - min < getSize() - max) {
int delta = Math.min(targetRow - min, getSize()-1 - max);
targetRow = min + delta;
if (targetRow > min) {
selectedRows = moveDown(selectedRows, targetRow);
}
}
fireContentsChanged(this, 0, getSize());
selectionModel.clearSelection();
for (int row: selectedRows) {
selectionModel.addSelectionInterval(row, row);
}
}
/**
* Move the currently selected vias up by one position
*/
public void moveUp() {
List<Integer> sel = getSelectedRows();
if (sel.isEmpty() || sel.get(0) == 0) return;
moveVias(sel, sel.get(0)-1);
}
/**
* Move the currently selected vias down by one position
*/
public void moveDown() {
List<Integer> sel = getSelectedRows();
if (sel.isEmpty() || sel.get(sel.size()-1) == getSize()-1) return;
moveVias(sel, sel.get(sel.size()-1)+1);
}
/**
* Inserts a list of OSM objects given by OSM primitive ids.
*
* @param idsToInsert the ids of the objects to insert
*/
public void insertVias(List<PrimitiveId> idsToInsert) {
if (idsToInsert == null) return;
List<OsmPrimitive> primitives = new ArrayList<>(idsToInsert.size());
DataSet ds = model.getLayer().data;
for (PrimitiveId id: idsToInsert) {
OsmPrimitive p = ds.getPrimitiveById(id);
if (p == null) {
System.out.println(tr("Failed to retrieve OSM object with id {0} from dataset {1}. Cannot add it as ''via''.", id, ds));
continue;
}
primitives.add(p);
}
int targetRow = Math.max(selectionModel.getMinSelectionIndex(), 0);
List<OsmPrimitive> newVias = new ArrayList<>(vias);
newVias.addAll(targetRow, primitives);
model.setVias(newVias);
fireContentsChanged(this, 0, getSize());
selectionModel.clearSelection();
for (int i = targetRow; i < targetRow + primitives.size(); i++) {
selectionModel.addSelectionInterval(i, i);
}
}
/**
* Removes the currently selected vias
*/
public void removeSelectedVias() {
ArrayList<OsmPrimitive> newVias = new ArrayList<>(vias);
int j = 0;
for (int i = 0; i < getSize(); i++) {
if (!selectionModel.isSelectedIndex(i)) continue;
newVias.remove(i-j);
j++;
}
if (j == 0) return; // nothing selected, nothing deleted
model.setVias(newVias);
}
/**
* Refreshes the list of 'vias' in this model with the current list of
* vias from the turn restriction model.
*/
protected void refresh() {
List<OsmPrimitive> sel = getSelectedVias();
vias.clear();
vias.addAll(model.getVias());
fireContentsChanged(this, 0, getSize());
setSelectedVias(sel);
}
@Override
public OsmPrimitive getElementAt(int index) {
return vias.get(index);
}
@Override
public int getSize() {
return vias.size();
}
/* ----------------------------------------------------------------------- */
/* interface PrimitiveIdListProvider */
/* ----------------------------------------------------------------------- */
@Override
public List<PrimitiveId> getSelectedPrimitiveIds() {
ArrayList<PrimitiveId> ids = new ArrayList<>();
for (OsmPrimitive p: getSelectedVias()) {
ids.add(p.getPrimitiveId());
}
return ids;
}
/* ----------------------------------------------------------------------- */
/* interface Observer */
/* ----------------------------------------------------------------------- */
@Override
public void update(Observable o, Object arg) {
refresh();
}
}