/*******************************************************************************
* 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
******************************************************************************/
/*
* @(#)BezierNodeHandle.java 2.1 2008-07-25
*
* 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.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import org.jhotdraw.geom.BezierPath.Node;
import org.jhotdraw.util.*;
import org.jhotdraw.undo.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import org.jhotdraw.geom.*;
import static org.jhotdraw.samples.svg.SVGAttributeKeys.*;
/**
* BezierNodeHandle.
*
*
* @author Werner Randelshofer
* @version 2.1 2008-07-25 Handle Delete and Backspace key.
* <br>2.0 2008-05-11 Handle attributes are now retrieved from
* DrawingEditor. Added keyPressed method.
* <br>1.0.1 2006-04-21 Don't change node type when right mouse button
* is down.
* <br>1.0 January 20, 2006 Created.
*/
public class BezierNodeHandle extends AbstractHandle {
protected int index;
private CompositeEdit edit;
private BezierPath.Node oldNode;
private Figure transformOwner;
/** Creates a new instance. */
public BezierNodeHandle(BezierFigure owner, int index) {
this(owner, index, owner);
}
public BezierNodeHandle(BezierFigure owner, int index, Figure transformOwner) {
super(owner);
this.index = index;
this.transformOwner = transformOwner;
transformOwner.addFigureListener(this);
}
@Override
public void dispose() {
super.dispose();
transformOwner.removeFigureListener(this);
transformOwner = null;
}
/**
* Draws this handle.
*/
@Override
public void draw(Graphics2D g) {
BezierFigure f = getOwner();
int size = f.getNodeCount();
boolean isClosed = f.isClosed();
Color fillColor;
Color strokeColor;
if (getEditor().getTool().supportsHandleInteraction()) {
fillColor = (Color) getEditor().getHandleAttribute(HandleAttributeKeys.BEZIER_NODE_HANDLE_FILL_COLOR);
strokeColor = (Color) getEditor().getHandleAttribute(HandleAttributeKeys.BEZIER_NODE_HANDLE_STROKE_COLOR);
} else {
fillColor = (Color) getEditor().getHandleAttribute(HandleAttributeKeys.BEZIER_NODE_HANDLE_FILL_COLOR_DISABLED);
strokeColor = (Color) getEditor().getHandleAttribute(HandleAttributeKeys.BEZIER_NODE_HANDLE_STROKE_COLOR_DISABLED);
}
if (size > index) {
BezierPath.Node v = f.getNode(index);
if (v.mask == 0 ||
index == 0 && v.mask == BezierPath.C1_MASK && !isClosed ||
index == size - 1 && v.mask == BezierPath.C2_MASK && !isClosed) {
drawRectangle(g, fillColor, strokeColor);
} else if (v.mask == BezierPath.C1_MASK ||
v.mask == BezierPath.C2_MASK ||
index == 0 && !isClosed ||
index == size - 1 && !isClosed) {
drawDiamond(g, fillColor, strokeColor);
} else {
drawCircle(g, fillColor, strokeColor);
}
}
}
@Override
public BezierFigure getOwner() {
return (BezierFigure) super.getOwner();
}
protected Point getLocation() {
if (getOwner().getNodeCount() > index) {
Point2D.Double p = getOwner().getPoint(index, 0);
if (TRANSFORM.get(getTransformOwner()) != null) {
TRANSFORM.get(getTransformOwner()).transform(p, p);
}
return view.drawingToView(p);
} else {
return new Point(10, 10);
}
}
protected BezierPath.Node getBezierNode() {
return getOwner().getNodeCount() > index ? getOwner().getNode(index) : null;
}
protected Rectangle basicGetBounds() {
Rectangle r = new Rectangle(getLocation());
int h = getHandlesize();
r.x -= h / 2;
r.y -= h / 2;
r.width = r.height = h;
return r;
}
protected Figure getTransformOwner() {
return transformOwner;
}
public void trackStart(Point anchor, int modifiersEx) {
BezierFigure figure = getOwner();
view.getDrawing().fireUndoableEditHappened(edit = new CompositeEdit("Punkt verschieben"));
Point2D.Double location = view.getConstrainer().constrainPoint(view.viewToDrawing(getLocation()));
Point2D.Double p = view.getConstrainer().constrainPoint(view.viewToDrawing(anchor));
oldNode = figure.getNode(index);
fireHandleRequestSecondaryHandles();
}
public void trackStep(Point anchor, Point lead, int modifiersEx) {
BezierFigure figure = getOwner();
figure.willChange();
Point2D.Double p = view.getConstrainer().constrainPoint(view.viewToDrawing(lead));
if (TRANSFORM.get(getTransformOwner()) != null) {
try {
TRANSFORM.get(getTransformOwner()).inverseTransform(p, p);
} catch (NoninvertibleTransformException ex) {
ex.printStackTrace();
}
}
BezierPath.Node n = figure.getNode(index);
//fireAreaInvalidated(n);
n.moveTo(p);
//fireAreaInvalidated(n);
figure.setNode(index, n);
figure.changed();
}
private void fireAreaInvalidated(BezierPath.Node v) {
Rectangle2D.Double dr = new Rectangle2D.Double(v.x[0], v.y[0], 0, 0);
for (int i = 1; i < 3; i++) {
dr.add(v.x[i], v.y[i]);
}
Rectangle vr = view.drawingToView(dr);
vr.grow(getHandlesize(), getHandlesize());
fireAreaInvalidated(vr);
}
public void trackEnd(Point anchor, Point lead, int modifiersEx) {
final BezierFigure f = getOwner();
BezierPath.Node oldValue = (BezierPath.Node) oldNode.clone();;
BezierPath.Node newValue = f.getNode(index);
// Change node type
if ((modifiersEx & (InputEvent.META_DOWN_MASK | InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)) != 0 &&
(modifiersEx & InputEvent.BUTTON2_MASK) == 0) {
f.willChange();
if (index > 0 && index < f.getNodeCount() || f.isClosed()) {
newValue.mask = (newValue.mask + 3) % 4;
} else if (index == 0) {
newValue.mask = ((newValue.mask & BezierPath.C2_MASK) == 0) ? BezierPath.C2_MASK : 0;
} else {
newValue.mask = ((newValue.mask & BezierPath.C1_MASK) == 0) ? BezierPath.C1_MASK : 0;
}
f.setNode(index, newValue);
f.changed();
fireHandleRequestSecondaryHandles();
}
view.getDrawing().fireUndoableEditHappened(new BezierNodeEdit(f, index, oldValue, newValue) {
@Override
public void redo() throws CannotRedoException {
super.redo();
fireHandleRequestSecondaryHandles();
}
@Override
public void undo() throws CannotUndoException {
super.undo();
fireHandleRequestSecondaryHandles();
}
});
view.getDrawing().fireUndoableEditHappened(edit);
}
@Override
public boolean isCombinableWith(Handle h) {
/*
if (super.isCombinableWith(h)) {
BezierNodeHandle that = (BezierNodeHandle) h;
return that.index == this.index &&
that.getOwner().getNodeCount() ==
this.getOwner().getNodeCount();
}*/
return false;
}
@Override
public void trackDoubleClick(Point p, int modifiersEx) {
final BezierFigure f = getOwner();
if (f.getNodeCount() > 2 &&
(modifiersEx &
(InputEvent.META_DOWN_MASK | InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK)) == 0) {
Rectangle invalidatedArea = getDrawingArea();
f.willChange();
final BezierPath.Node removedNode = f.removeNode(index);
f.changed();
fireHandleRequestRemove(invalidatedArea);
fireUndoableEditHappened(new AbstractUndoableEdit() {
@Override
public String getPresentationName() {
ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels");
return labels.getString("edit.bezierPath.joinSegments.text");
}
@Override
public void redo() throws CannotRedoException {
super.redo();
view.removeFromSelection(f);
f.willChange();
f.removeNode(index);
f.changed();
view.addToSelection(f);
}
@Override
public void undo() throws CannotUndoException {
super.undo();
view.removeFromSelection(f);
f.willChange();
f.addNode(index, removedNode);
f.changed();
view.addToSelection(f);
}
});
}
}
@Override
public Collection<Handle> createSecondaryHandles() {
BezierFigure f = getOwner();
LinkedList<Handle> list = new LinkedList<Handle>();
BezierPath.Node v = f.getNode(index);
if ((v.mask & BezierPath.C1_MASK) != 0 &&
(index != 0 || f.isClosed())) {
list.add(new BezierControlPointHandle(f, index, 1, getTransformOwner()));
}
if ((v.mask & BezierPath.C2_MASK) != 0 &&
(index < f.getNodeCount() - 1 ||
f.isClosed())) {
list.add(new BezierControlPointHandle(f, index, 2, getTransformOwner()));
}
if (index > 0 || f.isClosed()) {
int i = (index == 0) ? f.getNodeCount() - 1 : index - 1;
v = f.getNode(i);
if ((v.mask & BezierPath.C2_MASK) != 0) {
list.add(new BezierControlPointHandle(f, i, 2, getTransformOwner()));
}
}
if (index < f.getNodeCount() - 1 || f.isClosed()) {
int i = (index == f.getNodeCount() - 1) ? 0 : index + 1;
v = f.getNode(i);
if ((v.mask & BezierPath.C1_MASK) != 0) {
list.add(new BezierControlPointHandle(f, i, 1, getTransformOwner()));
}
}
return list;
}
@Override
public String getToolTipText(Point p) {
ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels");
BezierPath.Node node = getBezierNode();
return (node == null) ? null : labels.getFormatted("handle.bezierNode.toolTipText",
labels.getFormatted(
(node.getMask() == 0) ? "handle.bezierNode.linear.value" : ((node.getMask() == BezierPath.C1C2_MASK) ? "handle.bezierNode.cubic.value" : "handle.bezierNode.quadratic.value")));
}
@Override
public void keyPressed(KeyEvent evt) {
final BezierFigure f = getOwner();
oldNode = f.getNode(index);
switch (evt.getKeyCode()) {
case KeyEvent.VK_UP:
f.willChange();
f.setPoint(index, new Point2D.Double(oldNode.x[0], oldNode.y[0] - 1d));
f.changed();
view.getDrawing().fireUndoableEditHappened(new BezierNodeEdit(f, index, oldNode, f.getNode(index)));
evt.consume();
break;
case KeyEvent.VK_DOWN:
f.willChange();
f.setPoint(index, new Point2D.Double(oldNode.x[0], oldNode.y[0] + 1d));
f.changed();
view.getDrawing().fireUndoableEditHappened(new BezierNodeEdit(f, index, oldNode, f.getNode(index)));
evt.consume();
break;
case KeyEvent.VK_LEFT:
f.willChange();
f.setPoint(index, new Point2D.Double(oldNode.x[0] - 1d, oldNode.y[0]));
f.changed();
view.getDrawing().fireUndoableEditHappened(new BezierNodeEdit(f, index, oldNode, f.getNode(index)));
evt.consume();
break;
case KeyEvent.VK_RIGHT:
f.willChange();
f.setPoint(index, new Point2D.Double(oldNode.x[0] + 1d, oldNode.y[0]));
f.changed();
view.getDrawing().fireUndoableEditHappened(new BezierNodeEdit(f, index, oldNode, f.getNode(index)));
evt.consume();
break;
case KeyEvent.VK_DELETE:
case KeyEvent.VK_BACK_SPACE:
Rectangle invalidatedArea = getDrawingArea();
f.willChange();
final BezierPath.Node removedNode = f.removeNode(index);
f.changed();
fireHandleRequestRemove(invalidatedArea);
fireUndoableEditHappened(new AbstractUndoableEdit() {
@Override
public String getPresentationName() {
ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels");
return labels.getString("edit.bezierPath.joinSegment.text");
}
@Override
public void redo() throws CannotRedoException {
super.redo();
view.removeFromSelection(f);
f.willChange();
f.removeNode(index);
f.changed();
view.addToSelection(f);
}
@Override
public void undo() throws CannotUndoException {
super.undo();
view.removeFromSelection(f);
f.willChange();
f.addNode(index, removedNode);
f.changed();
view.addToSelection(f);
}
});
evt.consume();
break;
}
}
}