package com.kreative.paint.tool;
import java.awt.Cursor;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Shape;
import java.awt.event.KeyEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Vector;
import com.kreative.paint.datatransfer.ClipboardUtilities;
import com.kreative.paint.document.draw.ControlPoint;
import com.kreative.paint.document.draw.ControlPointType;
import com.kreative.paint.document.draw.DrawObject;
import com.kreative.paint.document.draw.DrawSurface;
import com.kreative.paint.document.draw.GroupDrawObject;
import com.kreative.paint.document.draw.ImageDrawObject;
import com.kreative.paint.document.draw.PaintSettings;
import com.kreative.paint.document.draw.PathDrawObject;
import com.kreative.paint.document.draw.ShapeDrawObject;
import com.kreative.paint.document.draw.TextDrawObject;
import com.kreative.paint.util.CursorUtils;
import com.kreative.paint.util.ImageUtils;
public class ArrowTool extends AbstractDrawSelectionTool {
private static final int K = 0xFF000000;
private static final Image icon = ToolUtilities.makeIcon(
16, 16,
new int[] {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,K,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,K,K,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,K,K,K,0,0,0,0,0,0,0,0,0,
0,0,0,0,K,K,K,K,0,0,0,0,0,0,0,0,
0,0,0,0,K,K,K,K,K,0,0,0,0,0,0,0,
0,0,0,0,K,K,K,K,K,K,0,0,0,0,0,0,
0,0,0,0,K,K,K,K,K,K,K,0,0,0,0,0,
0,0,0,0,K,K,K,K,K,K,K,K,0,0,0,0,
0,0,0,0,K,K,K,K,K,0,0,0,0,0,0,0,
0,0,0,0,K,K,0,K,K,0,0,0,0,0,0,0,
0,0,0,0,K,0,0,0,K,K,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,K,K,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,K,K,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,K,K,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
}
);
private DrawObject currentObject;
private List<ControlPoint> originalControlPoints;
private int originalCurrentControlPoint; // don't
private int currentCurrentControlPoint; // ask
private Map<DrawObject,Point2D> anchorPoints;
private boolean startedDragRect;
private TextDrawObject text;
protected Image getBWIcon() {
return icon;
}
public boolean toolSelected(ToolEvent e) {
if (text != null) {
text.setCursor(-1, -1);
if (e.isTransactionInProgress()) e.commitTransaction();
}
currentObject = null;
originalControlPoints = null;
originalCurrentControlPoint = 0;
currentCurrentControlPoint = 0;
anchorPoints = null;
startedDragRect = false;
text = null;
return false;
}
public boolean toolDeselected(ToolEvent e) {
if (text != null) {
text.setCursor(-1, -1);
if (e.isTransactionInProgress()) e.commitTransaction();
}
currentObject = null;
originalControlPoints = null;
originalCurrentControlPoint = 0;
currentCurrentControlPoint = 0;
anchorPoints = null;
startedDragRect = false;
text = null;
return false;
}
public boolean toolDoubleClicked(ToolEvent e) {
if (text != null) {
text.setCursor(-1, -1);
if (e.isTransactionInProgress()) e.commitTransaction();
}
if (e.isTransactionInProgress()) {
for (DrawObject o : e.getDrawSurface()) {
o.setSelected(true);
}
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.SelectAll"));
e.getCanvas().clearPaintSelection();
for (DrawObject o : e.getDrawSurface()) {
o.setSelected(true);
}
e.commitTransaction();
}
currentObject = null;
originalControlPoints = null;
originalCurrentControlPoint = 0;
currentCurrentControlPoint = 0;
anchorPoints = null;
startedDragRect = false;
text = null;
return false;
}
public boolean mouseMoved(ToolEvent e) {
Point2D.Float p = new Point2D.Float(e.getX(), e.getY());
if (text != null && text.contains(p)) {
setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
return false;
}
DrawSurface d = e.getDrawSurface();
ListIterator<DrawObject> oi;
oi = d.listIterator(d.size());
while (oi.hasPrevious()) {
DrawObject o = oi.previous();
if (o.isSelected()) {
for (ControlPoint c : o.getControlPoints()) {
if (c.getType() != ControlPointType.HIDDEN && c.distance(p) <= 3.0) {
setCursor(getControlPointCursor(c));
return false;
}
}
}
}
oi = d.listIterator(d.size());
while (oi.hasPrevious()) {
DrawObject o = oi.previous();
if (o.contains(p)) {
if (e.isAltDown()) {
setCursor(CursorUtils.CURSOR_ARROW_HALF_DOUBLE_HALLOW);
return false;
} else {
setCursor(CursorUtils.CURSOR_ARROW_HALLOW);
return false;
}
}
}
setCursor(CursorUtils.CURSOR_ARROW);
return false;
}
public boolean mousePressed(ToolEvent e) {
if (text != null) {
if (text.contains(e.getX(), e.getY())) {
int i = text.getCursorIndexOfLocation(e.getDrawGraphics(), e.getX(), e.getY());
text.setCursor(i, i);
return true;
} else {
text.setCursor(-1, -1);
if (e.isTransactionInProgress()) e.commitTransaction();
}
}
e.beginTransaction(getName());
e.getCanvas().clearPaintSelection();
Point2D.Float p = new Point2D.Float(e.getX(), e.getY());
DrawSurface d = e.getDrawSurface();
ListIterator<DrawObject> oi;
// clicked on a control point of a selected shape?
oi = d.listIterator(d.size());
while (oi.hasPrevious()) {
DrawObject o = oi.previous();
if (o.isSelected()) {
List<ControlPoint> cp = o.getControlPoints();
for (int i = 0; i < cp.size(); i++) {
ControlPoint c = cp.get(i);
if (c.getType() != ControlPointType.HIDDEN && c.distance(p) <= 3.0) {
// yes, clicked on a control point of a selected shape
// start a control point drag
e.renameTransaction(ToolUtilities.messages.getString("arrow.ChangeControlPoint"));
currentObject = o;
originalControlPoints = cp;
originalCurrentControlPoint = i;
currentCurrentControlPoint = i;
anchorPoints = null;
startedDragRect = false;
text = null;
return true;
}
}
}
}
if (e.isShiftDown()) {
// clicked with shift key down
// start a drag rectangle for inverse selection
e.renameTransaction(ToolUtilities.messages.getString("arrow.Select"));
currentObject = null;
originalControlPoints = null;
originalCurrentControlPoint = 0;
currentCurrentControlPoint = 0;
anchorPoints = null;
startedDragRect = true;
text = null;
return true;
} else {
// clicked on a selected shape?
oi = d.listIterator(d.size());
while (oi.hasPrevious()) {
DrawObject o = oi.previous();
if (o.isSelected() && o.contains(p)) {
// yes, clicked on a selected shape
// double clicked on a text object?
if (o instanceof TextDrawObject && e.getClickCount() > 1 && !o.isLocked()) {
// yes, double clicked on a text object
// start a text edit
e.renameTransaction(ToolUtilities.messages.getString("arrow.TextEdit"));
currentObject = null;
originalControlPoints = null;
originalCurrentControlPoint = 0;
currentCurrentControlPoint = 0;
anchorPoints = null;
startedDragRect = false;
text = (TextDrawObject)o;
int i = text.getCursorIndexOfLocation(e.getDrawGraphics(), e.getX(), e.getY());
text.setCursor(i, i);
return true;
} else {
// no, single clicked on a text object or clicked on a different kind of object
// start a translation
if (e.isAltDown()) {
e.renameTransaction(ToolUtilities.messages.getString("arrow.Duplicate"));
List<DrawObject> clones = new Vector<DrawObject>();
for (DrawObject o2 : d) {
if (o2.isSelected()) {
o2.setSelected(false);
DrawObject clone = o2.clone();
clone.setSelected(true);
clone.setVisible(true);
clone.setLocked(false);
clones.add(clone);
}
}
d.addAll(clones);
} else {
e.renameTransaction(ToolUtilities.messages.getString("arrow.Move"));
}
currentObject = null;
originalControlPoints = null;
originalCurrentControlPoint = 0;
currentCurrentControlPoint = 0;
anchorPoints = new IdentityHashMap<DrawObject,Point2D>();
for (DrawObject o2 : d) {
if (o2.isSelected()) anchorPoints.put(o2, (Point2D)o2.getLocation().clone());
}
startedDragRect = false;
text = null;
return true;
}
}
}
// didn't find a selected shape at the clicked location
// deselect all shapes
for (DrawObject o : d) {
o.setSelected(false);
}
// clicked on an unselected shape?
oi = d.listIterator(d.size());
while (oi.hasPrevious()) {
DrawObject o = oi.previous();
if (o.contains(p)) {
o.setSelected(true);
// yes, clicked on an unselected shape
// double clicked on a text object?
if (o instanceof TextDrawObject && e.getClickCount() > 1 && !o.isLocked()) {
// yes, double clicked on a text object
// start a text edit
e.renameTransaction(ToolUtilities.messages.getString("arrow.TextEdit"));
currentObject = null;
originalControlPoints = null;
originalCurrentControlPoint = 0;
currentCurrentControlPoint = 0;
anchorPoints = null;
startedDragRect = false;
text = (TextDrawObject)o;
int i = text.getCursorIndexOfLocation(e.getDrawGraphics(), e.getX(), e.getY());
text.setCursor(i, i);
return true;
} else {
// no, single clicked on a text object or clicked on a different kind of object
// start a translation
if (e.isAltDown()) {
e.renameTransaction(ToolUtilities.messages.getString("arrow.Duplicate"));
List<DrawObject> clones = new Vector<DrawObject>();
for (DrawObject o2 : d) {
if (o2.isSelected()) {
o2.setSelected(false);
DrawObject clone = o2.clone();
clone.setSelected(true);
clone.setVisible(true);
clone.setLocked(false);
clones.add(clone);
}
}
d.addAll(clones);
} else {
e.renameTransaction(ToolUtilities.messages.getString("arrow.Move"));
}
currentObject = null;
originalControlPoints = null;
originalCurrentControlPoint = 0;
currentCurrentControlPoint = 0;
anchorPoints = new IdentityHashMap<DrawObject,Point2D>();
for (DrawObject o2 : d) {
if (o2.isSelected()) anchorPoints.put(o2, (Point2D)o2.getLocation().clone());
}
startedDragRect = false;
text = null;
return true;
}
}
}
// clicked nowhere
// start a drag rectangle for selection
e.renameTransaction(ToolUtilities.messages.getString("arrow.Select"));
currentObject = null;
originalControlPoints = null;
originalCurrentControlPoint = 0;
currentCurrentControlPoint = 0;
anchorPoints = null;
startedDragRect = true;
text = null;
return true;
}
}
public boolean mouseDragged(ToolEvent e) {
if (text != null) {
int i = text.getCursorIndexOfLocation(e.getDrawGraphics(), e.getX(), e.getY());
text.setCursorEnd(i);
return true;
} else {
if (startedDragRect) {
return false;
}
else if (currentObject != null && originalControlPoints != null) {
// control point drag
if (!currentObject.isLocked()) {
float x = e.getX();
float y = e.getY();
if (e.isShiftDown()) {
Point2D.Float p = controlPointConstrain(e.getPreviousClickedX(), e.getPreviousClickedY(), x, y);
x = p.x; y = p.y;
}
currentCurrentControlPoint = currentObject.setControlPoint(currentCurrentControlPoint, new Point2D.Float(x,y));
return true;
} else {
return false;
}
}
else if (anchorPoints != null) {
// translation
float sx = e.getPreviousClickedX();
float sy = e.getPreviousClickedY();
float x = e.getX();
float y = e.getY();
if (e.isShiftDown()) {
float w = Math.abs(x-sx);
float h = Math.abs(y-sy);
if (w > h*2) y = sy;
else if (h > w*2) x = sx;
else {
float s = Math.max(w, h);
if (y > sy) y = sy+s;
else y = sy-s;
if (x > sx) x = sx+s;
else x = sx-s;
}
}
for (Map.Entry<DrawObject,Point2D> me : anchorPoints.entrySet()) {
DrawObject o = me.getKey();
if (!o.isLocked()) {
Point2D a = me.getValue();
Point2D na = new Point2D.Double(a.getX()-sx+x, a.getY()-sy+y);
o.setLocation(na);
}
}
return true;
}
else {
return false;
}
}
}
public boolean mouseReleased(ToolEvent e) {
if (text != null) {
int i = text.getCursorIndexOfLocation(e.getDrawGraphics(), e.getX(), e.getY());
text.setCursorEnd(i);
return true;
} else {
if (startedDragRect) {
DrawSurface d = e.getDrawSurface();
Point2D.Float p = new Point2D.Float(e.getX(), e.getY());
Rectangle2D r = new Rectangle2D.Float(
Math.min(e.getX(), e.getPreviousClickedX()),
Math.min(e.getY(), e.getPreviousClickedY()),
Math.abs(e.getX()-e.getPreviousClickedX()),
Math.abs(e.getY()-e.getPreviousClickedY())
);
if (r.isEmpty()) {
if (e.isShiftDown()) {
ListIterator<DrawObject> oi = d.listIterator(d.size());
while (oi.hasPrevious()) {
DrawObject o = oi.previous();
if (o.contains(p)) {
o.setSelected(!o.isSelected());
break;
}
}
} else {
ListIterator<DrawObject> oi = d.listIterator(d.size());
while (oi.hasPrevious()) {
DrawObject o = oi.previous();
if (o.contains(p)) {
o.setSelected(true);
break;
}
}
}
} else {
if (e.isShiftDown()) {
for (DrawObject o : d) {
if (o.intersects(r)) {
o.setSelected(!o.isSelected());
}
}
} else {
for (DrawObject o : d) {
o.setSelected(o.intersects(r));
}
}
}
}
else if (currentObject != null && originalControlPoints != null) {
// control point drag
if (!currentObject.isLocked()) {
float x = e.getX();
float y = e.getY();
if (e.isShiftDown()) {
Point2D.Float p = controlPointConstrain(e.getPreviousClickedX(), e.getPreviousClickedY(), x, y);
x = p.x; y = p.y;
}
currentCurrentControlPoint = currentObject.setControlPoint(currentCurrentControlPoint, new Point2D.Float(x,y));
}
}
else if (anchorPoints != null) {
// translation
float sx = e.getPreviousClickedX();
float sy = e.getPreviousClickedY();
float x = e.getX();
float y = e.getY();
if (e.isShiftDown()) {
float w = Math.abs(x-sx);
float h = Math.abs(y-sy);
if (w > h*2) y = sy;
else if (h > w*2) x = sx;
else {
float s = Math.max(w, h);
if (y > sy) y = sy+s;
else y = sy-s;
if (x > sx) x = sx+s;
else x = sx-s;
}
}
for (Map.Entry<DrawObject,Point2D> me : anchorPoints.entrySet()) {
DrawObject o = me.getKey();
if (!o.isLocked()) {
Point2D a = me.getValue();
Point2D na = new Point2D.Double(a.getX()-sx+x, a.getY()-sy+y);
o.setLocation(na);
}
}
}
currentObject = null;
originalControlPoints = null;
originalCurrentControlPoint = 0;
currentCurrentControlPoint = 0;
anchorPoints = null;
startedDragRect = false;
text = null;
e.commitTransaction();
return true;
}
}
private Point2D.Float controlPointConstrain(float sx, float sy, float x, float y) {
ControlPointType t = originalControlPoints.get(originalCurrentControlPoint).getType();
switch (t) {
case NORTHWEST:
case NORTHEAST:
case SOUTHWEST:
case SOUTHEAST:
{
// keep rectangles proportional
double x1 = Double.NaN;
double y1 = Double.NaN;
double x2 = Double.NaN;
double y2 = Double.NaN;
for (ControlPoint cp : originalControlPoints) {
switch (cp.getType()) {
case NORTHWEST: x1 = cp.getX(); y1 = cp.getY(); break;
case NORTHEAST: x2 = cp.getX(); y1 = cp.getY(); break;
case SOUTHWEST: x1 = cp.getX(); y2 = cp.getY(); break;
case SOUTHEAST: x2 = cp.getX(); y2 = cp.getY(); break;
default: break;
}
}
if (!(Double.isNaN(x1) || Double.isNaN(y1) || Double.isNaN(x2) || Double.isNaN(y2))) {
double bx;
double by;
switch (t) {
case NORTHWEST: bx = x2; by = y2; break;
case NORTHEAST: bx = x1; by = y2; break;
case SOUTHWEST: bx = x2; by = y1; break;
case SOUTHEAST: bx = x1; by = y1; break;
default: bx = x1; by = y1; break;
}
double bw = Math.abs(x2-x1);
double bh = Math.abs(y2-y1);
double w = (x-bx)*bh;
double h = (y-by)*bw;
double s = Math.max(Math.abs(w),Math.abs(h));
x = (float)(bx+s/bh*Math.signum(w));
y = (float)(by+s/bw*Math.signum(h));
return new Point2D.Float(x,y);
} else {
// by default, constrain relative to the original location
float w = Math.abs(x-sx);
float h = Math.abs(y-sy);
if (w > h*2) y = sy;
else if (h > w*2) x = sx;
else {
float s = Math.max(w, h);
if (y > sy) y = sy+s;
else y = sy-s;
if (x > sx) x = sx+s;
else x = sx-s;
}
return new Point2D.Float(x,y);
}
}
case RADIUS:
case ANGLE:
{
// constrain relative to the CENTER
for (ControlPoint cp : originalControlPoints) {
if (cp.getType() == ControlPointType.CENTER) {
sx = (float)cp.getX();
sy = (float)cp.getY();
float w = Math.abs(x-sx);
float h = Math.abs(y-sy);
if (w > h*2) y = sy;
else if (h > w*2) x = sx;
else {
float s = Math.max(w, h);
if (y > sy) y = sy+s;
else y = sy-s;
if (x > sx) x = sx+s;
else x = sx-s;
}
return new Point2D.Float(x,y);
}
}
// by default, constrain relative to the original location
float w = Math.abs(x-sx);
float h = Math.abs(y-sy);
if (w > h*2) y = sy;
else if (h > w*2) x = sx;
else {
float s = Math.max(w, h);
if (y > sy) y = sy+s;
else y = sy-s;
if (x > sx) x = sx+s;
else x = sx-s;
}
return new Point2D.Float(x,y);
}
default:
{
// by default, constrain relative to the original location
float w = Math.abs(x-sx);
float h = Math.abs(y-sy);
if (w > h*2) y = sy;
else if (h > w*2) x = sx;
else {
float s = Math.max(w, h);
if (y > sy) y = sy+s;
else y = sy-s;
if (x > sx) x = sx+s;
else x = sx-s;
}
return new Point2D.Float(x,y);
}
}
}
public boolean paintIntermediate(ToolEvent e, Graphics2D g) {
if (startedDragRect && e.isMouseDown()) {
Shape dr = new Rectangle2D.Float(
Math.min(e.getX(), e.getPreviousClickedX()),
Math.min(e.getY(), e.getPreviousClickedY()),
Math.abs(e.getX()-e.getPreviousClickedX()),
Math.abs(e.getY()-e.getPreviousClickedY())
);
ToolUtilities.fillSelectionShape(e, g, dr);
}
return true;
}
public boolean paintSettingsChanged(ToolEvent e) {
int delta = e.getPaintSettingsDelta();
if (text != null) {
if (delta == -1) {
text.setPaintSettings(e.getPaintSettings());
} else {
PaintSettings ps = text.getPaintSettings();
if ((delta & CHANGED_DRAW_COMPOSITE) != 0) ps = ps.deriveDrawComposite(e.getPaintSettings().drawComposite);
if ((delta & CHANGED_DRAW_PAINT) != 0) ps = ps.deriveDrawPaint(e.getPaintSettings().drawPaint);
if ((delta & CHANGED_FILL_COMPOSITE) != 0) ps = ps.deriveFillComposite(e.getPaintSettings().fillComposite);
if ((delta & CHANGED_FILL_PAINT) != 0) ps = ps.deriveFillPaint(e.getPaintSettings().fillPaint);
if ((delta & CHANGED_STROKE) != 0) ps = ps.deriveDrawStroke(e.getPaintSettings().drawStroke);
if ((delta & CHANGED_FONT) != 0) ps = ps.deriveTextFont(e.getPaintSettings().textFont);
if ((delta & CHANGED_TEXT_ALIGNMENT) != 0) ps = ps.deriveTextAlignment(e.getPaintSettings().textAlignment);
if ((delta & CHANGED_ANTI_ALIASED) != 0) {
ps = ps.deriveDrawAntiAliased(e.getPaintSettings().drawAntiAliased);
ps = ps.deriveFillAntiAliased(e.getPaintSettings().fillAntiAliased);
ps = ps.deriveTextAntiAliased(e.getPaintSettings().textAntiAliased);
}
text.setPaintSettings(ps);
}
return true;
} else {
DrawSurface d = e.getDrawSurface();
if (e.isTransactionInProgress()) {
for (DrawObject o : d) {
if (o.isSelected() && !o.isLocked()) {
if (delta == -1) {
o.setPaintSettings(e.getPaintSettings());
} else {
PaintSettings ps = o.getPaintSettings();
if ((delta & CHANGED_DRAW_COMPOSITE) != 0) ps = ps.deriveDrawComposite(e.getPaintSettings().drawComposite);
if ((delta & CHANGED_DRAW_PAINT) != 0) ps = ps.deriveDrawPaint(e.getPaintSettings().drawPaint);
if ((delta & CHANGED_FILL_COMPOSITE) != 0) ps = ps.deriveFillComposite(e.getPaintSettings().fillComposite);
if ((delta & CHANGED_FILL_PAINT) != 0) ps = ps.deriveFillPaint(e.getPaintSettings().fillPaint);
if ((delta & CHANGED_STROKE) != 0) ps = ps.deriveDrawStroke(e.getPaintSettings().drawStroke);
if ((delta & CHANGED_FONT) != 0) ps = ps.deriveTextFont(e.getPaintSettings().textFont);
if ((delta & CHANGED_TEXT_ALIGNMENT) != 0) ps = ps.deriveTextAlignment(e.getPaintSettings().textAlignment);
if ((delta & CHANGED_ANTI_ALIASED) != 0) {
ps = ps.deriveDrawAntiAliased(e.getPaintSettings().drawAntiAliased);
ps = ps.deriveFillAntiAliased(e.getPaintSettings().fillAntiAliased);
ps = ps.deriveTextAntiAliased(e.getPaintSettings().textAntiAliased);
}
o.setPaintSettings(ps);
}
}
}
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.ChangePaintSettings"));
e.getCanvas().clearPaintSelection();
for (DrawObject o : d) {
if (o.isSelected() && !o.isLocked()) {
if (delta == -1) {
o.setPaintSettings(e.getPaintSettings());
} else {
PaintSettings ps = o.getPaintSettings();
if ((delta & CHANGED_DRAW_COMPOSITE) != 0) ps = ps.deriveDrawComposite(e.getPaintSettings().drawComposite);
if ((delta & CHANGED_DRAW_PAINT) != 0) ps = ps.deriveDrawPaint(e.getPaintSettings().drawPaint);
if ((delta & CHANGED_FILL_COMPOSITE) != 0) ps = ps.deriveFillComposite(e.getPaintSettings().fillComposite);
if ((delta & CHANGED_FILL_PAINT) != 0) ps = ps.deriveFillPaint(e.getPaintSettings().fillPaint);
if ((delta & CHANGED_STROKE) != 0) ps = ps.deriveDrawStroke(e.getPaintSettings().drawStroke);
if ((delta & CHANGED_FONT) != 0) ps = ps.deriveTextFont(e.getPaintSettings().textFont);
if ((delta & CHANGED_TEXT_ALIGNMENT) != 0) ps = ps.deriveTextAlignment(e.getPaintSettings().textAlignment);
if ((delta & CHANGED_ANTI_ALIASED) != 0) {
ps = ps.deriveDrawAntiAliased(e.getPaintSettings().drawAntiAliased);
ps = ps.deriveFillAntiAliased(e.getPaintSettings().fillAntiAliased);
ps = ps.deriveTextAntiAliased(e.getPaintSettings().textAntiAliased);
}
o.setPaintSettings(ps);
}
}
}
e.commitTransaction();
}
return true;
}
}
public boolean keyTyped(ToolEvent e) {
if (text != null) {
char ch = e.getChar();
if (ch >= 0x0020 && ch <= 0xFFFD) {
text.setSelectedText(Character.toString(ch));
return true;
} else {
return false;
}
} else {
return false;
}
}
public boolean keyPressed(ToolEvent e) {
if (text != null) {
switch (e.getKeyCode()) {
case KeyEvent.VK_ALT:
return mouseMoved(e);
case KeyEvent.VK_ESCAPE:
case KeyEvent.VK_CLEAR:
text.setCursor(-1, -1);
if (e.isTransactionInProgress()) e.commitTransaction();
text = null;
return true;
case KeyEvent.VK_BACK_SPACE:
text.deleteBackward();
return true;
case KeyEvent.VK_DELETE:
text.deleteForward();
return true;
case KeyEvent.VK_ENTER:
text.setSelectedText("\n");
return true;
case KeyEvent.VK_UP:
text.moveCursor(e.getDrawGraphics(), -1, 0, e.isShiftDown());
return true;
case KeyEvent.VK_LEFT:
text.moveCursor(e.getDrawGraphics(), 0, -1, e.isShiftDown());
return true;
case KeyEvent.VK_RIGHT:
text.moveCursor(e.getDrawGraphics(), 0, +1, e.isShiftDown());
return true;
case KeyEvent.VK_DOWN:
text.moveCursor(e.getDrawGraphics(), +1, 0, e.isShiftDown());
return true;
default:
return false;
}
} else {
switch (e.getKeyCode()) {
case KeyEvent.VK_ALT:
return mouseMoved(e);
case KeyEvent.VK_LEFT:
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_UP:
case KeyEvent.VK_DOWN:
if (e.isTransactionInProgress()) {
nudge(e);
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.Nudge"));
e.getCanvas().clearPaintSelection();
nudge(e);
e.commitTransaction();
}
return true;
case KeyEvent.VK_BACK_SPACE:
case KeyEvent.VK_DELETE:
if (e.isTransactionInProgress()) {
delete(e.getDrawSurface());
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.Clear"));
e.getCanvas().clearPaintSelection();
delete(e.getDrawSurface());
e.commitTransaction();
}
return true;
default:
return false;
}
}
}
public boolean keyReleased(ToolEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_ALT:
return mouseMoved(e);
default:
return false;
}
}
public boolean respondsToCommand(ToolEvent e) {
switch (e.getCommand()) {
case CUT:
case COPY:
case PASTE:
case CLEAR:
case DUPLICATE:
case SELECT_ALL:
case DESELECT_ALL:
case INVERT_SELECTION:
case GROUP:
case UNGROUP:
case BREAK_APART:
case LOCK:
case UNLOCK:
case HIDE:
case UNHIDE:
case SEND_TO_BACK:
case SEND_BACKWARD:
case BRING_FORWARD:
case BRING_TO_FRONT:
return true;
default:
return false;
}
}
public boolean enableCommand(ToolEvent e) {
if (text != null) {
switch (e.getCommand()) {
case CUT:
case COPY:
case CLEAR:
return (text.getCursorStart() != text.getCursorEnd());
case PASTE:
return ClipboardUtilities.clipboardHasString();
case SELECT_ALL:
return true;
default:
return false;
}
} else {
switch (e.getCommand()) {
case CUT:
case CLEAR:
case BREAK_APART:
case SEND_TO_BACK:
case SEND_BACKWARD:
case BRING_FORWARD:
case BRING_TO_FRONT:
for (DrawObject o : e.getDrawSurface()) {
if (o.isSelected() && !o.isLocked()) return true;
}
return false;
case COPY:
case DUPLICATE:
for (DrawObject o : e.getDrawSurface()) {
if (o.isSelected()) return true;
}
return false;
case PASTE:
return ClipboardUtilities.clipboardHasDrawObjects() || ClipboardUtilities.clipboardHasImage() || ClipboardUtilities.clipboardHasString();
case SELECT_ALL:
case DESELECT_ALL:
case INVERT_SELECTION:
return true;
case GROUP:
int cnt = 0;
for (DrawObject o : e.getDrawSurface()) {
if (o.isSelected()) cnt++;
}
return (cnt > 1);
case UNGROUP:
for (DrawObject o : e.getDrawSurface()) {
if (o.isSelected() && o instanceof GroupDrawObject) return true;
}
return false;
case LOCK:
for (DrawObject o : e.getDrawSurface()) {
if (o.isSelected() && !o.isLocked()) return true;
}
return false;
case UNLOCK:
for (DrawObject o : e.getDrawSurface()) {
if (o.isSelected() && o.isLocked()) return true;
}
return false;
case HIDE:
for (DrawObject o : e.getDrawSurface()) {
if (o.isSelected() && o.isVisible()) return true;
}
return false;
case UNHIDE:
for (DrawObject o : e.getDrawSurface()) {
if (o.isSelected() && !o.isVisible()) return true;
}
return false;
default:
return false;
}
}
}
public boolean doCommand(ToolEvent e) {
if (text != null) {
switch (e.getCommand()) {
case CUT:
ClipboardUtilities.setClipboardString(text.getSelectedText());
text.setSelectedText("");
return true;
case COPY:
ClipboardUtilities.setClipboardString(text.getSelectedText());
return false;
case PASTE:
if (ClipboardUtilities.clipboardHasString()) {
text.setSelectedText(ClipboardUtilities.getClipboardString());
}
return true;
case CLEAR:
text.setSelectedText("");
return true;
case SELECT_ALL:
text.setCursor(0, text.getText().length());
return true;
default:
return false;
}
} else {
switch (e.getCommand()) {
case CUT:
if (e.isTransactionInProgress()) {
copy(e.getDrawSurface());
delete(e.getDrawSurface());
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.Cut"));
e.getCanvas().clearPaintSelection();
copy(e.getDrawSurface());
delete(e.getDrawSurface());
e.commitTransaction();
}
return true;
case COPY:
copy(e.getDrawSurface());
return false;
case PASTE:
if (e.isTransactionInProgress()) {
paste(e);
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.Paste"));
e.getCanvas().clearPaintSelection();
paste(e);
e.commitTransaction();
}
return true;
case CLEAR:
if (e.isTransactionInProgress()) {
delete(e.getDrawSurface());
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.Clear"));
e.getCanvas().clearPaintSelection();
delete(e.getDrawSurface());
e.commitTransaction();
}
return true;
case DUPLICATE:
if (e.isTransactionInProgress()) {
duplicate(e.getDrawSurface());
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.Duplicate"));
e.getCanvas().clearPaintSelection();
duplicate(e.getDrawSurface());
e.commitTransaction();
}
return true;
case SELECT_ALL:
if (e.isTransactionInProgress()) {
for (DrawObject o : e.getDrawSurface()) {
o.setSelected(true);
}
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.SelectAll"));
e.getCanvas().clearPaintSelection();
for (DrawObject o : e.getDrawSurface()) {
o.setSelected(true);
}
e.commitTransaction();
}
return true;
case DESELECT_ALL:
if (e.isTransactionInProgress()) {
for (DrawObject o : e.getDrawSurface()) {
o.setSelected(false);
}
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.DeselectAll"));
e.getCanvas().clearPaintSelection();
for (DrawObject o : e.getDrawSurface()) {
o.setSelected(false);
}
e.commitTransaction();
}
return true;
case INVERT_SELECTION:
if (e.isTransactionInProgress()) {
for (DrawObject o : e.getDrawSurface()) {
o.setSelected(!o.isSelected());
}
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.InvertSelection"));
e.getCanvas().clearPaintSelection();
for (DrawObject o : e.getDrawSurface()) {
o.setSelected(!o.isSelected());
}
e.commitTransaction();
}
return true;
case GROUP:
if (e.isTransactionInProgress()) {
group(e.getDrawSurface(), e.getPaintSettings());
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.Group"));
e.getCanvas().clearPaintSelection();
group(e.getDrawSurface(), e.getPaintSettings());
e.commitTransaction();
}
return true;
case UNGROUP:
if (e.isTransactionInProgress()) {
ungroup(e.getDrawSurface());
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.Ungroup"));
e.getCanvas().clearPaintSelection();
ungroup(e.getDrawSurface());
e.commitTransaction();
}
return true;
case BREAK_APART:
if (e.isTransactionInProgress()) {
breakApart(e.getDrawSurface());
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.BreakApart"));
e.getCanvas().clearPaintSelection();
breakApart(e.getDrawSurface());
e.commitTransaction();
}
return true;
case LOCK:
if (e.isTransactionInProgress()) {
for (DrawObject o : e.getDrawSurface()) {
if (o.isSelected()) o.setLocked(true);
}
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.Lock"));
e.getCanvas().clearPaintSelection();
for (DrawObject o : e.getDrawSurface()) {
if (o.isSelected()) o.setLocked(true);
}
e.commitTransaction();
}
return true;
case UNLOCK:
if (e.isTransactionInProgress()) {
for (DrawObject o : e.getDrawSurface()) {
if (o.isSelected()) o.setLocked(false);
}
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.Unlock"));
e.getCanvas().clearPaintSelection();
for (DrawObject o : e.getDrawSurface()) {
if (o.isSelected()) o.setLocked(false);
}
e.commitTransaction();
}
return true;
case HIDE:
if (e.isTransactionInProgress()) {
for (DrawObject o : e.getDrawSurface()) {
if (o.isSelected()) o.setVisible(false);
}
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.Hide"));
e.getCanvas().clearPaintSelection();
for (DrawObject o : e.getDrawSurface()) {
if (o.isSelected()) o.setVisible(false);
}
e.commitTransaction();
}
return true;
case UNHIDE:
if (e.isTransactionInProgress()) {
for (DrawObject o : e.getDrawSurface()) {
if (o.isSelected()) o.setVisible(true);
}
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.Unhide"));
e.getCanvas().clearPaintSelection();
for (DrawObject o : e.getDrawSurface()) {
if (o.isSelected()) o.setVisible(true);
}
e.commitTransaction();
}
return true;
case BRING_TO_FRONT:
if (e.isTransactionInProgress()) {
bringToFront(e.getDrawSurface());
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.BringToFront"));
e.getCanvas().clearPaintSelection();
bringToFront(e.getDrawSurface());
e.commitTransaction();
}
return true;
case BRING_FORWARD:
if (e.isTransactionInProgress()) {
bringForward(e.getDrawSurface());
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.BringForward"));
e.getCanvas().clearPaintSelection();
bringForward(e.getDrawSurface());
e.commitTransaction();
}
return true;
case SEND_BACKWARD:
if (e.isTransactionInProgress()) {
sendBackward(e.getDrawSurface());
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.SendBackward"));
e.getCanvas().clearPaintSelection();
sendBackward(e.getDrawSurface());
e.commitTransaction();
}
return true;
case SEND_TO_BACK:
if (e.isTransactionInProgress()) {
sendToBack(e.getDrawSurface());
} else {
e.beginTransaction(ToolUtilities.messages.getString("arrow.SendToBack"));
e.getCanvas().clearPaintSelection();
sendToBack(e.getDrawSurface());
e.commitTransaction();
}
return true;
default:
return false;
}
}
}
private void nudge(ToolEvent e) {
float n;
if (!e.isCtrlDown() && !e.isAltDown() && !e.isShiftDown()) {
n = 1;
} else {
n = 0;
if (e.isCtrlDown()) n += 8;
if (e.isAltDown()) n += 16;
if (e.isShiftDown()) n += 32;
}
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
nudge(e.getDrawSurface(), -n, 0);
break;
case KeyEvent.VK_RIGHT:
nudge(e.getDrawSurface(), n, 0);
break;
case KeyEvent.VK_UP:
nudge(e.getDrawSurface(), 0, -n);
break;
case KeyEvent.VK_DOWN:
nudge(e.getDrawSurface(), 0, n);
break;
}
}
private void nudge(DrawSurface d, float dx, float dy) {
for (DrawObject o : d) {
if (o.isSelected() && !o.isLocked()) {
Point2D p = o.getLocation();
o.setLocation(new Point2D.Double(p.getX()+dx, p.getY()+dy));
}
}
}
private void copy(DrawSurface d) {
List<DrawObject> sel = new Vector<DrawObject>();
for (DrawObject o : d) {
if (o.isSelected()) sel.add(o);
}
ClipboardUtilities.setClipboardDrawObjects(sel);
}
private void paste(ToolEvent e) {
for (DrawObject o : e.getDrawSurface()) {
o.setSelected(false);
}
if (ClipboardUtilities.clipboardHasDrawObjects()) {
Collection<? extends DrawObject> c = ClipboardUtilities.getClipboardDrawObjects();
for (DrawObject o : c) {
DrawObject oo = o.clone();
oo.setSelected(true);
e.getDrawSurface().add(oo);
}
}
else if (ClipboardUtilities.clipboardHasImage()) {
Image i = ClipboardUtilities.getClipboardImage();
if (ImageUtils.prepImage(i)) {
int w = i.getWidth(null);
int h = i.getHeight(null);
PaintSettings ps = new PaintSettings(null, null);
ImageDrawObject ido = ImageDrawObject.forGraphicsDrawImage(
ps, i,
(int)(e.getPreviousClickedX() - w / 2),
(int)(e.getPreviousClickedY() - h / 2));
ido.setSelected(true);
e.getDrawSurface().add(ido);
} else {
System.err.println("Error: Failed to paste image.");
}
}
else if (ClipboardUtilities.clipboardHasString()) {
TextDrawObject tdo = new TextDrawObject(
e.getPaintSettings(),
e.getPreviousClickedX(),
e.getPreviousClickedY(),
Integer.MAX_VALUE,
ClipboardUtilities.getClipboardString()
);
tdo.setSelected(true);
e.getDrawSurface().add(tdo);
}
}
private void delete(DrawSurface d) {
Map<DrawObject,DrawObject> sel = new IdentityHashMap<DrawObject,DrawObject>();
for (DrawObject o : d) {
if (o.isSelected() && !o.isLocked()) sel.put(o,o);
}
for (DrawObject o : sel.values()) {
d.remove(o);
}
}
private void duplicate(DrawSurface d) {
List<DrawObject> sel = new Vector<DrawObject>();
for (DrawObject o : d) {
if (o.isSelected()) {
o.setSelected(false);
o = o.clone();
o.setSelected(true);
o.setVisible(true);
o.setLocked(false);
Point2D p = o.getLocation();
p = new Point2D.Double(p.getX()+8, p.getY()+8);
o.setLocation(p);
sel.add(o);
}
}
d.addAll(sel);
}
private void group(DrawSurface d, PaintSettings ps) {
int selindex = 0;
List<DrawObject> sel = new Vector<DrawObject>();
for (int i = 0; i < d.size(); i++) {
DrawObject o = d.get(i);
if (o.isSelected()) {
sel.add(o);
selindex = i+1;
}
}
if (sel.size() > 1) {
GroupDrawObject gdo = new GroupDrawObject(sel);
gdo.setSelected(true);
d.add(selindex, gdo);
d.removeAll(sel);
}
}
private void ungroup(DrawSurface d) {
for (int i = d.size()-1; i >= 0; i--) {
DrawObject o = d.get(i);
if (o.isSelected() && o instanceof GroupDrawObject) {
GroupDrawObject g = (GroupDrawObject)o;
d.addAll(i+1, g.ungroup());
d.remove(i);
}
}
}
private void breakApart(DrawSurface d) {
for (int i = d.size()-1; i >= 0; i--) {
DrawObject o = d.get(i);
if (o.isSelected() && !o.isLocked()) {
if (o instanceof GroupDrawObject) {
GroupDrawObject g = (GroupDrawObject)o;
d.addAll(i+1, g.ungroup());
d.remove(i);
} else if (o instanceof ShapeDrawObject) {
ShapeDrawObject s1 = (ShapeDrawObject)o;
ShapeDrawObject s2 = new PathDrawObject(s1.getPaintSettings(), s1.getShape());
s2.setSelected(s1.isSelected());
s2.setVisible(s1.isVisible());
s2.setLocked(s1.isLocked());
s2.setTransform(s1.getTransform());
s2.setShadowSettings(s1.getShadowSettings());
d.add(i+1, s2);
d.remove(i);
}
}
}
}
private void sendToBack(DrawSurface d) {
List<DrawObject> sel = new Vector<DrawObject>();
for (DrawObject o : d) {
if (o.isSelected() && !o.isLocked()) sel.add(o);
}
d.removeAll(sel);
d.addAll(0, sel);
}
private void sendBackward(DrawSurface d) {
boolean first = true;
int index = 0;
List<DrawObject> sel = new Vector<DrawObject>();
for (int i = 0; i < d.size(); i++) {
DrawObject o = d.get(i);
if (o.isSelected() && !o.isLocked()) {
if (first) {
first = false;
index = i;
}
sel.add(o);
}
}
index--;
if (index < 0) index = 0;
d.removeAll(sel);
d.addAll(index, sel);
}
private void bringForward(DrawSurface d) {
int index = d.size();
List<DrawObject> sel = new Vector<DrawObject>();
for (int i = 0; i < d.size(); i++) {
DrawObject o = d.get(i);
if (o.isSelected() && !o.isLocked()) {
index = i;
sel.add(o);
}
}
index += 2;
if (index > d.size()) index = d.size();
d.removeAll(sel);
index -= sel.size();
d.addAll(index, sel);
}
private void bringToFront(DrawSurface d) {
List<DrawObject> sel = new Vector<DrawObject>();
for (DrawObject o : d) {
if (o.isSelected() && !o.isLocked()) sel.add(o);
}
d.removeAll(sel);
d.addAll(sel);
}
private Cursor cursor = CursorUtils.CURSOR_ARROW;
private void setCursor(Cursor c) {
cursor = c;
}
public Cursor getCursor(ToolEvent e) {
return cursor;
}
public boolean usesLetterKeys() {
return (text != null);
}
private static Cursor getControlPointCursor(ControlPoint cp) {
switch (cp.getType()) {
case NORTHWEST:
return Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR);
case NORTH:
return Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR);
case NORTHEAST:
return Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR);
case EAST:
return Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR);
case SOUTHEAST:
return Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR);
case SOUTH:
return Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR);
case SOUTHWEST:
return Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR);
case WEST:
return Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR);
case ENDPOINT:
case CURVED_MIDPOINT:
case STRAIGHT_MIDPOINT:
case CONTROL_POINT:
case PULL_TAB:
return CursorUtils.CURSOR_ARROW_HALF;
default:
return CursorUtils.CURSOR_MOVE;
}
}
}