/******************************************************************************* * 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.scenegraph.g2d.nodes; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Stroke; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import org.simantics.scenegraph.g2d.G2DNode; import org.simantics.scenegraph.g2d.events.EventTypes; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent; import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent; import org.simantics.scenegraph.utils.GeometryUtils; import org.simantics.scenegraph.utils.NodeUtil; /** * @author Tuukka Lehtonen */ public class BoxSelectionNode extends G2DNode { private static final long serialVersionUID = 8508750881358776559L; protected Stroke stroke = new BasicStroke(2); protected Color color = Color.BLACK; protected boolean scaleStroke = false; protected int mouseButton = 0; protected Point2D start = new Point2D.Double(0, 0); protected Point2D end = new Point2D.Double(0, 0); protected SelectionListener listener = null; public static interface SelectionListener { public void onSelect(Rectangle2D rect, int modifiers); } @Override public void init() { super.init(); addEventHandler(this); } @Override public void cleanup() { removeEventHandler(this); super.cleanup(); } @SyncField("stroke") public void setStroke(Stroke stroke) { this.stroke = stroke; } @SyncField("color") public void setColor(Color color) { this.color = color; } @SyncField("scaleStroke") public void setScaleStroke(boolean scaleStroke) { this.scaleStroke = scaleStroke; } @SyncField("start") public void setStart(Point2D start) { this.start = start; } @SyncField("end") public void setEnd(Point2D end) { this.end = end; } @SyncField("mouseButton") public void setMouseButton(int mouseButton) { this.mouseButton = mouseButton; } @Override public void render(Graphics2D g2d) { Rectangle2D bounds = getBoundsInLocal(); if (bounds == null || bounds.isEmpty()) return; if (color != null) g2d.setColor(color); if (stroke != null) { if (scaleStroke && stroke instanceof BasicStroke) { BasicStroke bs = GeometryUtils.scaleStroke(stroke, (float) (1.0 / GeometryUtils.getScale(g2d.getTransform()))); g2d.setStroke(bs); } else { g2d.setStroke(stroke); } } g2d.draw(bounds); } public void setSelectionListener(SelectionListener listener) { this.listener = listener; } @ServerSide private void onSelect(Rectangle2D selection, int modifiers) { if (listener != null) listener.onSelect(selection, modifiers); } @Override public Rectangle2D getBoundsInLocal() { // Empty bounds indicate no rendering. if (start == null || end == null) return new Rectangle2D.Double(); Rectangle2D rect = new Rectangle2D.Double(Math.min(start.getX(), end.getX()), Math.min(start.getY(), end.getY()), Math.max(start.getX(), end.getX())-Math.min(start.getX(), end.getX()), Math.max(start.getY(), end.getY())-Math.min(start.getY(), end.getY())); return rect; } @Override public int getEventMask() { return EventTypes.MouseButtonReleasedMask | EventTypes.MouseDragBeginMask | EventTypes.MouseMovedMask; } @Override protected boolean mouseButtonReleased(MouseButtonReleasedEvent e) { if (e.button == mouseButton) { onSelect(getBoundsInLocal(), e.stateMask); start = null; end = null; } return false; } @Override protected boolean mouseMoved(MouseMovedEvent e) { if (end != null) { NodeUtil.worldToLocal(this, e.controlPosition, end); repaint(); } return false; } }