();
- this.elementsToDrag = elementsToDrag;
-
- if (!Collections.disjoint(sel, picks)) {
- elementsToDrag.addAll(sel);
- } else {
- if (topMostPick != null && (sel.isEmpty() || !sel.contains(topMostPick))) {
- selection.setSelection(me.mouseId, topMostPick);
- sel = selection.getSelection(me.mouseId);
- elementsToDrag.addAll(sel);
- }
- }
-
- // Drag Elements
- if (!elementsToDrag.isEmpty() && hasElementDnDDrag()) {
- // To Be Implemented in the next Diagram data model.
- } else {
- if (!anyModifierPressed && !elementsToDrag.isEmpty() && hasElementDrag()) {
- // Connections are not translatable, re-routing is in RouteGraphNode.
- boolean onlyConnections = onlyConnections(elementsToDrag);
- if (!onlyConnections) {
- ICanvasParticipant tm = createTranslateTool(me.mouseId, me.startCanvasPos, curCanvasPos, elementsToDrag);
- if (tm != null) {
- getContext().add(tm);
- return !onlyConnections;
- }
- }
- }
- }
-
- return false;
- }
-
- /**
- * Always invoked after after {@link #handleDrag(MouseDragBegin)} and scene
- * graph event handling to prevent the box selection mode from being
- * initiated before scene graph nodes have a chance to react.
- *
- *
- * Note that this method assumes that elementsToDrag
and
- * curCanvasPos
are already set by
- * {@link #handleDrag(MouseDragBegin)}.
- *
- * @param me
- * @return
- */
- @EventHandler(priority = BOX_SELECT_PRIORITY)
- public boolean handleBoxSelect(MouseDragBegin me) {
- if (!hasBoxSelect()) return false;
- if (me.button != MouseEvent.LEFT_BUTTON) return false;
- if (getToolMode() != Hints.POINTERTOOL) return false;
- if (hasToolMode(me.mouseId)) return false;
-
- boolean nonSelectionModifierPressed = me.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK
- ^ (MouseEvent.SHIFT_MASK /*| MouseEvent.CTRL_MASK*/));
- if (nonSelectionModifierPressed)
- return false;
-
- if (!nonSelectionModifierPressed && elementsToDrag.isEmpty()) {
- // Box Select
- ICanvasParticipant bsm = createBoxSelectTool(me.mouseId, me.startCanvasPos, curCanvasDragPos, me.button, boxSelectMode);
- if (bsm != null)
- getContext().add(bsm);
- }
-
- return false;
- }
-
- private static boolean onlyConnections(Set elements) {
- for (IElement e : elements)
- if (!e.getElementClass().containsClass(ConnectionHandler.class))
- return false;
- return true;
- }
-
- private IElement rotatingPick(int selectionId, List pickables) {
- Set sel = selection.getSelection(selectionId);
- return rotatingPick(sel, pickables);
- }
-
- private IElement rotatingPick(Set sel, List pickables) {
- int earliestIndex = pickables.size();
- for (int i = pickables.size() - 1; i >= 0; --i) {
- if (sel.contains(pickables.get(i))) {
- earliestIndex = i;
- break;
- }
- }
- if (earliestIndex == 0)
- earliestIndex = pickables.size();
- IElement selectedPick = pickables.get(earliestIndex - 1);
- return selectedPick;
- }
-
- /**
- * Is mouse in some kind of mode?
- * @param mouseId
- * @return
- */
- boolean hasToolMode(int mouseId) {
- for (AbstractMode am : getContext().getItemsByClass(AbstractMode.class))
- if (am.mouseId==mouseId) return true;
- return false;
- }
-
- boolean hasToolMode(IToolMode mode) {
- return ObjectUtils.objectEquals(mode, getToolMode());
- }
-
- boolean hasToolMode(IToolMode... modes) {
- IToolMode current = getToolMode();
- if (current == null)
- return false;
- for (IToolMode mode : modes)
- if (current.equals(mode))
- return true;
- return false;
- }
-
- protected IToolMode getToolMode() {
- return getHint(Hints.KEY_TOOL);
- }
-
- /// is box select enabled
- protected boolean hasBoxSelect() {
- return boxSelect;
- }
-
- /// is click select enabled
- protected boolean hasClickSelect() {
- return clickSelect;
- }
-
- /// is double click edit enabled
- protected boolean hasDoubleClickEdit() {
- return doubleClickEdit;
- }
-
- // is element drag enabled
- protected boolean hasElementDrag() {
- return dragElement;
- }
-
- // is element drag enabled
- protected boolean hasElementDnDDrag() {
- return dndDragElement;
- }
-
- // is connect enabled
- protected boolean connects() {
- return connect;
- }
-
- public double getPickDistance() {
- Double pickDistance = getHint(KEY_PICK_DISTANCE);
- return pickDistance == null ? PICK_DIST : Math.max(pickDistance, 0);
- }
-
- public PickPolicy getBoxSelectMode() {
- return boxSelectMode;
- }
-
- public void setBoxSelectMode(PickPolicy boxSelectMode) {
- this.boxSelectMode = boxSelectMode;
- }
-
- public void setSelectionEnabled(boolean select) {
- this.clickSelect = select;
- this.boxSelect = select;
- if(select == false) { // Clear all selections if select is disabled
- final int[] ids = selection.getSelectionIds();
- if(ids.length > 0) {
- ThreadUtils.asyncExec(getContext().getThreadAccess(), new Runnable() {
- @Override
- public void run() {
- for(int id : ids)
- selection.clear(id);
- getContext().getContentContext().setDirty();
- }
- });
- }
- }
- }
-
- boolean anyModifierPressed(MouseEvent e) {
- return e.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK);
- }
-
- boolean connectToolModifiersPressed(int stateMask) {
- return (stateMask & (MouseEvent.ALT_MASK | MouseEvent.ALT_GRAPH_MASK)) != 0;
- }
-
- static {
- LINE10 = new Path2D.Double();
- LINE10.moveTo(0, 0);
- LINE10.lineTo(10, 0);
- LINE10.lineTo(7, -3);
- LINE10.moveTo(10, 0);
- LINE10.lineTo(7, 3);
-
- LINE15 = new Path2D.Double();
- LINE15.moveTo(0, 0);
- LINE15.lineTo(15, 0);
- LINE15.lineTo(12, -3);
- LINE15.moveTo(15, 0);
- LINE15.lineTo(12, 3);
-
- LINE20 = new Path2D.Double();
- LINE20.moveTo(0, 0);
- LINE20.lineTo(20, 0);
- LINE20.lineTo(17, -3);
- LINE20.moveTo(20, 0);
- LINE20.lineTo(17, 3);
-
- }
-
- // CUSTOMIZE
-
- protected ICanvasParticipant createConnectTool(TerminalInfo ti, int mouseId, Point2D startCanvasPos) {
- return null;
- }
-
- protected ICanvasParticipant createConnectToolWithTerminals(List tis, int mouseId, Point2D startCanvasPos) {
- return null;
- }
-
- protected ICanvasParticipant createTranslateTool(int mouseId, Point2D startCanvasPos, Point2D curCanvasPos, Set elementsToDrag) {
- return new TranslateMode(startCanvasPos, curCanvasPos, mouseId, elementsToDrag);
- }
-
- protected ICanvasParticipant createBoxSelectTool(int mouseId, Point2D startCanvasPos, Point2D curCanvasPos, int button, PickPolicy boxSelectMode) {
- return new BoxSelectionMode(startCanvasPos, curCanvasPos, mouseId, button, boxSelectMode);
- }
-
- /**
- * A context listener for resetting tool mode back to pointer mode after the
- * tracked participant has been removed.
- */
- protected class ToolModeResetter implements IContextListener {
- private ICanvasParticipant tracked;
- public ToolModeResetter(ICanvasParticipant trackedParticipant) {
- this.tracked = trackedParticipant;
- }
- @Override
- public void itemAdded(IContext sender, ICanvasParticipant item) {
- }
- @Override
- public void itemRemoved(IContext sender, ICanvasParticipant item) {
- if (item == tracked) {
- sender.removeContextListener(this);
- if (!isRemoved() && !connectToolModifiersPressed(lastStateMask)) {
- temporarilyEnabledConnectTool = false;
- setHint(Hints.KEY_TOOL, Hints.POINTERTOOL);
- }
- }
- }
- }
-
-}
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.g2d.diagram.participant.pointertool;
+
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Path2D;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.simantics.g2d.canvas.Hints;
+import org.simantics.g2d.canvas.ICanvasContext;
+import org.simantics.g2d.canvas.ICanvasParticipant;
+import org.simantics.g2d.canvas.IToolMode;
+import org.simantics.g2d.canvas.impl.CanvasContext;
+import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;
+import org.simantics.g2d.canvas.impl.DependencyReflection.Reference;
+import org.simantics.g2d.connection.IConnectionAdvisor;
+import org.simantics.g2d.connection.handler.ConnectionHandler;
+import org.simantics.g2d.diagram.DiagramHints;
+import org.simantics.g2d.diagram.handler.PickContext;
+import org.simantics.g2d.diagram.handler.PickRequest;
+import org.simantics.g2d.diagram.handler.PickRequest.PickPolicy;
+import org.simantics.g2d.diagram.handler.PickRequest.PickSorter;
+import org.simantics.g2d.diagram.participant.AbstractDiagramParticipant;
+import org.simantics.g2d.diagram.participant.Selection;
+import org.simantics.g2d.diagram.participant.TerminalPainter;
+import org.simantics.g2d.diagram.participant.TerminalPainter.ChainedHoverStrategy;
+import org.simantics.g2d.diagram.participant.TerminalPainter.TerminalHoverStrategy;
+import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil.TerminalInfo;
+import org.simantics.g2d.element.ElementClassProviders;
+import org.simantics.g2d.element.IElement;
+import org.simantics.g2d.element.IElementClassProvider;
+import org.simantics.g2d.participant.KeyUtil;
+import org.simantics.g2d.participant.MouseUtil;
+import org.simantics.g2d.participant.TransformUtil;
+import org.simantics.g2d.scenegraph.SceneGraphConstants;
+import org.simantics.g2d.utils.CanvasUtils;
+import org.simantics.g2d.utils.GeometryUtils;
+import org.simantics.scenegraph.g2d.events.Event;
+import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
+import org.simantics.scenegraph.g2d.events.KeyEvent;
+import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;
+import org.simantics.scenegraph.g2d.events.command.Commands;
+import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;
+import org.simantics.utils.ObjectUtils;
+import org.simantics.utils.datastructures.context.IContext;
+import org.simantics.utils.datastructures.context.IContextListener;
+import org.simantics.utils.datastructures.hints.IHintContext.Key;
+import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
+import org.simantics.utils.threads.ThreadUtils;
+
+/**
+ * Pointer tool does the following operations with mouse:
+ *
+ * - Selections
+ * - Scale
+ * - Rotate
+ * - Translate
+ * - Draws connections (requires re-implementing
+ * {@link #createConnectTool(TerminalInfo, int, Point2D)})
+ *
+ *
+ * Pointer tool is active only when {@link Hints#KEY_TOOL} is
+ * {@value Hints#POINTERTOOL}.
+ *
+ * @author Toni Kalajainen
+ */
+public class PointerInteractor extends AbstractDiagramParticipant {
+
+ /**
+ * A hint key for terminal pick distance in control pixels.
+ * @see #PICK_DIST
+ */
+ public static final Key KEY_PICK_DISTANCE = new KeyOf(Double.class, "PICK_DISTANCE");
+
+ /**
+ * Default terminal pick distance in control pixels.
+ * @see #DEFAULT_PICK_DISTANCE
+ */
+ public static final double PICK_DIST = 10;
+
+ /**
+ * @see #altToggled(KeyEvent)
+ */
+ protected int lastStateMask;
+
+ /**
+ * @see #altToggled(KeyEvent)
+ */
+ protected boolean temporarilyEnabledConnectTool = false;
+
+ public class DefaultHoverStrategy extends ChainedHoverStrategy {
+ public DefaultHoverStrategy(TerminalHoverStrategy orig) {
+ super(orig);
+ }
+ @Override
+ public boolean highlightEnabled() {
+ if (Hints.CONNECTTOOL.equals(getToolMode()))
+ return true;
+
+ boolean ct = connectToolModifiersPressed(lastStateMask);
+ //System.out.println("highlightEnabled: " + String.format("%x", lastStateMask) + " : " + ct);
+ return ct;
+ }
+ @Override
+ public boolean canHighlight(TerminalInfo ti) {
+ //boolean alt = (lastStateMask & MouseEvent.ALT_MASK) != 0;
+ //System.out.println("canHighlight: " + String.format("%x", lastStateMask) + " : " + alt);
+ //if (!alt)
+ // return false;
+ IConnectionAdvisor advisor = diagram.getHint(DiagramHints.CONNECTION_ADVISOR);
+ return advisor == null || advisor.canBeginConnection(null, ti.e, ti.t);
+ }
+ }
+
+ @Dependency Selection selection;
+ @Dependency KeyUtil keys;
+ @Dependency TransformUtil util;
+ @Dependency PickContext pickContext;
+ @Dependency MouseUtil mice;
+ @Reference TerminalPainter terminalPainter;
+
+ /**
+ * This must be higher than
+ * {@link SceneGraphConstants#SCENEGRAPH_EVENT_PRIORITY}
+ * ({@value SceneGraphConstants#SCENEGRAPH_EVENT_PRIORITY}) to allow for
+ * better selection handling than is possible by using the plain scene graph
+ * event handling facilities which are installed in {@link CanvasContext}.
+ */
+ public static final int TOOL_PRIORITY = 1 << 21;
+
+ /**
+ * This must be lower than
+ * {@link SceneGraphConstants#SCENEGRAPH_EVENT_PRIORITY} (
+ * {@value SceneGraphConstants#SCENEGRAPH_EVENT_PRIORITY}) to not start box
+ * handling before scene graph nodes have been given a chance to react
+ * events.
+ */
+ public static final int BOX_SELECT_PRIORITY = 1 << 19;
+
+ private static final Path2D LINE10;
+ private static final Path2D LINE15;
+ private static final Path2D LINE20;
+
+ boolean clickSelect;
+ boolean boxSelect;
+ boolean dragElement, dndDragElement;
+ boolean connect;
+ boolean doubleClickEdit;
+ protected IElementClassProvider elementClassProvider;
+
+ PickPolicy boxSelectMode = PickPolicy.PICK_CONTAINED_OBJECTS;
+
+ DefaultHoverStrategy hoverStrategy;
+
+ private PickSorter pickSorter;
+
+ public PointerInteractor() {
+ this(true, true, true, false, true, false, ElementClassProviders.staticProvider(null), null);
+ }
+
+ public PointerInteractor(PickSorter pickSorter) {
+ this(true, true, true, false, true, false, ElementClassProviders.staticProvider(null), pickSorter);
+ }
+
+ public PointerInteractor(boolean clickSelect, boolean boxSelect, boolean dragElement, boolean dndDragElement, boolean connect, IElementClassProvider ecp) {
+ this(clickSelect, boxSelect, dragElement, dndDragElement, connect, false, ecp, null);
+ }
+
+ public PointerInteractor(boolean clickSelect, boolean boxSelect, boolean dragElement, boolean dndDragElement, boolean connect, boolean doubleClickEdit, IElementClassProvider ecp, PickSorter pickSorter) {
+ super();
+ this.clickSelect = clickSelect;
+ this.boxSelect = boxSelect;
+ this.dragElement = dragElement;
+ this.dndDragElement = dndDragElement;
+ this.connect = connect;
+ this.doubleClickEdit = doubleClickEdit;
+ this.elementClassProvider = ecp;
+ this.pickSorter = pickSorter;
+ }
+
+ @Override
+ public void addedToContext(ICanvasContext ctx) {
+ super.addedToContext(ctx);
+ hoverStrategy = new DefaultHoverStrategy((TerminalHoverStrategy) getHint(TerminalPainter.TERMINAL_HOVER_STRATEGY));
+ setHint(TerminalPainter.TERMINAL_HOVER_STRATEGY, hoverStrategy);
+ }
+
+ @EventHandler(priority = 0)
+ public boolean handleStateMask(MouseEvent me) {
+ lastStateMask = me.stateMask;
+ if (temporarilyEnabledConnectTool) {
+ if (!connectToolModifiersPressed(me.stateMask)) {
+ temporarilyEnabledConnectTool = false;
+ setHint(Hints.KEY_TOOL, Hints.POINTERTOOL);
+ }
+ } else {
+ // It may be that the mouse has come into this control
+ // from outside and no key state changes have occurred yet.
+ // In this case this code should take care of moving the canvas
+ // context into CONNECTTOOL mode.
+ if (getToolMode() == Hints.POINTERTOOL
+ && connectToolModifiersPressed(me.stateMask))
+ {
+ temporarilyEnabledConnectTool = true;
+ setHint(Hints.KEY_TOOL, Hints.CONNECTTOOL);
+ }
+ }
+ return false;
+ }
+
+ private static int mask(int mask, int mask2, boolean set) {
+ return set ? mask | mask2 : mask & ~mask2;
+ }
+
+ @EventHandler(priority = -1)
+ public boolean altToggled(KeyEvent ke) {
+ int mods = ke.stateMask;
+ boolean press = ke instanceof KeyPressedEvent;
+ boolean modifierPressed = false;
+ if (ke.keyCode == java.awt.event.KeyEvent.VK_ALT) {
+ mods = mask(mods, MouseEvent.ALT_MASK, press);
+ modifierPressed = true;
+ }
+ if (ke.keyCode == java.awt.event.KeyEvent.VK_ALT_GRAPH) {
+ mods = mask(mods, MouseEvent.ALT_GRAPH_MASK, press);
+ modifierPressed = true;
+ }
+ if (ke.keyCode == java.awt.event.KeyEvent.VK_SHIFT) {
+ mods = mask(mods, MouseEvent.SHIFT_MASK, press);
+ modifierPressed = true;
+ }
+ if (ke.keyCode == java.awt.event.KeyEvent.VK_CONTROL) {
+ mods = mask(mods, MouseEvent.CTRL_MASK, press);
+ modifierPressed = true;
+ }
+ if (ke.keyCode == java.awt.event.KeyEvent.VK_META) {
+ // TODO: NO MASK FOR META!
+ modifierPressed = true;
+ }
+ // Don't deny connecting when CTRL is marked pressed because ALT_GRAPH
+ // is actually ALT+CTRL in SWT. There's no way in SWT to tell apart
+ // CTRL+ALT and ALT GRAPH.
+ boolean otherModifiers = (mods & (MouseEvent.SHIFT_MASK)) != 0;
+ boolean altModifier = (mods & (MouseEvent.ALT_MASK | MouseEvent.ALT_GRAPH_MASK)) != 0;
+ if (modifierPressed) {
+ boolean altPressed = !otherModifiers && altModifier;
+ lastStateMask = mods;
+ if (altPressed) {
+ IToolMode mode = getToolMode();
+ if (mode == Hints.POINTERTOOL) {
+ //System.out.println("TEMP++");
+ temporarilyEnabledConnectTool = true;
+ setHint(Hints.KEY_TOOL, Hints.CONNECTTOOL);
+ }
+ } else {
+ if (temporarilyEnabledConnectTool) {
+ //System.out.println("TEMP--");
+ temporarilyEnabledConnectTool = false;
+ setHint(Hints.KEY_TOOL, Hints.POINTERTOOL);
+ }
+ }
+ // Make sure that TerminalPainter updates its scene graph.
+ if (terminalPainter != null) {
+ terminalPainter.update(terminalPainter.highlightEnabled());
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @param controlPos
+ * @return null
if current canvas transform is not invertible
+ */
+ public Shape getCanvasPickShape(Point2D controlPos) {
+ AffineTransform inverse = util.getInverseTransform();
+ if (inverse == null)
+ return null;
+
+ double pd = getPickDistance();
+ Rectangle2D controlPickRect = new Rectangle2D.Double(controlPos.getX()-pd, controlPos.getY()-pd, pd*2+1, pd*2+1);
+ Shape canvasShape = GeometryUtils.transformShape(controlPickRect, inverse);
+ return canvasShape;
+ }
+
+ public List pickTerminals(Point2D controlPos)
+ {
+ Shape canvasPickRect = getCanvasPickShape(controlPos);
+ if (canvasPickRect == null)
+ return Collections.emptyList();
+ return TerminalUtil.pickTerminals(getContext(), diagram, canvasPickRect, true, true);
+ }
+
+ public TerminalInfo pickTerminal(Point2D controlPos)
+ {
+ Shape canvasPickRect = getCanvasPickShape(controlPos);
+ if (canvasPickRect == null)
+ return null;
+ TerminalInfo ti = TerminalUtil.pickTerminal(getContext(), diagram, canvasPickRect);
+ return ti;
+ }
+
+ @EventHandler(priority = TOOL_PRIORITY)
+ public boolean handlePress(MouseButtonPressedEvent me) {
+ if (!connects())
+ return false;
+ if (me.button != MouseEvent.LEFT_BUTTON)
+ return false;
+
+ IToolMode mode = getToolMode();
+
+ // It may be that the mouse has come into this control
+ // from outside without focusing it and without any mouse
+ // buttons being pressed. If the user is pressing the
+ // connection modifier we need to temporarily enable connect
+ // mode here and now.
+ if (mode == Hints.POINTERTOOL && connectToolModifiersPressed(me.stateMask)) {
+ temporarilyEnabledConnectTool = true;
+ mode = Hints.CONNECTTOOL;
+ setHint(Hints.KEY_TOOL, Hints.CONNECTTOOL);
+ }
+
+ if (mode == Hints.CONNECTTOOL) {
+ Point2D curCanvasPos = util.controlToCanvas(me.controlPosition, null);
+ return checkInitiateConnectTool(me, curCanvasPos);
+ }
+
+ return false;
+ }
+
+ protected boolean checkInitiateConnectTool(MouseEvent me, Point2D mouseCanvasPos) {
+ // Pick Terminal
+ IToolMode mode = getToolMode();
+ if (mode == Hints.CONNECTTOOL || connectToolModifiersPressed(me.stateMask)) {
+ IConnectionAdvisor advisor = diagram.getHint(DiagramHints.CONNECTION_ADVISOR);
+ TerminalInfo ti = pickTerminal(me.controlPosition);
+
+ ICanvasParticipant bsi = null;
+ if (ti != null) {
+ if (advisor == null || advisor.canBeginConnection(null, ti.e, ti.t)) {
+ bsi = createConnectTool(ti, me.mouseId, mouseCanvasPos);
+ }
+ } else {
+ ISnapAdvisor snapAdvisor = getHint(DiagramHints.SNAP_ADVISOR);
+ if (snapAdvisor != null)
+ snapAdvisor.snap(mouseCanvasPos);
+
+ // Start connection out of thin air, without a terminal.
+ bsi = createConnectTool(null, me.mouseId, mouseCanvasPos);
+ }
+
+ // Did we catch anything?
+ if (bsi != null) {
+ startConnectTool(bsi);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ protected void startConnectTool(ICanvasParticipant tool) {
+ getContext().add(tool);
+ //System.out.println("TEMP: " + temporarilyEnabledConnectTool);
+ if (temporarilyEnabledConnectTool) {
+ // Resets pointer tool back into use if necessary after
+ // connection has been finished or canceled.
+ getContext().addContextListener(new ToolModeResetter(tool));
+ }
+ }
+
+ @EventHandler(priority = TOOL_PRIORITY)
+ public boolean handleClick(MouseClickEvent me) {
+ //System.out.println(getClass().getSimpleName() + ": mouse clicked: @ " + me.time);
+
+ if (hasDoubleClickEdit() && me.clickCount == 2) {
+ if (handleDoubleClick(me))
+ return true;
+ }
+
+ if (!hasClickSelect()) return false;
+ if (!hasToolMode(Hints.POINTERTOOL)) return false;
+
+ // Don't handle any events where click count is more than 1 to prevent
+ // current diagram selection from bouncing around unnecessarily.
+ if (me.clickCount > 1)
+ return false;
+
+ boolean isLeft = me.button == MouseEvent.LEFT_BUTTON;
+ boolean isRight = me.button == MouseEvent.RIGHT_BUTTON;
+ if (!isLeft && !isRight) return false;
+
+ boolean popupWasVisible = wasPopupJustClosed(me);
+ boolean noModifiers = !anyModifierPressed(me);
+ boolean isShiftPressed = me.hasAllModifiers(MouseEvent.SHIFT_MASK);
+
+ assertDependencies();
+
+ // Pick Terminal
+ double pickDist = getPickDistance();
+ Rectangle2D controlPickRect = new Rectangle2D.Double(me.controlPosition.getX()-pickDist, me.controlPosition.getY()-pickDist, pickDist*2+1, pickDist*2+1);
+ Shape canvasPickRect = GeometryUtils.transformShape(controlPickRect, util.getInverseTransform());
+ int selectionId = me.mouseId;
+
+ PickRequest req = new PickRequest(canvasPickRect).context(getContext());
+ req.pickPolicy = PickPolicy.PICK_INTERSECTING_OBJECTS;
+ req.pickSorter = pickSorter;
+ //req.pickSorter = PickRequest.PickSorter.CONNECTIONS_LAST;
+ List pickables = new ArrayList();
+ pickContext.pick(diagram, req, pickables);
+
+ Set currentSelection = selection.getSelection(selectionId);
+
+ // Clear selection
+ if (pickables.isEmpty()) {
+ if (!popupWasVisible) {
+ // Only clear selection on left button clicks.
+ if (isLeft) {
+ selection.clear(selectionId);
+ }
+ }
+ if (isRight) {
+ if (/*!currentSelection.isEmpty() &&*/ noModifiers)
+ setHint(DiagramHints.SHOW_POPUP_MENU, me.controlPosition);
+ }
+ return false;
+ }
+
+ // Toggle select
+ if ((me.stateMask & MouseEvent.CTRL_MASK) != 0) {
+ if (isLeft) {
+ /*
+ * - If the mouse points to an object not in the selection, add it to selection.
+ * - If the mouse points to multiple objects, add the first.
+ * - If all objects the mouse points are already in selection, remove one of them from selection.
+ */
+ IElement removable = null;
+ for (int i = pickables.size() - 1; i >= 0; --i) {
+ IElement pickable = pickables.get(i);
+ if (selection.add(selectionId, pickable)) {
+ removable = null;
+ break;
+ } else
+ removable = pickable;
+
+ // Do not perform rotating pick in toggle selection
+ // when only CTRL is pressed. Requires SHIFT+CTRL.
+ if (!isShiftPressed)
+ break;
+ }
+ if (removable != null)
+ selection.remove(selectionId, removable);
+ }
+ return false;
+ }
+
+ boolean result = false;
+
+ // Click Select
+ {
+ if (isLeft && popupWasVisible)
+ // Popup menu is visible, just let it close
+ return false;
+
+ // Don't change selection on right clicks if there's more to pick
+ // than a single element.
+ if (isRight && pickables.size() > 1) {
+ IElement selectElement = singleElementAboveNonselectedConnections(currentSelection, pickables);
+ if (selectElement != null) {
+ selection.setSelection(selectionId, selectElement);
+ }
+ if (!currentSelection.isEmpty() && noModifiers)
+ setHint(DiagramHints.SHOW_POPUP_MENU, me.controlPosition);
+ return false;
+ }
+
+ /*
+ * Select the one object the mouse points to. If multiple object
+ * are picked, select the one that is after the earliest by
+ * index of the current selection when shift is pressed. Otherwise
+ * always pick the topmost element.
+ */
+ IElement selectedPick = isShiftPressed
+ ? rotatingPick(currentSelection, pickables)
+ : pickables.get(pickables.size() - 1);
+
+ // Only select when
+ // 1. the selection would actually change
+ // AND
+ // 2.1. left button was pressed
+ // OR
+ // 2.2. right button was pressed and the element to-be-selected
+ // is NOT a part of the current selection
+ if (!Collections.singleton(selectedPick).equals(currentSelection)
+ && (isLeft || (isRight && !currentSelection.contains(selectedPick)))) {
+ selection.setSelection(selectionId, selectedPick);
+ // Stop propagation
+ result = true;
+ }
+
+ if (isRight && pickables.size() == 1 && noModifiers) {
+ setHint(DiagramHints.SHOW_POPUP_MENU, me.controlPosition);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * A heuristic needed for implementing right-click diagram selection in a
+ * sensible manner.
+ *
+ * @param currentSelection
+ * @param pickables
+ * @return
+ */
+ private IElement singleElementAboveNonselectedConnections(Set currentSelection, List pickables) {
+ if (pickables.isEmpty())
+ return null;
+
+ // Check that the pickable-list doesn't contain anything that is in the current selection.
+ if (!Collections.disjoint(currentSelection, pickables))
+ return null;
+
+ IElement top = pickables.get(pickables.size() - 1);
+ boolean elementOnTop = !PickRequest.PickFilter.FILTER_CONNECTIONS.accept(top);
+ if (!elementOnTop)
+ return null;
+ for (int i = pickables.size() - 2; i >= 0; --i) {
+ IElement e = pickables.get(i);
+ if (!PickRequest.PickFilter.FILTER_CONNECTIONS.accept(e))
+ return null;
+ }
+ return top;
+ }
+
+ /**
+ * Since there's seems to be no better method available for finding out if
+ * the SWT popup menu was just recently closed or not, we use the following
+ * heuristic:
+ *
+ * SWT popup was just closed if it was closed < 300ms ago.
+ *
+ * Note that this is a very bad heuristic and may fail on slower machines or
+ * under heavy system load.
+ *
+ * @return
+ */
+ private boolean wasPopupJustClosed(Event event) {
+ Long popupCloseTime = getHint(DiagramHints.POPUP_MENU_HIDDEN);
+ if (popupCloseTime != null) {
+ long timeDiff = event.time - popupCloseTime;
+ //System.out.println("time diff: " + timeDiff);
+ if (timeDiff < 300) {
+ //System.out.println("POPUP WAS JUST CLOSED!");
+ return true;
+ }
+ }
+ //System.out.println("Popup has been closed for a while.");
+ return false;
+ }
+
+ boolean handleDoubleClick(MouseClickEvent me) {
+ //System.out.println("mouse double clicked: " + me);
+ if (!hasDoubleClickEdit()) return false;
+ if (me.button != MouseEvent.LEFT_BUTTON) return false;
+ if (getToolMode() != Hints.POINTERTOOL) return false;
+ if (me.clickCount < 2) return false;
+
+ // Pick Terminal
+ double pickDist = getPickDistance();
+ Rectangle2D controlPickRect = new Rectangle2D.Double(me.controlPosition.getX()-pickDist, me.controlPosition.getY()-pickDist, pickDist*2+1, pickDist*2+1);
+ Shape canvasPickRect = GeometryUtils.transformShape(controlPickRect, util.getInverseTransform());
+ int selectionId = me.mouseId;
+
+ PickRequest req = new PickRequest(canvasPickRect).context(getContext());
+ req.pickPolicy = PickPolicy.PICK_INTERSECTING_OBJECTS;
+ List pick = new ArrayList();
+ pickContext.pick(diagram, req, pick);
+
+ // Clear selection
+ if (pick.isEmpty()) {
+ selection.clear(selectionId);
+ return false;
+ }
+
+ IElement selectedPick = rotatingPick(selectionId, pick);
+
+ if (!selection.contains(selectionId, selectedPick)) {
+ selection.setSelection(selectionId, selectedPick);
+ }
+
+ CanvasUtils.sendCommand(getContext(), Commands.RENAME);
+
+ return false;
+ }
+
+ // Values shared by #handleDrag and #handleBoxSelect
+ private transient Point2D curCanvasDragPos = new Point2D.Double();
+ private transient Set elementsToDrag = Collections.emptySet();
+
+ /**
+ * Invoked before scene graph event handling and {@link #handleBoxSelect(MouseDragBegin)}.
+ * @param me
+ * @return
+ * @see #handleBoxSelect(MouseDragBegin)
+ */
+ @EventHandler(priority = TOOL_PRIORITY)
+ public boolean handleDrag(MouseDragBegin me) {
+ if (!hasElementDrag() && !hasBoxSelect()) return false;
+ if (me.button != MouseEvent.LEFT_BUTTON) return false;
+ if (getToolMode() != Hints.POINTERTOOL) return false;
+ if (hasToolMode(me.mouseId)) return false;
+
+ boolean anyModifierPressed = me.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK);
+ boolean nonSelectionModifierPressed = me.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK
+ ^ (MouseEvent.SHIFT_MASK /*| MouseEvent.CTRL_MASK*/));
+
+ if (nonSelectionModifierPressed)
+ return false;
+
+ assertDependencies();
+
+ Point2D curCanvasPos = util.controlToCanvas(me.controlPosition, curCanvasDragPos);
+ PickRequest req = new PickRequest(me.startCanvasPos).context(getContext());
+ req.pickPolicy = PickRequest.PickPolicy.PICK_INTERSECTING_OBJECTS;
+ List picks = new ArrayList();
+ pickContext.pick(diagram, req, picks);
+
+ //System.out.println(picks);
+ if (picks.isEmpty()) {
+ // Widen the area of searching if nothing is found with point picking
+ double pickDist = getPickDistance();
+ Rectangle2D controlPickRect = new Rectangle2D.Double(me.controlPosition.getX()-pickDist, me.controlPosition.getY()-pickDist, pickDist*2+1, pickDist*2+1);
+ Shape canvasPickRect = GeometryUtils.transformShape(controlPickRect, util.getInverseTransform());
+ req = new PickRequest(canvasPickRect).context(getContext());
+ req.pickPolicy = PickRequest.PickPolicy.PICK_INTERSECTING_OBJECTS;
+ pickContext.pick(diagram, req, picks);
+ //System.out.println("2nd try: " + picks);
+ }
+
+ Set sel = selection.getSelection(me.mouseId);
+ IElement topMostPick = picks.isEmpty() ? null : picks.get(picks.size() - 1);
+ Set elementsToDrag = new HashSet();
+ this.elementsToDrag = elementsToDrag;
+
+ if (!Collections.disjoint(sel, picks)) {
+ elementsToDrag.addAll(sel);
+ } else {
+ if (topMostPick != null && (sel.isEmpty() || !sel.contains(topMostPick))) {
+ selection.setSelection(me.mouseId, topMostPick);
+ sel = selection.getSelection(me.mouseId);
+ elementsToDrag.addAll(sel);
+ }
+ }
+
+ // Drag Elements
+ if (!elementsToDrag.isEmpty() && hasElementDnDDrag()) {
+ // To Be Implemented in the next Diagram data model.
+ } else {
+ if (!anyModifierPressed && !elementsToDrag.isEmpty() && hasElementDrag()) {
+ // Connections are not translatable, re-routing is in RouteGraphNode.
+ boolean onlyConnections = onlyConnections(elementsToDrag);
+ if (!onlyConnections) {
+ ICanvasParticipant tm = createTranslateTool(me.mouseId, me.startCanvasPos, curCanvasPos, elementsToDrag);
+ if (tm != null) {
+ getContext().add(tm);
+ return !onlyConnections;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Always invoked after after {@link #handleDrag(MouseDragBegin)} and scene
+ * graph event handling to prevent the box selection mode from being
+ * initiated before scene graph nodes have a chance to react.
+ *
+ *
+ * Note that this method assumes that elementsToDrag
and
+ * curCanvasPos
are already set by
+ * {@link #handleDrag(MouseDragBegin)}.
+ *
+ * @param me
+ * @return
+ */
+ @EventHandler(priority = BOX_SELECT_PRIORITY)
+ public boolean handleBoxSelect(MouseDragBegin me) {
+ if (!hasBoxSelect()) return false;
+ if (me.button != MouseEvent.LEFT_BUTTON) return false;
+ if (getToolMode() != Hints.POINTERTOOL) return false;
+ if (hasToolMode(me.mouseId)) return false;
+
+ boolean nonSelectionModifierPressed = me.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK
+ ^ (MouseEvent.SHIFT_MASK /*| MouseEvent.CTRL_MASK*/));
+ if (nonSelectionModifierPressed)
+ return false;
+
+ if (!nonSelectionModifierPressed && elementsToDrag.isEmpty()) {
+ // Box Select
+ ICanvasParticipant bsm = createBoxSelectTool(me.mouseId, me.startCanvasPos, curCanvasDragPos, me.button, boxSelectMode);
+ if (bsm != null)
+ getContext().add(bsm);
+ }
+
+ return false;
+ }
+
+ private static boolean onlyConnections(Set elements) {
+ for (IElement e : elements)
+ if (!e.getElementClass().containsClass(ConnectionHandler.class))
+ return false;
+ return true;
+ }
+
+ private IElement rotatingPick(int selectionId, List pickables) {
+ Set sel = selection.getSelection(selectionId);
+ return rotatingPick(sel, pickables);
+ }
+
+ private IElement rotatingPick(Set sel, List pickables) {
+ int earliestIndex = pickables.size();
+ for (int i = pickables.size() - 1; i >= 0; --i) {
+ if (sel.contains(pickables.get(i))) {
+ earliestIndex = i;
+ break;
+ }
+ }
+ if (earliestIndex == 0)
+ earliestIndex = pickables.size();
+ IElement selectedPick = pickables.get(earliestIndex - 1);
+ return selectedPick;
+ }
+
+ /**
+ * Is mouse in some kind of mode?
+ * @param mouseId
+ * @return
+ */
+ boolean hasToolMode(int mouseId) {
+ for (AbstractMode am : getContext().getItemsByClass(AbstractMode.class))
+ if (am.mouseId==mouseId) return true;
+ return false;
+ }
+
+ boolean hasToolMode(IToolMode mode) {
+ return ObjectUtils.objectEquals(mode, getToolMode());
+ }
+
+ boolean hasToolMode(IToolMode... modes) {
+ IToolMode current = getToolMode();
+ if (current == null)
+ return false;
+ for (IToolMode mode : modes)
+ if (current.equals(mode))
+ return true;
+ return false;
+ }
+
+ protected IToolMode getToolMode() {
+ return getHint(Hints.KEY_TOOL);
+ }
+
+ /// is box select enabled
+ protected boolean hasBoxSelect() {
+ return boxSelect;
+ }
+
+ /// is click select enabled
+ protected boolean hasClickSelect() {
+ return clickSelect;
+ }
+
+ /// is double click edit enabled
+ protected boolean hasDoubleClickEdit() {
+ return doubleClickEdit;
+ }
+
+ // is element drag enabled
+ protected boolean hasElementDrag() {
+ return dragElement;
+ }
+
+ // is element drag enabled
+ protected boolean hasElementDnDDrag() {
+ return dndDragElement;
+ }
+
+ // is connect enabled
+ protected boolean connects() {
+ return connect;
+ }
+
+ public double getPickDistance() {
+ Double pickDistance = getHint(KEY_PICK_DISTANCE);
+ return pickDistance == null ? PICK_DIST : Math.max(pickDistance, 0);
+ }
+
+ public PickPolicy getBoxSelectMode() {
+ return boxSelectMode;
+ }
+
+ public void setBoxSelectMode(PickPolicy boxSelectMode) {
+ this.boxSelectMode = boxSelectMode;
+ }
+
+ public void setSelectionEnabled(boolean select) {
+ this.clickSelect = select;
+ this.boxSelect = select;
+ if(select == false) { // Clear all selections if select is disabled
+ final int[] ids = selection.getSelectionIds();
+ if(ids.length > 0) {
+ ThreadUtils.asyncExec(getContext().getThreadAccess(), new Runnable() {
+ @Override
+ public void run() {
+ for(int id : ids)
+ selection.clear(id);
+ getContext().getContentContext().setDirty();
+ }
+ });
+ }
+ }
+ }
+
+ boolean anyModifierPressed(MouseEvent e) {
+ return e.hasAnyModifier(MouseEvent.ALL_MODIFIERS_MASK);
+ }
+
+ boolean connectToolModifiersPressed(int stateMask) {
+ return (stateMask & (MouseEvent.ALT_MASK | MouseEvent.ALT_GRAPH_MASK)) != 0;
+ }
+
+ static {
+ LINE10 = new Path2D.Double();
+ LINE10.moveTo(0, 0);
+ LINE10.lineTo(10, 0);
+ LINE10.lineTo(7, -3);
+ LINE10.moveTo(10, 0);
+ LINE10.lineTo(7, 3);
+
+ LINE15 = new Path2D.Double();
+ LINE15.moveTo(0, 0);
+ LINE15.lineTo(15, 0);
+ LINE15.lineTo(12, -3);
+ LINE15.moveTo(15, 0);
+ LINE15.lineTo(12, 3);
+
+ LINE20 = new Path2D.Double();
+ LINE20.moveTo(0, 0);
+ LINE20.lineTo(20, 0);
+ LINE20.lineTo(17, -3);
+ LINE20.moveTo(20, 0);
+ LINE20.lineTo(17, 3);
+
+ }
+
+ // CUSTOMIZE
+
+ protected ICanvasParticipant createConnectTool(TerminalInfo ti, int mouseId, Point2D startCanvasPos) {
+ return null;
+ }
+
+ protected ICanvasParticipant createConnectToolWithTerminals(List tis, int mouseId, Point2D startCanvasPos) {
+ return null;
+ }
+
+ protected ICanvasParticipant createTranslateTool(int mouseId, Point2D startCanvasPos, Point2D curCanvasPos, Set elementsToDrag) {
+ return new TranslateMode(startCanvasPos, curCanvasPos, mouseId, elementsToDrag);
+ }
+
+ protected ICanvasParticipant createBoxSelectTool(int mouseId, Point2D startCanvasPos, Point2D curCanvasPos, int button, PickPolicy boxSelectMode) {
+ return new BoxSelectionMode(startCanvasPos, curCanvasPos, mouseId, button, boxSelectMode);
+ }
+
+ /**
+ * A context listener for resetting tool mode back to pointer mode after the
+ * tracked participant has been removed.
+ */
+ protected class ToolModeResetter implements IContextListener {
+ private ICanvasParticipant tracked;
+ public ToolModeResetter(ICanvasParticipant trackedParticipant) {
+ this.tracked = trackedParticipant;
+ }
+ @Override
+ public void itemAdded(IContext sender, ICanvasParticipant item) {
+ }
+ @Override
+ public void itemRemoved(IContext sender, ICanvasParticipant item) {
+ if (item == tracked) {
+ sender.removeContextListener(this);
+ if (!isRemoved() && !connectToolModifiersPressed(lastStateMask)) {
+ temporarilyEnabledConnectTool = false;
+ setHint(Hints.KEY_TOOL, Hints.POINTERTOOL);
+ }
+ }
+ }
+ }
+
+}