/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2015, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.gui.javafx.render2d.edition;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Polygon;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.BorderPane;
import org.apache.sis.feature.FeatureExt;
import org.opengis.feature.Feature;
import org.geotoolkit.gui.javafx.render2d.FXMap;
import org.geotoolkit.gui.javafx.render2d.FXPanMouseListen;
import org.geotoolkit.gui.javafx.render2d.shape.FXGeometryLayer;
import org.geotoolkit.internal.GeotkFX;
import org.geotoolkit.map.FeatureMapLayer;
import org.opengis.feature.AttributeType;
/**
*
* @author Johann Sorel (Geomatys)
*/
public class MoveNodeTool extends AbstractEditionTool{
public static final class Spi extends AbstractEditionToolSpi{
public Spi() {
super("MoveNode",
GeotkFX.getI18NString(MoveNodeTool.class, "title"),
GeotkFX.getI18NString(MoveNodeTool.class, "abstract"),
GeotkFX.ICON_EDIT);
}
@Override
public boolean canHandle(Object candidate) {
if(candidate instanceof FeatureMapLayer){
final FeatureMapLayer fml = (FeatureMapLayer) candidate;
if(!fml.getCollection().isWritable()) return false;
final AttributeType desc = FeatureExt.getDefaultGeometryAttribute(fml.getCollection().getFeatureType());
return desc != null;
}
return false;
}
@Override
public EditionTool create(FXMap map, Object layer) {
return new MoveNodeTool(map, (FeatureMapLayer) layer);
}
};
private final BorderPane configPane = null;
private final BorderPane helpPane = new BorderPane();
private final FeatureMapLayer layer;
private final EditionHelper helper;
private final MouseListen mouseInputListener = new MouseListen();
private final FXGeometryLayer decoration= new EditionLayer();
private Feature feature = null;
private final EditionHelper.EditionGeometry selection = new EditionHelper.EditionGeometry();
private boolean modified = false;
private MouseButton pressed = null;
public MoveNodeTool(FXMap map, FeatureMapLayer layer) {
super(EditionHelper.getToolSpi("MoveNode"));
this.layer = layer;
this.helper = new EditionHelper(map, layer);
}
@Override
public Node getConfigurationPane() {
return configPane;
}
@Override
public Node getHelpPane() {
return helpPane;
}
private void reset(){
feature = null;
selection.reset();
decoration.getGeometries().clear();
decoration.setNodeSelection(null);
}
private void refreshDecoration(){
decoration.getGeometries().setAll(this.selection.geometry.get());
decoration.setNodeSelection(this.selection);
}
public void setCurrentFeature(final Feature feature){
this.feature = feature;
if(feature != null){
this.selection.geometry.set( helper.toObjectiveCRS(feature) );
}else{
this.selection.geometry.set( null );
}
refreshDecoration();
}
@Override
public void install(final FXMap component) {
super.install(component);
component.addEventHandler(MouseEvent.ANY, mouseInputListener);
component.addEventHandler(ScrollEvent.ANY, mouseInputListener);
component.addEventHandler(KeyEvent.ANY, mouseInputListener);
component.setCursor(Cursor.CROSSHAIR);
component.addDecoration(0,decoration);
}
@Override
public boolean uninstall(FXMap component) {
super.uninstall(component);
component.removeEventHandler(MouseEvent.ANY, mouseInputListener);
component.removeEventHandler(ScrollEvent.ANY, mouseInputListener);
component.removeEventHandler(KeyEvent.ANY, mouseInputListener);
component.removeDecoration(decoration);
return true;
}
private class MouseListen extends FXPanMouseListen {
public MouseListen() {
super(MoveNodeTool.this);
}
@Override
public void mouseClicked(final MouseEvent e) {
final MouseButton button = e.getButton();
if(button == MouseButton.PRIMARY){
if(selection.geometry.get() == null){
setCurrentFeature(helper.grabFeature(e.getX(), e.getY(), false));
}else if(e.getClickCount() >= 2){
//double click = add a node
final Geometry result;
if(selection.geometry.get() instanceof LineString){
result = helper.insertNode((LineString)selection.geometry.get(), e.getX(), e.getY());
}else if(selection.geometry.get() instanceof Polygon){
result = helper.insertNode((Polygon)selection.geometry.get(), e.getX(), e.getY());
}else if(selection.geometry.get() instanceof GeometryCollection){
result = helper.insertNode((GeometryCollection)selection.geometry.get(), e.getX(), e.getY());
}else{
result = selection.geometry.get();
}
modified = modified || result != selection.geometry.get();
selection.geometry.set( result );
decoration.getGeometries().setAll(selection.geometry.get());
}else if(e.getClickCount() == 1){
//single click with a geometry = select a node
helper.grabGeometryNode(e.getX(), e.getY(), selection);
decoration.setNodeSelection(selection);
}
}else if(button == MouseButton.SECONDARY){
helper.sourceModifyFeature(feature, selection.geometry.get(), true);
reset();
}
}
@Override
public void mousePressed(final MouseEvent e) {
pressed = e.getButton();
if(pressed == MouseButton.PRIMARY){
if(selection.geometry.get() == null){
setCurrentFeature(helper.grabFeature(e.getX(), e.getY(), false));
}else if(e.getClickCount() == 1){
//single click with a geometry = select a node
helper.grabGeometryNode(e.getX(), e.getY(), selection);
decoration.setNodeSelection(selection);
}
}
super.mousePressed(e);
}
@Override
public void mouseDragged(final MouseEvent e) {
if(pressed == MouseButton.PRIMARY && selection != null){
//dragging node
selection.moveSelectedNode(helper.toCoord(e.getX(), e.getY()));
refreshDecoration();
modified = true;
return;
}
super.mouseDragged(e);
}
@Override
public void keyReleased(KeyEvent e) {
if(KeyCode.DELETE == e.getCode() && selection != null){
//delete node
selection.deleteSelectedNode();
refreshDecoration();
modified = true;
}
}
}
}