1 /*******************************************************************************
2 * Copyright (c) 2011 Association for Decentralized Information Management in
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.scenegraph.g2d.nodes.connection;
14 import java.awt.BasicStroke;
15 import java.awt.Color;
16 import java.awt.Graphics2D;
17 import java.awt.RenderingHints;
18 import java.awt.Shape;
19 import java.awt.Stroke;
20 import java.awt.event.KeyEvent;
21 import java.awt.geom.AffineTransform;
22 import java.awt.geom.Path2D;
23 import java.awt.geom.Point2D;
24 import java.awt.geom.Rectangle2D;
25 import java.lang.reflect.Constructor;
26 import java.util.Collection;
29 import org.simantics.diagram.connection.RouteGraph;
30 import org.simantics.diagram.connection.RouteLine;
31 import org.simantics.diagram.connection.RouteLink;
32 import org.simantics.diagram.connection.RouteTerminal;
33 import org.simantics.diagram.connection.actions.IAction;
34 import org.simantics.diagram.connection.actions.IReconnectAction;
35 import org.simantics.diagram.connection.actions.MoveAction;
36 import org.simantics.diagram.connection.actions.ReconnectLineAction;
37 import org.simantics.diagram.connection.delta.RouteGraphDelta;
38 import org.simantics.diagram.connection.rendering.BasicConnectionStyle;
39 import org.simantics.diagram.connection.rendering.ConnectionStyle;
40 import org.simantics.diagram.connection.rendering.IRouteGraphRenderer;
41 import org.simantics.diagram.connection.rendering.StyledRouteGraphRenderer;
42 import org.simantics.diagram.connection.rendering.arrows.ILineEndStyle;
43 import org.simantics.diagram.connection.splitting.SplittedRouteGraph;
44 import org.simantics.scenegraph.INode;
45 import org.simantics.scenegraph.ISelectionPainterNode;
46 import org.simantics.scenegraph.g2d.G2DNode;
47 import org.simantics.scenegraph.g2d.G2DParentNode;
48 import org.simantics.scenegraph.g2d.IG2DNode;
49 import org.simantics.scenegraph.g2d.events.EventTypes;
50 import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
51 import org.simantics.scenegraph.g2d.events.KeyEvent.KeyReleasedEvent;
52 import org.simantics.scenegraph.g2d.events.MouseEvent;
53 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;
54 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent;
55 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;
56 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;
57 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;
58 import org.simantics.scenegraph.g2d.events.command.CommandEvent;
59 import org.simantics.scenegraph.g2d.events.command.Commands;
60 import org.simantics.scenegraph.g2d.nodes.GridNode;
61 import org.simantics.scenegraph.g2d.nodes.LinkNode;
62 import org.simantics.scenegraph.g2d.nodes.connection.HighlightActionPointsAction.Action;
63 import org.simantics.scenegraph.g2d.nodes.connection.HighlightActionPointsAction.Pick;
64 import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;
65 import org.simantics.scenegraph.utils.GeometryUtils;
66 import org.simantics.scenegraph.utils.InitValueSupport;
67 import org.simantics.scenegraph.utils.NodeUtil;
69 import gnu.trove.map.hash.THashMap;
72 * @author Tuukka Lehtonen
74 public class RouteGraphNode extends G2DNode implements ISelectionPainterNode, InitValueSupport {
76 private static final long serialVersionUID = -917194130412280965L;
78 private static final double TOLERANCE = IAction.TOLERANCE;
79 private static final Stroke SELECTION_STROKE = new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
80 private static final Color SELECTION_COLOR = new Color(255, 0, 255, 96);
82 private static final HighlightActionPointsAction highlightActions = new HighlightActionPointsAction(null);
84 protected RouteGraph rg;
85 protected IRouteGraphRenderer baseRenderer;
86 protected IRouteGraphRenderer renderer;
87 protected double pickTolerance = TOLERANCE;
88 protected boolean editable = true;
89 private boolean branchable = true;
91 protected IRouteGraphListener rgListener;
92 protected RouteGraphDelta rgDelta;
94 protected transient double mouseX;
95 protected transient double mouseY;
96 protected transient Point2D pt = new Point2D.Double();
98 protected transient MoveAction dragAction;
99 protected transient IAction currentAction;
100 protected transient Rectangle2D bounds;
103 * Dynamic color for connection rendering.
105 protected transient Color dynamicColor;
108 * Dynamic stroke for connection rendering.
110 protected transient Stroke dynamicStroke;
112 protected transient Path2D selectionPath = new Path2D.Double();
113 protected transient Stroke selectionStroke = null;
115 protected transient boolean highlightActionsEnabled = false;
116 protected transient AffineTransform lastViewTransform = null;
119 * x = NaN is used to indicate that possible branch point should not be
120 * rendered but interaction has not ended yet.
122 protected transient Point2D newBranchPointPosition = null;
125 protected transient Map<Object,ILineEndStyle> dynamicStyles = null;
127 private transient boolean ignoreSelection = false;
130 public void initValues() {
135 public void setIgnoreSelection(boolean value) {
136 ignoreSelection = value;
139 public boolean getIgnoreSelection() {
140 return ignoreSelection;
143 @PropertySetter("color")
144 @SyncField(value = {"dynamicColor"})
145 public void setDynamicColor(Color color) {
146 this.dynamicColor = color;
150 @PropertySetter("width")
151 @SyncField("dynamicStroke")
152 public void setDynamicStroke(Stroke stroke) {
153 this.dynamicStroke = stroke;
155 createSelectionStroke();
158 @SyncField(value = {"dynamicStyles"})
159 public void setDynamicLineEnd(RouteTerminal terminal, ILineEndStyle style) {
160 if (dynamicStyles == null)
161 dynamicStyles = new THashMap<Object, ILineEndStyle>();
162 terminal.setDynamicStyle(style);
163 if (terminal.getData() != null) {
165 dynamicStyles.put(terminal.getData(),style);
167 dynamicStyles.remove(terminal.getData());
171 private void updateLineEnds() {
172 if (dynamicStyles == null)
174 for (RouteTerminal t : rg.getTerminals()) {
175 if (t.getData() == null)
177 ILineEndStyle dynamicStyle = dynamicStyles.get(t.getData());
178 if (dynamicStyle != null)
179 t.setDynamicStyle(dynamicStyle);
183 @SyncField(value = {"rg"})
184 public void setRouteGraph(RouteGraph graph) {
190 @SyncField(value = {"rgDelta"})
191 public void setRouteGraphDelta(RouteGraphDelta delta) {
192 this.rgDelta = delta;
195 @SyncField(value = {"renderer"})
196 public void setRenderer(IRouteGraphRenderer renderer) {
198 this.baseRenderer = renderer;
201 createSelectionStroke();
204 private void createSelectionStroke() {
205 BasicConnectionStyle style = tryGetStyle();
206 selectionStroke = null;
208 BasicStroke stroke = (BasicStroke) style.getLineStroke();
209 if (stroke != null) {
210 float width = Math.max(stroke.getLineWidth() + 0.75f, stroke.getLineWidth()*1.3f);
211 selectionStroke = new BasicStroke(width, BasicStroke.CAP_BUTT, stroke.getLineJoin());
214 selectionStroke = SELECTION_STROKE;
218 private void wrapRenderer() {
220 if(baseRenderer == null) {
225 if(dynamicColor != null || dynamicStroke != null) {
226 BasicConnectionStyle baseStyle = (BasicConnectionStyle)tryGetStyle(baseRenderer);
228 Constructor<? extends BasicConnectionStyle> c = baseStyle.getClass().getConstructor(Color.class, Color.class, double.class, Stroke.class, Stroke.class, double.class, double.class);
229 renderer = new StyledRouteGraphRenderer(c.newInstance(
230 dynamicColor != null ? dynamicColor : baseStyle.getLineColor(),
231 baseStyle.getBranchPointColor(), baseStyle.getBranchPointRadius(),
232 dynamicStroke != null ? dynamicStroke : baseStyle.getLineStroke(),
233 dynamicStroke != null ? dynamicStroke : baseStyle.getRouteLineStroke(),
234 baseStyle.getDegeneratedLineLength(), baseStyle.getRounding()));
235 } catch (Exception e) {
236 renderer = new StyledRouteGraphRenderer(new BasicConnectionStyle(
237 dynamicColor != null ? dynamicColor : baseStyle.getLineColor(),
238 baseStyle.getBranchPointColor(), baseStyle.getBranchPointRadius(),
239 dynamicStroke != null ? dynamicStroke : baseStyle.getLineStroke(),
240 dynamicStroke != null ? dynamicStroke : baseStyle.getRouteLineStroke(),
241 baseStyle.getDegeneratedLineLength(), baseStyle.getRounding()));
246 renderer = baseRenderer;
251 @SyncField(value = {"pickTolerance"})
252 public void setPickTolerance(double tolerance) {
253 this.pickTolerance = tolerance;
256 @SyncField(value = {"editable"})
257 public void setEditable(boolean editable) {
258 this.editable = editable;
261 @SyncField(value = {"branchable"})
262 public void setBranchable(boolean branchable) {
263 this.branchable = branchable;
266 public RouteGraph getRouteGraph() {
270 public RouteGraphDelta getRouteGraphDelta() {
274 public IRouteGraphRenderer getRenderer() {
278 public boolean isEditable() {
282 public boolean isBranchable() {
286 public double getPickTolerance() {
287 return pickTolerance;
291 * When in client-server mode, listener is only set on the server side and
292 * fireRouteGraphChanged will tell it when rg has changed.
296 public void setRouteGraphListener(IRouteGraphListener listener) {
297 this.rgListener = listener;
303 * @return <code>true</code> if changes were fired
305 private boolean setRouteGraphAndFireChanges(RouteGraph before, RouteGraph after) {
306 RouteGraphDelta delta = new RouteGraphDelta(before, after);
307 if (!delta.isEmpty()) {
308 setRouteGraph(after);
309 setRouteGraphDelta(delta);
310 fireRouteGraphChanged(before, after, delta);
317 protected void fireRouteGraphChanged(RouteGraph before, RouteGraph after, RouteGraphDelta delta) {
318 if (rgListener != null) {
319 RouteGraphChangeEvent event = new RouteGraphChangeEvent(this, before, after, delta);
320 rgListener.routeGraphChanged(event);
324 public void showBranchPoint(Point2D p) {
325 newBranchPointPosition = p;
331 addEventHandler(this);
335 public void cleanup() {
337 removeEventHandler(this);
341 protected boolean isSelected() {
342 return NodeUtil.isSelected(this, 1);
346 public void render(Graphics2D g) {
347 if (renderer == null)
350 AffineTransform ot = null;
351 AffineTransform t = getTransform();
352 if (t != null && !t.isIdentity()) {
353 ot = g.getTransform();
354 g.transform(getTransform());
357 Object aaHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
358 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
360 boolean selected = ignoreSelection ? false : NodeUtil.isSelected(this, 1);
362 rg.updateTerminals();
364 if (currentAction != null) {
365 currentAction.render(g, renderer, mouseX, mouseY);
367 if (selected && selectionStroke != null) {
368 selectionPath.reset();
369 rg.getPath2D(selectionPath);
370 Shape selectionShape = selectionStroke.createStrokedShape(selectionPath);
371 g.setColor(SELECTION_COLOR);
372 g.fill(selectionShape);
375 renderer.render(g, rg);
377 renderer.renderGuides(g, rg);
379 if (selected && highlightActionsEnabled) {
380 // Needed for performing actions in #mouseClicked
381 this.lastViewTransform = g.getTransform();
382 highlightActions.setRouteGraph(rg);
383 highlightActions.render(g, renderer, mouseX, mouseY);
384 highlightActions.setRouteGraph(null);
388 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aaHint);
390 if (editable && branchable && newBranchPointPosition != null && !Double.isNaN(newBranchPointPosition.getX())) {
391 ConnectionStyle style = tryGetStyle();
393 style.drawBranchPoint(g, newBranchPointPosition.getX(), newBranchPointPosition.getY());
400 private BasicConnectionStyle tryGetStyle() {
401 return tryGetStyle(renderer);
404 private BasicConnectionStyle tryGetStyle(IRouteGraphRenderer renderer) {
405 if (renderer instanceof StyledRouteGraphRenderer) {
406 ConnectionStyle cs = ((StyledRouteGraphRenderer) renderer).getStyle();
407 if (cs instanceof BasicConnectionStyle)
408 return (BasicConnectionStyle) cs;
413 private double getSelectionStrokeWidth() {
414 if (selectionStroke instanceof BasicStroke) {
415 BasicStroke bs = (BasicStroke) selectionStroke;
416 return bs.getLineWidth();
422 public Rectangle2D getBoundsInLocal() {
426 protected void updateBounds() {
427 Rectangle2D r = this.bounds;
429 r = new Rectangle2D.Double();
430 this.bounds = calculateBounds(r);
432 // Need to expand to take stroke width into account.
433 double sw = getSelectionStrokeWidth() / 2;
434 GeometryUtils.expandRectangle(this.bounds, sw, sw);
437 protected Rectangle2D calculateBounds(Rectangle2D rect) {
438 RouteGraph rg = this.rg;
439 if (currentAction instanceof MoveAction)
440 rg = ((MoveAction) currentAction).getRouteGraph();
445 protected void getMouseLocalPos(MouseEvent e) {
446 //System.out.println("m: " + e.controlPosition);
447 pt.setLocation(e.controlPosition);
448 //System.out.println("parent: " + pt);
449 pt = NodeUtil.worldToLocal(this, pt, pt);
450 //System.out.println("local: " + pt);
456 protected boolean mouseDragged(MouseDragBegin e) {
457 if (dragAction != null && !e.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK) && e.button == MouseEvent.LEFT_BUTTON) {
458 currentAction = dragAction;
461 return updateCurrentAction(e, true);
465 protected boolean mouseMoved(MouseMovedEvent e) {
466 //System.out.println("mouse moved: " + e);
468 // Handle connection branching visualization.
470 if (newBranchPointPosition == null && e.hasAnyModifier(MouseEvent.ALT_MASK | MouseEvent.ALT_GRAPH_MASK)) {
471 if (bounds.contains(mouseX, mouseY)) {
472 newBranchPointPosition = new Point2D.Double(Double.NaN, Double.NaN);
475 if (newBranchPointPosition != null) {
476 RouteLine line = rg.pickLine(mouseX, mouseY, pickTolerance);
478 newBranchPointPosition.setLocation(mouseX, mouseY);
479 SplittedRouteGraph.snapToLine(newBranchPointPosition, line);
481 } else if (!Double.isNaN(newBranchPointPosition.getX())) {
482 newBranchPointPosition.setLocation(Double.NaN, Double.NaN);
487 // Make sure that highlight action rendering is according to mouse hover.
488 if (highlightActionsEnabled) {
489 if (NodeUtil.isSelected(this, 1)) {
494 return updateCurrentAction(e, false);
497 protected boolean updateCurrentAction(MouseEvent e, boolean updateMousePos) {
498 boolean oldHighlight = highlightActionsEnabled;
499 highlightActionsEnabled = e.hasAllModifiers(MouseEvent.CTRL_MASK);
500 if (oldHighlight != highlightActionsEnabled)
503 if (currentAction != null) {
508 // Repaint, but only if absolutely necessary.
509 if (currentAction instanceof MoveAction || bounds.contains(mouseX, mouseY))
518 protected boolean mouseClicked(MouseClickEvent e) {
522 if (e.button == MouseEvent.LEFT_BUTTON) {
523 if (isSelected() && highlightActionsEnabled) {
524 // Reconnection / segment deletion only available for branched connections.
525 if (rg.getTerminals().size() > 2) {
526 Pick pick = highlightActions.pickAction(rg, lastViewTransform, mouseX, mouseY);
527 if (pick.hasAction(Action.REMOVE)) {
528 RemoveLineAction remove = RemoveLineAction.perform(rg, pick.line.getLine(), mouseX, mouseY);
529 if (remove != null) {
530 setRouteGraphAndFireChanges(remove.getOriginalRouteGraph(), remove.getRouteGraph());
535 if (pick.hasAction(Action.RECONNECT)) {
536 currentAction = ReconnectLineAction.create(rg, mouseX, mouseY);
537 if (currentAction != null) {
549 protected boolean mouseButtonPressed(MouseButtonPressedEvent e) {
553 if (e.button == MouseEvent.LEFT_BUTTON) {
554 // Visualize new branch point no longer.
555 newBranchPointPosition = null;
559 // if(currentAction instanceof HighlightActionPointsAction) {
560 // RemoveLineAction remove = RemoveLineAction.perform(rg, mouseX, mouseY);
561 // if (remove != null) {
562 // setRouteGraphAndFireChanges(remove.getOriginalRouteGraph(), remove.getRouteGraph());
565 // currentAction = ReconnectLineAction.create(rg, mouseX, mouseY);
566 // if (currentAction != null)
571 if(currentAction instanceof IReconnectAction) {
572 RouteGraph originalRg = rg.copy();
573 ((IReconnectAction)currentAction).finish(mouseX, mouseY);
574 currentAction = null;
576 setRouteGraphAndFireChanges(originalRg, rg);
578 currentAction = null;
583 if (!allowConnectionRerouting()) {
586 //System.out.println("move action");
587 dragAction = SnappingMoveAction.create(rg, mouseX, mouseY, pickTolerance, moveFilter, getSnapAdvisor());
588 //System.out.println("DRAG ACTION: " + dragAction);
591 //System.out.println(this + " NEW action: " + currentAction);
592 if (currentAction != null)
599 * Checks the selections data node in the scene graph for any links
602 private boolean allowConnectionRerouting() {
603 final int maxOtherNodesSelected = 1;
605 INode selections = NodeUtil.tryLookup(this, "selections");
606 if (!(selections instanceof G2DParentNode))
608 G2DParentNode p = (G2DParentNode) selections;
609 for (IG2DNode selection : p.getNodes()) {
610 if (!(selection instanceof G2DParentNode))
613 G2DParentNode sp = (G2DParentNode) selection;
614 Collection<IG2DNode> links = sp.getNodes();
617 int othersSelected = 0;
618 for (IG2DNode link : links) {
619 if (link instanceof LinkNode) {
620 INode node = ((LinkNode) link).getDelegate();
621 if (!NodeUtil.isParentOf(node, this)) {
623 if (othersSelected > maxOtherNodesSelected)
628 if (othersSelected > maxOtherNodesSelected)
634 protected ISnapAdvisor getSnapAdvisor() {
635 GridNode grid = lookupNode(GridNode.GRID_NODE_ID, GridNode.class);
636 return grid != null ? grid.getSnapAdvisor() : null;
639 MoveAction.TargetFilter moveFilter = new MoveAction.TargetFilter() {
641 public boolean accept(Object target) {
642 return (target instanceof RouteLine) || (target instanceof RouteLink);
647 protected boolean handleCommand(CommandEvent e) {
648 /*if (Commands.DELETE.equals(e.command)) {
649 Object target = rg.pick(mouseX, mouseY, pickTolerance);
650 return deleteTarget(target);
651 } else if (Commands.SPLIT_CONNECTION.equals(e.command)) {
652 Object target = rg.pick(mouseX, mouseY, pickTolerance);
653 return splitTarget(target);
655 if (Commands.CANCEL.equals(e.command)) {
656 return cancelCurrentAction();
661 protected boolean mouseButtonReleased(MouseButtonReleasedEvent e) {
662 if (currentAction instanceof MoveAction) {
663 MoveAction move = (MoveAction) currentAction;
664 RouteGraph originalRg = rg.copy();
665 move.finish(mouseX, mouseY);
667 setRouteGraphAndFireChanges(originalRg, rg);
669 currentAction = null;
677 protected boolean keyPressed(KeyPressedEvent e) {
681 if (!e.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK) && e.keyCode == KeyEvent.VK_S) {
682 Object target = rg.pick(mouseX, mouseY, pickTolerance, RouteGraph.PICK_PERSISTENT_LINES | RouteGraph.PICK_TRANSIENT_LINES);
683 return splitTarget(target);
685 else if (!e.hasAnyModifier(MouseEvent.ALT_MASK | MouseEvent.ALT_GRAPH_MASK | MouseEvent.CTRL_MASK) && (e.keyCode == KeyEvent.VK_R || e.keyCode == KeyEvent.VK_D)) {
686 Object target = rg.pick(mouseX, mouseY, pickTolerance, RouteGraph.PICK_PERSISTENT_LINES);
687 return deleteTarget(target);
689 else if (e.keyCode == KeyEvent.VK_ESCAPE) {
690 return cancelCurrentAction();
692 // else if (e.keyCode == KeyEvent.VK_D) {
693 // if (target instanceof RouteTerminal) {
694 // RouteTerminal terminal = (RouteTerminal) target;
695 // RouteGraph before = rg.copy();
696 // rg.toggleDirectLines(terminal);
697 // setRouteGraphAndFireChanges(before, rg);
701 // else if (target != null && e.getKeyCode() == KeyEvent.VK_P) {
704 else if (e.keyCode == KeyEvent.VK_CONTROL) {
705 highlightActionsEnabled = true;
708 else if (e.keyCode == KeyEvent.VK_ALT) {
709 // Begin connection branching visualization.
710 RouteLine line = rg.pickLine(mouseX, mouseY, pickTolerance);
711 if (branchable && line != null) {
712 newBranchPointPosition = new Point2D.Double(mouseX, mouseY);
713 SplittedRouteGraph.snapToLine(newBranchPointPosition, line);
722 protected boolean keyReleased(KeyReleasedEvent e) {
723 if (e.keyCode == KeyEvent.VK_ALT) {
724 // End connection branching visualization.
725 if (newBranchPointPosition != null) {
726 newBranchPointPosition = null;
730 if (e.keyCode == KeyEvent.VK_CONTROL) {
731 highlightActionsEnabled = false;
738 private boolean cancelCurrentAction() {
739 if (currentAction != null) {
740 currentAction = null;
747 private boolean splitTarget(Object target) {
748 if (target instanceof RouteLine) {
749 RouteLine rLine = (RouteLine)target;
750 RouteGraph before = rg.copy();
751 rg.split(rLine, rLine.isHorizontal() ? mouseX : mouseY);
752 setRouteGraphAndFireChanges(before, rg);
759 private boolean deleteTarget(Object target) {
760 boolean changed = false;
761 if (target instanceof RouteLine) {
762 RouteLine line = (RouteLine) target;
763 RouteGraph before = rg.copy();
765 changed = setRouteGraphAndFireChanges(before, rg);
767 else if (target instanceof RouteLink) {
768 RouteGraph before = rg.copy();
769 rg.deleteCorner((RouteLink) target);
770 changed = setRouteGraphAndFireChanges(before, rg);
772 // else if (target instanceof RouteTerminal) {
773 // RouteGraph before = rg.copy();
774 // rg.remove((RouteTerminal) target);
775 // changed = setRouteGraphAndFireChanges(before, rg);
783 * A version of MoveAction that snaps movements using the specified
786 static class SnappingMoveAction extends MoveAction {
788 private ISnapAdvisor snapAdvisor;
789 private Point2D point = new Point2D.Double();
791 public SnappingMoveAction(RouteGraph rg, Object target, ISnapAdvisor snapAdvisor) {
793 this.snapAdvisor = snapAdvisor;
796 protected void move(RouteGraph rg, Object target, double x, double y) {
797 point.setLocation(x, y);
798 snapAdvisor.snap(point);
799 super.move(rg, target, point.getX(), point.getY());
802 public static MoveAction create(RouteGraph rg, double x, double y, double tolerance, TargetFilter filter, ISnapAdvisor snapAdvisor) {
803 Object target = rg.pick(x, y, tolerance, RouteGraph.PICK_LINES | RouteGraph.PICK_INTERIOR_POINTS);
804 if (target != null && (filter == null || filter.accept(target))) {
805 if (snapAdvisor != null)
806 return new SnappingMoveAction(rg, target, snapAdvisor);
807 return new MoveAction(rg, target);
815 public int getEventMask() {
816 return EventTypes.CommandMask | EventTypes.KeyMask | EventTypes.MouseMask;