/******************************************************************************* * Copyright (c) 2006-2012 * Software Technology Group, Dresden University of Technology * DevBoost GmbH, Berlin, Amtsgericht Charlottenburg, HRB 140026 * * 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: * Software Technology Group - TU Dresden, Germany; * DevBoost GmbH - Berlin, Germany * - initial API and implementation ******************************************************************************/ /* * @(#)BezierTool.java 2.0.2 2009-03-15 * * Copyright (c) 1996-2009 by the original authors of JHotDraw * and all its contributors. * All rights reserved. * * The copyright of this software is owned by the authors and * contributors of the JHotDraw project ("the copyright holders"). * You may not use, copy or modify this software, except in * accordance with the license agreement you entered into with * the copyright holders. For details see accompanying license terms. */ package org.jhotdraw.draw; import javax.swing.undo.*; import org.jhotdraw.util.*; import java.awt.*; import java.awt.geom.*; import java.awt.event.*; import java.util.*; import org.jhotdraw.geom.*; /** * Tool to scribble a BezierFigure * * @author Werner Randelshofer * @version 2.0.2 2009-03-15 Finish creation if the user switches to another * view. * <br>2.0.1 2008-06-08 FittedCurve did not include the point which * was digitized on mouse pressed. * <br>2.0 2008-05-17 Added support for property toolDoneAfterCreation. * Draw rubberband while editing. * <br>1.2 2007-11-30 Huw Jones: Factored calls to Bezier.fitBezierPath out * into method calculateFittedPath. * <br>1.1 2006-07-12 Werner Randelshofer: Extended support for subclassing. * <br>1.0 2006-01-21 Werner Randelshofer: Created. */ public class BezierTool extends AbstractTool { /** * Set this to true to turn on debugging output on System.out. */ private final static boolean DEBUG = false; private Boolean finishWhenMouseReleased; protected Map<AttributeKey, Object> attributes; private boolean isToolDoneAfterCreation; /** * The prototype for new figures. */ private BezierFigure prototype; /** * The created figure. */ protected BezierFigure createdFigure; private int nodeCountBeforeDrag; /** * A localized name for this tool. The presentationName is displayed by the * UndoableEdit. */ private String presentationName; private Point mouseLocation; /** Holds the view on which we are currently creating a figure. */ private DrawingView creationView; /** Creates a new instance. */ public BezierTool(BezierFigure prototype) { this(prototype, null); } /** Creates a new instance. */ public BezierTool(BezierFigure prototype, Map<AttributeKey, Object> attributes) { this(prototype, attributes, null); } public BezierTool(BezierFigure prototype, Map<AttributeKey, Object> attributes, String name) { this.prototype = prototype; this.attributes = attributes; if (name == null) { ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels"); name = labels.getString("edit.createFigure.text"); } this.presentationName = name; } public String getPresentationName() { return presentationName; } @Override public void activate(DrawingEditor editor) { super.activate(editor); getView().setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); } @Override public void deactivate(DrawingEditor editor) { super.deactivate(editor); getView().setCursor(Cursor.getDefaultCursor()); if (createdFigure != null) { if (anchor != null && mouseLocation != null) { Rectangle r = new Rectangle(anchor); r.add(mouseLocation); if (createdFigure.getNodeCount() > 0 && createdFigure.isClosed()) { r.add(getView().drawingToView(createdFigure.getStartPoint())); } fireAreaInvalidated(r); } finishCreation(createdFigure, creationView); createdFigure = null; } } @Override public void mousePressed(MouseEvent evt) { if (DEBUG) { System.out.println("BezierTool.mousePressed " + evt); } if (mouseLocation != null) { Rectangle r = new Rectangle(mouseLocation); r.add(evt.getPoint()); r.grow(1, 1); fireAreaInvalidated(r); } mouseLocation = evt.getPoint(); super.mousePressed(evt); if (createdFigure != null && creationView != getView()) { finishCreation(createdFigure, creationView); createdFigure = null; } if (createdFigure == null) { creationView = getView(); creationView.clearSelection(); finishWhenMouseReleased = null; createdFigure = createFigure(); createdFigure.addNode(new BezierPath.Node( creationView.getConstrainer().constrainPoint( creationView.viewToDrawing(anchor)))); getDrawing().add(createdFigure); } else { if (evt.getClickCount() == 1) { addPointToFigure(creationView.getConstrainer().constrainPoint( creationView.viewToDrawing(anchor))); } } nodeCountBeforeDrag = createdFigure.getNodeCount(); } @SuppressWarnings("unchecked") protected BezierFigure createFigure() { BezierFigure f = (BezierFigure) prototype.clone(); getEditor().applyDefaultAttributesTo(f); if (attributes != null) { for (Map.Entry<AttributeKey, Object> entry : attributes.entrySet()) { entry.getKey().basicSet(f, entry.getValue()); } } return f; } protected Figure getCreatedFigure() { return createdFigure; } protected Figure getAddedFigure() { return createdFigure; } protected void addPointToFigure(Point2D.Double newPoint) { int pointCount = createdFigure.getNodeCount(); createdFigure.willChange(); if (pointCount < 2) { createdFigure.addNode(new BezierPath.Node(newPoint)); } else { Point2D.Double endPoint = createdFigure.getEndPoint(); Point2D.Double secondLastPoint = (pointCount <= 1) ? endPoint : createdFigure.getPoint(pointCount - 2, 0); if (newPoint.equals(endPoint)) { // nothing to do } else if (pointCount > 1 && Geom.lineContainsPoint(newPoint.x, newPoint.y, secondLastPoint.x, secondLastPoint.y, endPoint.x, endPoint.y, 0.9f / getView().getScaleFactor())) { createdFigure.setPoint(pointCount - 1, 0, newPoint); } else { createdFigure.addNode(new BezierPath.Node(newPoint)); } } createdFigure.changed(); } @Override public void mouseClicked(MouseEvent evt) { if (createdFigure != null) { switch (evt.getClickCount()) { case 1: if (createdFigure.getNodeCount() > 2) { Rectangle r = new Rectangle(getView().drawingToView(createdFigure.getStartPoint())); r.grow(2, 2); if (r.contains(evt.getX(), evt.getY())) { createdFigure.setClosed(true); finishCreation(createdFigure, creationView); createdFigure = null; if (isToolDoneAfterCreation) { fireToolDone(); } } } break; case 2: finishWhenMouseReleased = null; finishCreation(createdFigure, creationView); createdFigure = null; break; } } } protected void fireUndoEvent(Figure createdFigure, DrawingView creationView) { final Figure addedFigure = createdFigure; final Drawing addedDrawing = creationView.getDrawing(); final DrawingView addedView = creationView; getDrawing().fireUndoableEditHappened(new AbstractUndoableEdit() { public String getPresentationName() { return presentationName; } public void undo() throws CannotUndoException { super.undo(); addedDrawing.remove(addedFigure); } public void redo() throws CannotRedoException { super.redo(); addedView.clearSelection(); addedDrawing.add(addedFigure); addedView.addToSelection(addedFigure); } }); } public void mouseReleased(MouseEvent evt) { if (DEBUG) { System.out.println("BezierTool.mouseReleased " + evt); } isWorking = false; if (createdFigure.getNodeCount() > nodeCountBeforeDrag + 1) { createdFigure.willChange(); BezierPath figurePath = createdFigure.getBezierPath(); BezierPath digitizedPath = new BezierPath(); for (int i = nodeCountBeforeDrag - 1, n = figurePath.size(); i < n; i++) { digitizedPath.add(figurePath.get(nodeCountBeforeDrag - 1)); figurePath.remove(nodeCountBeforeDrag - 1); } BezierPath fittedPath = calculateFittedCurve(digitizedPath); //figurePath.addAll(digitizedPath); figurePath.addAll(fittedPath); createdFigure.setBezierPath(figurePath); createdFigure.changed(); nodeCountBeforeDrag = createdFigure.getNodeCount(); } if (finishWhenMouseReleased == Boolean.TRUE) { if (createdFigure.getNodeCount() > 1) { finishCreation(createdFigure, creationView); createdFigure = null; finishWhenMouseReleased = null; return; } } else if (finishWhenMouseReleased == null) { finishWhenMouseReleased = Boolean.FALSE; } // repaint dotted line Rectangle r = new Rectangle(anchor); r.add(mouseLocation); r.add(evt.getPoint()); r.grow(1, 1); fireAreaInvalidated(r); anchor.x = evt.getX(); anchor.y = evt.getY(); mouseLocation = evt.getPoint(); } protected void finishCreation(BezierFigure createdFigure, DrawingView creationView) { fireUndoEvent(createdFigure, creationView); creationView.addToSelection(createdFigure); if (isToolDoneAfterCreation) { fireToolDone(); } } public void mouseDragged(MouseEvent evt) { if (finishWhenMouseReleased == null) { finishWhenMouseReleased = Boolean.TRUE; } int x = evt.getX(); int y = evt.getY(); addPointToFigure(getView().viewToDrawing(new Point(x, y))); } @Override public void draw(Graphics2D g) { if (createdFigure != null && // anchor != null && // mouseLocation != null &&// getView() == creationView) { g.setColor(Color.BLACK); g.setStroke(new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0f, new float[]{1f, 5f}, 0f)); g.drawLine(anchor.x, anchor.y, mouseLocation.x, mouseLocation.y); if (!isWorking && createdFigure.isClosed() && createdFigure.getNodeCount() > 1) { Point p = creationView.drawingToView(createdFigure.getStartPoint()); g.drawLine(mouseLocation.x, mouseLocation.y, p.x, p.y); } } } @Override public void mouseMoved(MouseEvent evt) { if (createdFigure != null && anchor != null && mouseLocation != null) { if (evt.getSource() == creationView) { Rectangle r = new Rectangle(anchor); r.add(mouseLocation); r.add(evt.getPoint()); if (createdFigure.isClosed() && createdFigure.getNodeCount() > 0) { r.add(creationView.drawingToView(createdFigure.getStartPoint())); } r.grow(1, 1); fireAreaInvalidated(r); mouseLocation = evt.getPoint(); } } } protected BezierPath calculateFittedCurve(BezierPath path) { return Bezier.fitBezierPath(path, 1.5d / getView().getScaleFactor()); } public void setToolDoneAfterCreation(boolean b) { isToolDoneAfterCreation = b; } public boolean isToolDoneAfterCreation() { return isToolDoneAfterCreation; } }