/******************************************************************************* * 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 ******************************************************************************/ /* * @(#)CreationTool.java 2.5 2008-05-24 * * Copyright (c) 1996-2008 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 java.awt.*; import java.awt.geom.*; import java.awt.event.*; import java.util.*; import org.jhotdraw.util.*; /** * A tool to create new figures. The figure to be created is specified by a * prototype. * <p> * To create a figure using the CreationTool, the user does the following mouse * gestures on a DrawingView: * <ol> * <li>Press the mouse button over the DrawingView. This defines the * start point of the Figure bounds.</li> * <li>Drag the mouse while keeping the mouse button pressed, and then release * the mouse button. This defines the end point of the Figure bounds.</li> * </ol> * The CreationTool works well with most figures that fit into a rectangular * shape or that concist of a single straight line. For figures that need * additional editing after these mouse gestures, the use of a specialized * creation tool is recommended. For example the TextTool allows to enter the * text into a TextFigure after the user has performed the mouse gestures. * <p> * Alltough the mouse gestures might be fitting for the creation of a connection, * the CreationTool is not suited for the creation of a ConnectionFigure. Use * the ConnectionTool for this type of figures instead. * <p> * Design pattern:<br> * Name: Prototype.<br> * Role: Client.<br> * Partners: {@link Figure} as Prototype. * * @author Werner Randelshofer * @version 2.4 2008-05-24 Made all private variables protected. Use crosshair * cursor for creation tool. * <br>2.2 2007-08-22 Added property 'toolDoneAfterCreation'. * <br>2.1.1 2006-07-20 Minimal size treshold was enforced too eagerly. * <br>2.1 2006-07-15 Changed to create prototype creation from class presentationName. * <br>2.0 2006-01-14 Changed to support double precision coordinates. * <br>1.0 2003-12-01 Derived from JHotDraw 5.4b1. */ public class CreationTool extends AbstractTool { /** * Attributes to be applied to the created ConnectionFigure. * These attributes override the default attributes of the * DrawingEditor. */ protected Map<AttributeKey, Object> prototypeAttributes; /** * A localized name for this tool. The presentationName is displayed by the * UndoableEdit. */ protected String presentationName; /** * Treshold for which we create a larger shape of a minimal size. */ protected Dimension minimalSizeTreshold = new Dimension(2, 2); /** * We set the figure to this minimal size, if it is smaller than the * minimal size treshold. */ protected Dimension minimalSize = new Dimension(40, 40); /** * The prototype for new figures. */ protected Figure prototype; /** * The created figure. */ protected Figure createdFigure; /** * If this is set to false, the CreationTool does not fire toolDone * after a new Figure has been created. This allows to create multiple * figures consecutively. */ private boolean isToolDoneAfterCreation = true; /** Creates a new instance. */ public CreationTool(String prototypeClassName) { this(prototypeClassName, null, null); } public CreationTool(String prototypeClassName, Map<AttributeKey, Object> attributes) { this(prototypeClassName, attributes, null); } public CreationTool(String prototypeClassName, Map<AttributeKey, Object> attributes, String name) { try { this.prototype = (Figure) Class.forName(prototypeClassName).newInstance(); } catch (Exception e) { InternalError error = new InternalError("Unable to create Figure from " + prototypeClassName); error.initCause(e); throw error; } this.prototypeAttributes = attributes; if (name == null) { ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels"); name = labels.getString("edit.createFigure.text"); } this.presentationName = name; } /** Creates a new instance with the specified prototype but without an * attribute set. The CreationTool clones this prototype each time a new * Figure needs to be created. When a new Figure is created, the * CreationTool applies the default attributes from the DrawingEditor to it. * * @param prototype The prototype used to create a new Figure. */ public CreationTool(Figure prototype) { this(prototype, null, null); } /** Creates a new instance with the specified prototype but without an * attribute set. The CreationTool clones this prototype each time a new * Figure needs to be created. When a new Figure is created, the * CreationTool applies the default attributes from the DrawingEditor to it, * and then it applies the attributes to it, that have been supplied in * this constructor. * * @param prototype The prototype used to create a new Figure. * @param attributes The CreationTool applies these attributes to the * prototype after having applied the default attributes from the DrawingEditor. */ public CreationTool(Figure prototype, Map<AttributeKey, Object> attributes) { this(prototype, attributes, null); } /** * Creates a new instance with the specified prototype and attribute set. * * @param prototype The prototype used to create a new Figure. * @param attributes The CreationTool applies these attributes to the * prototype after having applied the default attributes from the DrawingEditor. * @param name The name parameter is currently not used. * @deprecated This constructor might go away, because the name parameter * is not used. */ public CreationTool(Figure prototype, Map<AttributeKey, Object> attributes, String name) { this.prototype = prototype; this.prototypeAttributes = attributes; if (name == null) { ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels"); name = labels.getString("edit.createFigure.text"); } this.presentationName = name; } public Figure getPrototype() { return prototype; } @Override public void activate(DrawingEditor editor) { super.activate(editor); //getView().clearSelection(); getView().setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); } @Override public void deactivate(DrawingEditor editor) { super.deactivate(editor); if (getView() != null) { getView().setCursor(Cursor.getDefaultCursor()); } if (createdFigure != null) { if (createdFigure instanceof CompositeFigure) { ((CompositeFigure) createdFigure).layout(); } createdFigure = null; } } public void mousePressed(MouseEvent evt) { super.mousePressed(evt); getView().clearSelection(); createdFigure = createFigure(); Point2D.Double p = constrainPoint(viewToDrawing(anchor)); anchor.x = evt.getX(); anchor.y = evt.getY(); createdFigure.setBounds(p, p); getDrawing().add(createdFigure); } public void mouseDragged(MouseEvent evt) { if (createdFigure != null) { Point2D.Double p = constrainPoint(new Point(evt.getX(), evt.getY())); createdFigure.willChange(); createdFigure.setBounds( constrainPoint(new Point(anchor.x, anchor.y)), p); createdFigure.changed(); } } public void mouseReleased(MouseEvent evt) { if (createdFigure != null) { Rectangle2D.Double bounds = createdFigure.getBounds(); if (bounds.width == 0 && bounds.height == 0) { getDrawing().remove(createdFigure); if (isToolDoneAfterCreation()) { fireToolDone(); } } else { if (Math.abs(anchor.x - evt.getX()) < minimalSizeTreshold.width && Math.abs(anchor.y - evt.getY()) < minimalSizeTreshold.height) { createdFigure.willChange(); createdFigure.setBounds( constrainPoint(new Point(anchor.x, anchor.y)), constrainPoint(new Point( anchor.x + (int) Math.max(bounds.width, minimalSize.width), anchor.y + (int) Math.max(bounds.height, minimalSize.height)))); createdFigure.changed(); } if (createdFigure instanceof CompositeFigure) { ((CompositeFigure) createdFigure).layout(); } final Figure addedFigure = createdFigure; final Drawing addedDrawing = getDrawing(); 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(); addedDrawing.add(addedFigure); } }); creationFinished(createdFigure); createdFigure = null; } } else { if (isToolDoneAfterCreation()) { fireToolDone(); } } } @SuppressWarnings("unchecked") protected Figure createFigure() { Figure f = (Figure) prototype.clone(); getEditor().applyDefaultAttributesTo(f); if (prototypeAttributes != null) { for (Map.Entry<AttributeKey, Object> entry : prototypeAttributes.entrySet()) { entry.getKey().basicSet(f, entry.getValue()); } } return f; } protected Figure getCreatedFigure() { return createdFigure; } protected Figure getAddedFigure() { return createdFigure; } /** * This method allows subclasses to do perform additonal user interactions * after the new figure has been created. * The implementation of this class just invokes fireToolDone. */ protected void creationFinished(Figure createdFigure) { if (createdFigure.isSelectable()) { getView().addToSelection(createdFigure); } if (isToolDoneAfterCreation()) { fireToolDone(); } } /** * If this is set to false, the CreationTool does not fire toolDone * after a new Figure has been created. This allows to create multiple * figures consecutively. */ public void setToolDoneAfterCreation(boolean newValue) { boolean oldValue = isToolDoneAfterCreation; isToolDoneAfterCreation = newValue; } /** * Returns true, if this tool fires toolDone immediately after a new * figure has been created. */ public boolean isToolDoneAfterCreation() { return isToolDoneAfterCreation; } @Override public void updateCursor(DrawingView view, Point p) { if (view.isEnabled()) { view.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); } else { view.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); } } }