--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.g2d.participant;\r
+\r
+import java.awt.BasicStroke;\r
+import java.awt.Color;\r
+import java.awt.Cursor;\r
+import java.awt.Shape;\r
+import java.awt.geom.Path2D;\r
+import java.awt.geom.Point2D;\r
+import java.awt.geom.Rectangle2D;\r
+\r
+import org.simantics.g2d.canvas.ICanvasContext;\r
+import org.simantics.g2d.canvas.IMouseCursorContext;\r
+import org.simantics.g2d.canvas.IMouseCursorHandle;\r
+import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;\r
+import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;\r
+import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup;\r
+import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;\r
+import org.simantics.g2d.diagram.DiagramHints;\r
+import org.simantics.g2d.diagram.handler.PickContext;\r
+import org.simantics.g2d.diagram.participant.Selection;\r
+import org.simantics.g2d.diagram.participant.pointertool.AbstractMode;\r
+import org.simantics.g2d.participant.MouseUtil.MouseInfo;\r
+import org.simantics.scenegraph.g2d.G2DParentNode;\r
+import org.simantics.scenegraph.g2d.events.Event;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent;\r
+import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonEvent;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;\r
+import org.simantics.scenegraph.g2d.events.command.CommandEvent;\r
+import org.simantics.scenegraph.g2d.events.command.Commands;\r
+import org.simantics.scenegraph.g2d.nodes.ShapeNode;\r
+import org.simantics.utils.ObjectUtils;\r
+import org.simantics.utils.page.MarginUtils;\r
+\r
+/**\r
+ * ZoomToAreaHandler starts an area zoom mode when a ZOOM_TO_AREA command is\r
+ * issued.\r
+ *\r
+ * @author Tuukka Lehtonen\r
+ */\r
+public class ZoomToAreaHandler extends AbstractCanvasParticipant {\r
+\r
+ public final static Cursor ZOOM_CURSOR = new Cursor(Cursor.CROSSHAIR_CURSOR);\r
+\r
+ @EventHandler(priority = 0)\r
+ public boolean handleZoomToArea(CommandEvent event) {\r
+ if (Commands.ZOOM_TO_AREA.equals( event.command )) {\r
+ if (!getContext().containsItemByClass(ZoomToAreaMode.class)) {\r
+ getContext().add( new ZoomToAreaMode() );\r
+ setDirty();\r
+ }\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ class ZoomToAreaMode extends AbstractMode {\r
+\r
+ @Dependency TransformUtil util;\r
+ @Dependency Selection selection;\r
+ @Dependency PickContext pickContext;\r
+ @Dependency KeyUtil keyUtil;\r
+ @Dependency MouseUtil mouseUtil;\r
+ @Dependency CanvasBoundsParticipant canvasBounds;\r
+\r
+ public final BasicStroke STROKE = new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10f, new float[] {4,2}, 0);\r
+\r
+ public static final int PAINT_PRIORITY = 30;\r
+\r
+ Point2D startingPoint;\r
+ Point2D currentPoint;\r
+ IMouseCursorHandle cursor;\r
+\r
+ public ZoomToAreaMode() {\r
+ super(0);\r
+ }\r
+\r
+ @Override\r
+ public void addedToContext(ICanvasContext ctx) {\r
+ super.addedToContext(ctx);\r
+ IMouseCursorContext mctx = getContext().getMouseCursorContext();\r
+ if (mctx != null)\r
+ cursor = mctx.setCursor(getMouseId(), ZOOM_CURSOR);\r
+ MouseInfo mi = mouseUtil.getMouseInfo(getMouseId());\r
+ if (mi != null)\r
+ currentPoint = mi.canvasPosition;\r
+ }\r
+\r
+ @Override\r
+ public void removedFromContext(ICanvasContext ctx) {\r
+ if (cursor != null) {\r
+ cursor.remove();\r
+ cursor = null;\r
+ }\r
+ super.removedFromContext(ctx);\r
+ }\r
+\r
+ @EventHandler(priority = 10)\r
+ public boolean handleEvent(Event e) {\r
+ if (e instanceof CommandEvent) {\r
+ CommandEvent ce = (CommandEvent) e;\r
+ if (ce.command.equals( Commands.CANCEL )){\r
+ setDirty();\r
+ remove();\r
+ return true;\r
+ }\r
+ } else if (e instanceof MouseMovedEvent) {\r
+ MouseMovedEvent event = (MouseMovedEvent) e;\r
+ if (event.mouseId != getMouseId()) return false;\r
+ Point2D canvasPos = util.controlToCanvas(event.controlPosition, null);\r
+ if (!ObjectUtils.objectEquals(currentPoint, canvasPos))\r
+ {\r
+ currentPoint = canvasPos;\r
+ update(getBox());\r
+ setDirty();\r
+ }\r
+ return false;\r
+ } else if (e instanceof MouseButtonEvent) {\r
+ MouseButtonEvent event = (MouseButtonEvent) e;\r
+\r
+ if (event.mouseId != getMouseId()) return false;\r
+ if (event.button != MouseEvent.LEFT_BUTTON) return false;\r
+\r
+ // Eat all other mouse button events besides press and click.\r
+ // Must use mouse clicks here because otherwise selection\r
+ // participants etc. might alter selections on the next incoming\r
+ // mouse click event, which only confuses the user.\r
+ if (e instanceof MouseClickEvent) {\r
+ Point2D canvasPos = util.controlToCanvas(event.controlPosition, null);\r
+ currentPoint = canvasPos;\r
+\r
+ if (startingPoint == null) {\r
+ // Start marking the box\r
+ startingPoint = currentPoint;\r
+ setDirty();\r
+ return true;\r
+ }\r
+\r
+ Rectangle2D area = new Rectangle2D.Double();\r
+ area.setFrameFromDiagonal(startingPoint, currentPoint);\r
+\r
+ Rectangle2D controlArea = canvasBounds.getControlBounds();\r
+ util.fitArea(controlArea, area, PanZoomRotateHandler.getZoomToFitMargins(getHintStack()));\r
+\r
+ setDirty();\r
+ remove();\r
+ return true;\r
+ }\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Get selection box in control coordinates\r
+ * @return control coordinates\r
+ */\r
+ protected Rectangle2D getBox() {\r
+ if (startingPoint == null)\r
+ return null;\r
+ Point2D p1 = startingPoint;\r
+ Point2D p2 = currentPoint;\r
+ double ax = p1.getX();\r
+ double ay = p1.getY();\r
+ double bx = p2.getX();\r
+ double by = p2.getY();\r
+ if (bx < ax) {\r
+ double temp = ax;\r
+ ax = bx;\r
+ bx = temp;\r
+ }\r
+ if (by < ay) {\r
+ double temp = ay;\r
+ ay = by;\r
+ by = temp;\r
+ }\r
+ return new Rectangle2D.Double(ax, ay, bx - ax, by - ay);\r
+ }\r
+\r
+ protected Path2D getCrossHair(Rectangle2D controlBounds) {\r
+ Point2D controlPos = util.canvasToControl(currentPoint, null);\r
+ Path2D path = new Path2D.Double();\r
+ path.moveTo(controlBounds.getMinX(), controlPos.getY());\r
+ path.lineTo(controlBounds.getMaxX(), controlPos.getY());\r
+ path.moveTo(controlPos.getX(), controlBounds.getMinY());\r
+ path.lineTo(controlPos.getX(), controlBounds.getMaxY());\r
+ return path;\r
+ }\r
+\r
+ public synchronized Color getToolColor()\r
+ {\r
+ Color c = getHint(DiagramHints.KEY_SELECTION_FRAME_COLOR);\r
+ if (c!=null) return c;\r
+ return Color.BLACK;\r
+ }\r
+\r
+ protected ShapeNode node = null;\r
+\r
+ @SGInit\r
+ public void initSG(G2DParentNode parent) {\r
+ node = parent.addNode("zoom to area", ShapeNode.class);\r
+ node.setZIndex(PAINT_PRIORITY);\r
+ node.setStroke(STROKE);\r
+ node.setScaleStroke(true);\r
+ node.setColor(getToolColor());\r
+ node.setFill(false);\r
+ }\r
+\r
+ void update(Shape shape) {\r
+ if (node != null) {\r
+ node.setShape(shape);\r
+ }\r
+ }\r
+\r
+ @SGCleanup\r
+ public void cleanupSG() {\r
+ if (node != null) {\r
+ node.remove();\r
+ node = null;\r
+ }\r
+ }\r
+ }\r
+\r
+}\r