/* Copyright (c) 2010, Carl Burch. License information is located in the
* com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
package com.cburch.draw.tools;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Icon;
import com.cburch.draw.actions.ModelAddAction;
import com.cburch.draw.canvas.Canvas;
import com.cburch.draw.model.CanvasModel;
import com.cburch.draw.model.CanvasObject;
import com.cburch.draw.shapes.DrawAttr;
import com.cburch.draw.shapes.LineUtil;
import com.cburch.draw.shapes.Poly;
import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.util.Icons;
public class PolyTool extends AbstractTool {
// how close we need to be to the start point to count as "closing the loop"
private static final int CLOSE_TOLERANCE = 2;
private boolean closed; // whether we are drawing polygons or polylines
private DrawingAttributeSet attrs;
private boolean active;
private ArrayList<Location> locations;
private boolean mouseDown;
private int lastMouseX;
private int lastMouseY;
public PolyTool(boolean closed, DrawingAttributeSet attrs) {
this.closed = closed;
this.attrs = attrs;
active = false;
locations = new ArrayList<Location>();
}
@Override
public Icon getIcon() {
if (closed) {
return Icons.getIcon("drawpoly.gif");
} else {
return Icons.getIcon("drawplin.gif");
}
}
@Override
public List<Attribute<?>> getAttributes() {
return DrawAttr.getFillAttributes(attrs.getValue(DrawAttr.PAINT_TYPE));
}
@Override
public Cursor getCursor(Canvas canvas) {
return Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR);
}
@Override
public void toolDeselected(Canvas canvas) {
CanvasObject add = commit(canvas);
canvas.toolGestureComplete(this, add);
repaintArea(canvas);
}
@Override
public void mousePressed(Canvas canvas, MouseEvent e) {
int mx = e.getX();
int my = e.getY();
lastMouseX = mx;
lastMouseY = my;
int mods = e.getModifiersEx();
if ((mods & InputEvent.CTRL_DOWN_MASK) != 0) {
mx = canvas.snapX(mx);
my = canvas.snapY(my);
}
if (active && e.getClickCount() > 1) {
CanvasObject add = commit(canvas);
canvas.toolGestureComplete(this, add);
return;
}
Location loc = Location.create(mx, my);
ArrayList<Location> locs = locations;
if (!active) { locs.clear(); locs.add(loc); }
locs.add(loc);
mouseDown = true;
active = canvas.getModel() != null;
repaintArea(canvas);
}
@Override
public void mouseDragged(Canvas canvas, MouseEvent e) {
updateMouse(canvas, e.getX(), e.getY(), e.getModifiersEx());
}
@Override
public void mouseReleased(Canvas canvas, MouseEvent e) {
if (active) {
updateMouse(canvas, e.getX(), e.getY(), e.getModifiersEx());
mouseDown = false;
int size = locations.size();
if (size >= 3) {
Location first = locations.get(0);
Location last = locations.get(size - 1);
if (first.manhattanDistanceTo(last) <= CLOSE_TOLERANCE) {
locations.remove(size - 1);
CanvasObject add = commit(canvas);
canvas.toolGestureComplete(this, add);
}
}
}
}
@Override
public void keyPressed(Canvas canvas, KeyEvent e) {
int code = e.getKeyCode();
if (active && mouseDown
&& (code == KeyEvent.VK_SHIFT || code == KeyEvent.VK_CONTROL)) {
updateMouse(canvas, lastMouseX, lastMouseY, e.getModifiersEx());
}
}
@Override
public void keyReleased(Canvas canvas, KeyEvent e) {
keyPressed(canvas, e);
}
@Override
public void keyTyped(Canvas canvas, KeyEvent e) {
if (active) {
char ch = e.getKeyChar();
if (ch == '\u001b') { // escape key
active = false;
locations.clear();
repaintArea(canvas);
canvas.toolGestureComplete(this, null);
} else if (ch == '\n') { // enter key
CanvasObject add = commit(canvas);
canvas.toolGestureComplete(this, add);
}
}
}
private CanvasObject commit(Canvas canvas) {
if (!active) return null;
CanvasObject add = null;
active = false;
ArrayList<Location> locs = locations;
for(int i = locs.size() - 2; i >= 0; i--) {
if (locs.get(i).equals(locs.get(i + 1))) locs.remove(i);
}
if (locs.size() > 1) {
CanvasModel model = canvas.getModel();
add = new Poly(closed, locs);
canvas.doAction(new ModelAddAction(model, add));
repaintArea(canvas);
}
locs.clear();
return add;
}
private void updateMouse(Canvas canvas, int mx, int my, int mods) {
lastMouseX = mx;
lastMouseY = my;
if (active) {
int index = locations.size() - 1;
Location last = locations.get(index);
Location newLast;
if ((mods & MouseEvent.SHIFT_DOWN_MASK) != 0 && index > 0) {
Location nextLast = locations.get(index - 1);
newLast = LineUtil.snapTo8Cardinals(nextLast, mx, my);
} else {
newLast = Location.create(mx, my);
}
if ((mods & MouseEvent.CTRL_DOWN_MASK) != 0) {
int lastX = newLast.getX();
int lastY = newLast.getY();
lastX = canvas.snapX(lastX);
lastY = canvas.snapY(lastY);
newLast = Location.create(lastX, lastY);
}
if (!newLast.equals(last)) {
locations.set(index, newLast);
repaintArea(canvas);
}
}
}
private void repaintArea(Canvas canvas) {
canvas.repaint();
}
@Override
public void draw(Canvas canvas, Graphics g) {
if (active) {
g.setColor(Color.GRAY);
int size = locations.size();
int[] xs = new int[size];
int[] ys = new int[size];
for(int i = 0; i < size; i++) {
Location loc = locations.get(i);
xs[i] = loc.getX();
ys[i] = loc.getY();
}
g.drawPolyline(xs, ys, size);
int lastX = xs[xs.length - 1];
int lastY = ys[ys.length - 1];
g.fillOval(lastX - 2, lastY - 2, 4, 4);
}
}
}