// License: GPL. For details, see LICENSE file.
package relcontext.actions;
import static org.openstreetmap.josm.tools.I18n.tr;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Collection;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.actions.JosmAction;
import org.openstreetmap.josm.command.ChangeCommand;
import org.openstreetmap.josm.command.Command;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
import org.openstreetmap.josm.data.osm.Relation;
import org.openstreetmap.josm.data.osm.RelationMember;
import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.gui.util.GuiHelper;
import org.openstreetmap.josm.tools.ImageProvider;
import org.openstreetmap.josm.tools.Shortcut;
import relcontext.ChosenRelation;
import relcontext.ChosenRelationListener;
/**
* An action to add or remove (or both) member(s) from the chosen relation.
* In time should be able to determine correct position for new members.
* Also, there should be some support for entering a role for new members.
*
* @author Zverik
*/
public class AddRemoveMemberAction extends JosmAction implements ChosenRelationListener {
private ChosenRelation rel;
private SortAndFixAction sortAndFix;
public AddRemoveMemberAction(ChosenRelation rel, SortAndFixAction sortAndFix) {
super(null, "relcontext/addremove", tr("Add/remove members from the chosen relation"),
Shortcut.registerShortcut("reltoolbox:addremove", tr("Relation Toolbox: {0}", tr("Add/remove members from the chosen relation")),
KeyEvent.VK_EQUALS, Shortcut.DIRECT), false);
this.rel = rel;
this.sortAndFix = sortAndFix;
rel.addChosenRelationListener(this);
updateEnabledState();
}
@Override
public void actionPerformed(ActionEvent e) {
if (rel.get() == null)
return;
Relation r = new Relation(rel.get());
Collection<OsmPrimitive> toAdd = new ArrayList<>(getLayerManager().getEditDataSet().getSelected());
toAdd.remove(rel.get());
toAdd.removeAll(r.getMemberPrimitives());
// 0. check if relation is broken (temporary)
boolean isBroken = !toAdd.isEmpty() && sortAndFix.needsFixing(r);
// 1. remove all present members
r.removeMembersFor(getLayerManager().getEditDataSet().getSelected());
// 2. add all new members
for (OsmPrimitive p : toAdd) {
int pos = -1; //p instanceof Way ? findAdjacentMember(p, r) : -1;
if (pos < 0) {
r.addMember(new RelationMember("", p));
} else {
r.addMember(pos, new RelationMember("", p));
}
}
// 3. check for roles again (temporary)
Command roleFix = !isBroken && sortAndFix.needsFixing(r) ? sortAndFix.fixRelation(r) : null;
if (roleFix != null) {
roleFix.executeCommand();
}
if (!r.getMemberPrimitives().equals(rel.get().getMemberPrimitives())) {
Main.main.undoRedo.add(new ChangeCommand(rel.get(), r));
}
}
/**
* Finds two relation members between which to place given way. Incomplete.
* @see org.openstreetmap.josm.gui.dialogs.relation.MemberTableModel#determineDirection
*/
protected int findAdjacentMember(Way w, Relation r) {
Node firstNode = w.firstNode();
Node lastNode = w.lastNode();
if (firstNode != null && !firstNode.equals(lastNode)) {
for (int i = 0; i < r.getMembersCount(); i++) {
if (r.getMember(i).getType().equals(OsmPrimitiveType.WAY)) {
Way rw = (Way) r.getMember(i).getMember();
Node firstNodeR = rw.firstNode();
Node lastNodeR = rw.lastNode();
if (firstNode.equals(firstNodeR) || firstNode.equals(lastNodeR) || lastNode.equals(firstNodeR) || lastNode.equals(lastNodeR))
return i + 1;
}
}
}
return -1;
}
@Override
public void chosenRelationChanged(Relation oldRelation, Relation newRelation) {
updateEnabledState();
}
@Override
protected void updateEnabledState() {
updateEnabledState(getLayerManager().getEditDataSet() == null ? null : getLayerManager().getEditDataSet().getSelected());
}
@Override
protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
updateIcon();
if (rel == null || rel.get() == null || selection == null || selection.isEmpty()) {
setEnabled(false);
return;
}
if (selection.size() == 1 && selection.contains(rel.get())) {
setEnabled(false);
return;
}
setEnabled(true);
}
protected void updateIcon() {
// todo: change icon based on selection
final int state; // 0=unknown, 1=add, 2=remove, 3=both
DataSet ds = getLayerManager().getEditDataSet();
if (ds == null || ds.getSelected() == null
|| ds.getSelected().isEmpty() || rel == null || rel.get() == null) {
state = 0;
} else {
Collection<OsmPrimitive> toAdd = new ArrayList<>(ds.getSelected());
toAdd.remove(rel.get());
int selectedSize = toAdd.size();
if (selectedSize == 0) {
state = 0;
} else {
toAdd.removeAll(rel.get().getMemberPrimitives());
if (toAdd.isEmpty()) {
state = 2;
} else if (toAdd.size() < selectedSize) {
state = 3;
} else {
state = 1;
}
}
}
GuiHelper.runInEDT(new Runnable() {
@Override
public void run() {
if (state == 0) {
putValue(LARGE_ICON_KEY, ImageProvider.get("relcontext", "addremove"));
} else {
String iconName = state == 1 ? "add" : state == 2 ? "remove" : "addremove";
putValue(NAME, null);
putValue(LARGE_ICON_KEY, ImageProvider.get("relcontext", iconName));
}
}
});
}
}