/* PaintApplet.java Written by Shinya Kasatani */ import java.applet.*; import java.awt.*; import java.util.Observer; import java.util.Observable; public class PaintApplet extends Applet { PaintState state; protected static int topMargin = 10; protected static final Dimension canvasSize = new Dimension(400, 300); protected static final int tnRatio = 4; protected static final Dimension tnSize = new Dimension(canvasSize.width / tnRatio, canvasSize.height / tnRatio); protected static final Dimension toolsSize = new Dimension(400, 30); protected static final int historyBlank = 20; protected static final Dimension historySize = new Dimension(historyBlank * 2 + tnSize.width, canvasSize.height + toolsSize.height); protected static final int tnNum = (historySize.height - historyBlank) / (tnSize.height + historyBlank); protected static final Dimension buttonSize = new Dimension(20, 20); protected static final Dimension toggleButtonSize = new Dimension(20, 20); protected static final Color toggleButtonColor = Color.green; protected static final int toggleXData[][] = { {10, 3, 17}, {10, 3, 17}, {3, 16, 16}, {17, 4, 4}}; protected static final int toggleYData[][] = { {3, 16, 16}, {17, 4, 4}, {10, 3, 17}, {10, 3, 17}}; protected static final int freeHandXData[] = { 4, 6, 6, 10, 12, 15}; protected static final int freeHandYData[] = { 6, 6, 6, 8, 7, 15}; protected static final int buttonBlank = 5; protected static final int LINE = 1; protected static final int FREEHAND = 2; int penType = FREEHAND; Line line = new Line(); FreeHand freehand = new FreeHand(); PaintState.OperationNode viewNode; int viewNodeIndex; public PaintApplet() { setBackground(Color.lightGray); setLayout(null); state = new PaintState(); viewNode = state.getCurrent(); viewNodeIndex = 0; PaintCanvas canvas = new PaintCanvas(); PaintTools tools = new PaintTools(); PaintHistory history = new PaintHistory(); add(history); add(canvas); add(tools); history.move(0, topMargin); canvas.move(historySize.width, topMargin); tools.move(historySize.width, canvasSize.height + topMargin); } public void start() { } public void stop() { } public void setPenType(int type) { penType = type; } protected PaintState.OperationNode getHistoryNode(int n) { if (n < viewNodeIndex) return null; PaintState.OperationNode node = viewNode; for (int i = 0; i < (n - viewNodeIndex); i++) { if (node != null) { node = node.getParent(); } } return node; } public abstract class AbstractPaintCanvas extends Canvas implements Observer, PaintState.Xform { Image image; public AbstractPaintCanvas() { setBackground(Color.white); } public void update(Observable o, Object arg) { repaint(); } public void update(Graphics g) { if (image == null) { image = createImage(getSize().width, getSize().height); } Graphics ig = image.getGraphics(); ig.setFont(getFont()); ig.setColor(getForeground()); ig.clearRect(0, 0, getSize().width, getSize().height); paint(ig); g.drawImage(image, 0, 0, this); } public abstract Point xform(int x, int y); public abstract void xform(int inX[], int inY[], int points, int outX[], int outY[]); public abstract Dimension getSize(); } public class PaintCanvas extends AbstractPaintCanvas { public PaintCanvas() { resize(canvasSize); state.addObserver(this); } public void paint(Graphics g) { state.drawCurrent(g, this); g.setColor(Color.black); g.drawRect(0, 0, canvasSize.width - 1, canvasSize.height - 1); } public Point xform(int x, int y) { return new Point(x, y); } public void xform(int inX[], int inY[], int points, int outX[], int outY[]) { for (int i = 0; i < points; i++) { outX[i] = inX[i]; outY[i] = inY[i]; } } public Dimension getSize() { return canvasSize; } public boolean mouseDown(Event evt, int x, int y) { switch (penType) { case LINE: line.newLine(); line.setBegin(x, y); break; case FREEHAND: freehand.newLine(); { freehand.put(x, y); Graphics g = getGraphics(); g.setColor(state.getColor()); freehand.drawTemp(g); } break; } return true; } public boolean mouseDrag(Event evt, int x, int y) { switch (penType) { case LINE: { Graphics g = getGraphics(); g.setXORMode(Color.white); g.setColor(Color.black); if (line.endIsSet()) { g.drawLine(line.x1, line.y1, line.x2, line.y2); } line.setEnd(x, y); g.drawLine(line.x1, line.y1, line.x2, line.y2); } break; case FREEHAND: { freehand.put(x, y); Graphics g = getGraphics(); g.setColor(state.getColor()); freehand.drawTemp(g); } break; } return true; } public boolean mouseUp(Event evt, int x, int y) { switch (penType) { case LINE: if (line.endIsSet()) { Graphics g = getGraphics(); g.setXORMode(Color.white); g.setColor(Color.black); g.drawLine(line.x1, line.y1, line.x2, line.y2); } line.setEnd(x, y); //System.out.println("line"); state.line(line.x1, line.y1, line.x2, line.y2); break; case FREEHAND: freehand.put(x, y); freehand.prepareOutput(); state.freehand(freehand.getX(), freehand.getY(), freehand.getX().length); break; } return true; } } public class PaintHistory extends Panel implements Observer { ThumbNailPanel panel[] = new ThumbNailPanel[tnNum]; ToggleButton up, down; public PaintHistory() { setLayout(null); resize(historySize); for (int i = 0; i < tnNum; i++) { panel[i] = new ThumbNailPanel(i); add(panel[i]); up = new ToggleButton(ToggleButton.UP, -1); add(up); down = new ToggleButton(ToggleButton.DOWN, -1); add(down); panel[i].move(0, historyBlank + (tnSize.height + historyBlank) * i); up.move((historySize.width - toggleButtonSize.width) / 2, 0); down.move((historySize.width - toggleButtonSize.width) / 2, historySize.height - toggleButtonSize.height); } state.addObserver(this); updateView(); } public void updateView() { if (viewNodeIndex == 0 && viewNode.getChild() != null) { up.show(); } else { up.hide(); } if (getHistoryNode(tnNum) != null) { down.show(); } else { down.hide(); } for (int i = 0; i < tnNum; i++) { panel[i].updateView(); } } public void update(Observable o, Object arg) { if (arg != null) { viewNode = state.getCurrent(); viewNodeIndex = 0; } updateView(); } public class ThumbNailPanel extends Panel { int num; ThumbNailCanvas canvas; ToggleButton left, right; public ThumbNailPanel(int n) { num = n; setLayout(null); resize(tnSize.width + historyBlank * 2, tnSize.height); canvas = new ThumbNailCanvas(n); left = new ToggleButton(ToggleButton.LEFT, n); right = new ToggleButton(ToggleButton.RIGHT, n); add(canvas); add(left); add(right); canvas.move(historyBlank, 0); left.move(0, (tnSize.height - toggleButtonSize.height) / 2); right.move(getSize().width - toggleButtonSize.width - 1, (tnSize.height - toggleButtonSize.height) / 2); } public void updateView() { PaintState.OperationNode node = getHistoryNode(num); if (node != null) { if (node.prevSibling() != null) { right.show(); } else { right.hide(); } if (node.nextSibling() != null) { left.show(); } else { left.hide(); } canvas.show(); canvas.repaint(); } else { canvas.hide(); } } } public class ToggleButton extends Canvas { public static final int UP = 0; public static final int DOWN = 1; public static final int LEFT = 2; public static final int RIGHT = 3; int type; int num; public ToggleButton(int type, int num) { this.type = type; this.num = num; resize(toggleButtonSize); hide(); } public void paint(Graphics g) { if (isVisible()) { g.setColor(toggleButtonColor); g.fillPolygon(toggleXData[type], toggleYData[type], 3); g.setColor(Color.black); g.drawPolygon(toggleXData[type], toggleYData[type], 3); } } public boolean mouseDown(Event evt, int x, int y) { switch (type) { case UP: if (viewNodeIndex == 0 && viewNode.getChild() != null) { viewNode = viewNode.getChild(); PaintHistory.this.updateView(); } break; case DOWN: if (viewNode.getParent() != null) { viewNode = viewNode.getParent(); if (viewNodeIndex > 0) viewNodeIndex--; PaintHistory.this.updateView(); } break; case RIGHT: { PaintState.OperationNode node = getHistoryNode(num); if (node != null && node.prevSibling() != null) { viewNode = node.prevSibling(); viewNodeIndex = num; normalizeViewNode(); PaintHistory.this.updateView(); } } break; case LEFT: { PaintState.OperationNode node = getHistoryNode(num); if (node != null && node.nextSibling() != null) { viewNode = node.nextSibling(); viewNodeIndex = num; normalizeViewNode(); PaintHistory.this.updateView(); } } break; } return true; } private void normalizeViewNode() { while (viewNodeIndex > 0) { PaintState.OperationNode node = viewNode.getChild(); if (node == null) { break; } viewNode = node; viewNodeIndex--; } } } public class ThumbNailCanvas extends AbstractPaintCanvas { int num; public ThumbNailCanvas(int n) { num = n; resize(tnSize); //state.addObserver(this); } public void paint(Graphics g) { PaintState.OperationNode node = getHistoryNode(num); if (node != null) { setBackground(Color.white); state.draw(node, g, this); g.setColor(Color.black); g.drawRect(0, 0, tnSize.width - 1, tnSize.height - 1); } else { setBackground(getParent().getBackground()); } } public boolean mouseDown(Event evt, int x, int y) { PaintState.OperationNode node = getHistoryNode(num); if (node != null) { state.setCurrent(node); } return true; } public Point xform(int x, int y) { return new Point(x / tnRatio, y / tnRatio); } public void xform(int inX[], int inY[], int points, int outX[], int outY[]) { for (int i = 0; i < points; i++) { outX[i] = inX[i] / tnRatio; outY[i] = inY[i] / tnRatio; } } public Dimension getSize() { return tnSize; } } } public class PaintTools extends Panel { ToolButton buttons[] = { new PenButton(FREEHAND), new PenButton(LINE), new ColorButton(Color.black), new ColorButton(Color.white), new ColorButton(Color.red), new ColorButton(Color.green), new ColorButton(Color.blue), new ColorButton(Color.yellow), new ColorButton(Color.orange), new ColorButton(Color.pink), new ColorButton(Color.cyan), new ColorButton(Color.gray) }; public PaintTools() { resize(toolsSize); for (int i = 0; i < buttons.length; i++) { add(buttons[i]); buttons[i].move(buttonBlank + (buttonBlank + buttonSize.width) * i, buttonBlank); } } public void updateTool() { for (int i = 0; i < buttons.length; i++) { buttons[i].repaint(); } } public class ToolButton extends Canvas { public ToolButton() { resize(buttonSize); } } public class PenButton extends ToolButton { int type; public PenButton(int type) { this.type = type; } public void paint(Graphics g) { g.setColor(Color.black); switch (type) { case LINE: g.drawLine(5, 5, 15, 15); break; case FREEHAND: g.drawPolyline(freeHandXData, freeHandYData, freeHandXData.length); break; } if (penType == type) { g.drawRect(0, 0, buttonSize.width - 1, buttonSize.height - 1); } g.drawRect(2, 2, buttonSize.width - 5, buttonSize.height - 5); } public boolean mouseDown(Event evt, int x, int y) { setPenType(type); updateTool(); return true; } } public class ColorButton extends ToolButton { Color color; public ColorButton(Color color) { this.color = color; } public void paint(Graphics g) { g.setColor(color); g.fillRect(2, 2, buttonSize.width - 5, buttonSize.height - 5); g.setColor(Color.black); g.drawRect(2, 2, buttonSize.width - 5, buttonSize.height - 5); if (state.getColor().equals(color)) { g.drawRect(0, 0, buttonSize.width - 1, buttonSize.height - 1); } } public boolean mouseDown(Event evt, int x, int y) { state.setColor(color); updateTool(); return true; } } } public class Line { public int x1, y1, x2, y2; private boolean end; public void newLine() { end = false; } public void setBegin(int x, int y) { x1 = x; y1 = y; } public void setEnd(int x, int y) { x2 = x; y2 = y; end = true; } public boolean endIsSet() { return end; } } public class FreeHand { static final int POINTS = 10; FreeHandSection first, current; int outx[], outy[]; int points; public void newLine() { points = 0; first = new FreeHandSection(); } public void put(int x, int y) { current.put(x, y); } public void prepareOutput() { int p = 0, end; outx = new int[points]; outy = new int[points]; for (FreeHandSection section = first; section != null; section = section.next) { end = (section == current) ? section.current : POINTS; for (int i = 0; i < end; i++) { outx[p] = section.xa[i]; outy[p] = section.ya[i]; p++; } } } public int[] getX() { return outx; } public int[] getY() { return outy; } public void drawTemp(Graphics g) { int end; for (FreeHandSection section = first; section != null; section = section.next) { end = (section == current) ? section.current : POINTS; g.drawPolyline(section.xa, section.ya, end); if (section != current) { g.drawLine(section.xa[POINTS - 1], section.ya[POINTS - 1], section.next.xa[0], section.next.ya[0]); } } } public class FreeHandSection { int xa[] = new int[POINTS]; int ya[] = new int[POINTS]; int current = 0; FreeHandSection next; public FreeHandSection() { FreeHand.this.current = this; } public void put(int x, int y) { if (current >= POINTS) { next = new FreeHandSection(); next.put(x, y); } else { xa[current] = x; ya[current] = y; current++; points++; } } } } }