import javax.swing.*; import javax.swing.event.*; import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import java.util.*; /**** * * Class DrawingCanvas defines a drawing canvas as a JPanel with a specialized * paint method. See the Java tutorial sections on the * overview of custom painting and Java 2D * rendering for the basics of how to specialize the paintComponent method * for drawing. * * @author Gene Fisher (gfisher@calpoly.edu) * @version 18may10 * */ public class DrawingCanvas extends JPanel { /** Reference to the WorkSpace model, sent in from above */ private WorkSpace workSpace; /** * The java draing area. */ private Graphics2D g2; /** The starting x-axis position for drawing a shape */ private int startX; /** The starting y-axis position for drawing a shape */ private int startY; /** Quadrant-I normalized value of startY */ private int nStartY; /** True if the user is moving the mouse. */ private boolean moving = false; SelectingMovingListener selectorMover; LineDrawingListener lineDrawer; RectDrawingListener rectDrawer; EllipseDrawingListener ellipseDrawer; PolyDrawingListener polyDrawer; TextDrawingListener textDrawer; MouseInputAdapter currentListener; /** * Construct this with the given shape list model. */ DrawingCanvas(WorkSpace workSpace) { /* * Initialize the model reference and toolar. */ this.workSpace = workSpace; /* * Construct the tool listeners. */ selectorMover = new SelectingMovingListener(this, workSpace); lineDrawer = new LineDrawingListener(this, workSpace); rectDrawer = new RectDrawingListener(this, workSpace); ellipseDrawer = new EllipseDrawingListener(this, workSpace); polyDrawer = new PolyDrawingListener(this, workSpace); textDrawer = new TextDrawingListener(this, workSpace); /* * Set the tool to "Select". */ setListener("Select"); /* * Set the layout to null. As note elsewhere, this is key * to how the text tool works. */ setLayout(null); } /** * Set the listener to that responsible for performing the function of the * clicker = new given tool. This method is called from the tool bar */ public void setListener(String toolName) { if (toolName == null) { return; } // System.out.println("Selected tool = " + toolName); if (toolName == "Select") { replaceListener(selectorMover); } if (toolName == "Line") { replaceListener(lineDrawer); } else if (toolName == "Rectangle") { replaceListener(rectDrawer); } else if (toolName == "Ellipse") { replaceListener(ellipseDrawer); } else if (toolName == "Polygon") { replaceListener(polyDrawer); } else if (toolName == "Text") { replaceListener(textDrawer); } // System.out.println("num listeners = " + // (getListeners(MouseListener.class).length + // getListeners(MouseMotionListener.class).length)); } /** * Remove the current mouse and mouse motion listerner, then add the given * listener as the current. */ private void replaceListener(MouseInputAdapter listener) { if (currentListener != null) { removeMouseListener(currentListener); removeMouseMotionListener(currentListener); } addMouseListener(listener); addMouseMotionListener(listener); currentListener = listener; } /** * Draw a base rectangle and each rectangle in parent.workSpace. Model * rectangles are added to the workSpace by pressing the "Add Rect" * button in the top-level view. */ public void paintComponent(Graphics g) { /* * Call the parent JPanel paintComponent to paint the background. */ super.paintComponent(g); /* * Cast the incoming Graphics object to a Graphics2D. This is the * standard way to do things. */ g2 = (Graphics2D) g; /* * Turn anti-aliasing on */ g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); /* * Draw the shapes in the work space. */ for (int i = 0; i < workSpace.size(); i++) { Shape shape = workSpace.get(i); if (shape instanceof Line) { drawLine((Line)shape); } // Needs to come before Rectangle, of which it's a subclass else if (shape instanceof Text) { drawText((Text)shape); } else if (shape instanceof Rectangle) { drawRectangle((Rectangle)shape); } else if (shape instanceof Ellipse) { drawEllipse((Ellipse)shape); } else if (shape instanceof ConvexPolygon) { drawConvexPolygon((ConvexPolygon)shape); } /* * Return the default color to black, in case it was set in one of * the drawing methods. */ g2.setColor(Color.BLACK); } } private void drawLine(Line l) { int nx1; // normalized x coord (same as unnormalized) int ny1; // normalized y coord int nx2; // normalized x coord (same as unnormalized) int ny2; // normalized y coord int ch; // canvas height, for normalization /* * Get l's coordinates, normalizing the y's. */ ch = getHeight(); nx1 = l.p1.x; ny1 = ch - l.p1.y; nx2 = l.p2.x; ny2 = ch - l.p2.y; /* * Draw l; note that the fill does not apply to a line. */ g2.setColor(l.getColor()); g2.drawLine(nx1, ny1, nx2, ny2); if ((! moving) && l.selected) { drawHandles(nx1, ny1, nx2 - nx1, ny2 - ny1); } } private void drawRectangle(Rectangle r) { int nx; // normalized x coord (same as unnormalized) int ny; // normalized y coord int ch; // canvas height, for normalization int w; // rectangle width int h; // rectangle height /* * Get r's coordinates, normalizing y. */ ch = getHeight(); nx = r.getPosition().x; ny = ch - r.getPosition().y; /* * Get the height and width. */ w = (int) r.getWidth(); h = (int) r.getHeight(); /* * Draw r filled or not filled, as appro. */ g2.setColor(r.getColor()); if (r.getFilled()) { g2.fillRect(nx, ny, w, h); } else { g2.drawRect(nx, ny, w, h); } if ((! moving) && r.selected) { drawHandles(nx, ny, w, h); } } /** * Model has x,y in center of circle, in quadrant 1. Java wants x,y at * upper left corner of framing rect, in quadrant 4. */ private void drawEllipse(Ellipse e) { int nx; // normalized x coord (same as unnormalized) int ny; // normalized y coord int ch; // canvas height, for normalization double maj; // semi-major axis double min; // semi-minor axis /* * Normalize qudrants. */ ch = getHeight(); nx = e.getPosition().x; ny = ch - e.getPosition().y; /* * Normalize to upper left corner of framing rect. */ maj = e.getSemiMajorAxis(); min = e.getSemiMinorAxis(); nx -= maj; ny -= min; g2.setColor(e.getColor()); if (e.getFilled()) { g2.fillOval(nx, ny, (int) (maj * 2), (int) (min * 2)); } else { g2.drawOval(nx, ny, (int) (maj * 2), (int) (min * 2)); } if ((! moving) && e.selected) { drawHandles(nx, ny, (int) maj * 2, (int) min * 2); } } private void drawConvexPolygon(ConvexPolygon p) { int ch; // canvas height, for normalization int n = p.numVertices(); // number of vertices int[] xs= new int[n]; // array of vertex x coords int[] nys = new int[n]; // array normalized vertex y coords /* * Populate the x/y arrays, normalizing the y coords. */ ch = getHeight(); for (int i = 0; i < n; i++) { xs[i] = p.getVertex(i).x; nys[i] = ch - p.getVertex(i).y; } g2.setColor(p.getColor()); if (p.getFilled()) { g2.fillPolygon(xs, nys, n); } else { g2.drawPolygon(xs, nys, n); } // System.out.println("moving = " + moving + " " + p.selected); if ((! moving) && p.selected) { java.awt.Rectangle br = (new Polygon(xs, nys, n)).getBounds(); drawHandles(br.x, br.y, br.width, br.height); } } private void drawText(Text t) { int nx; // normalized x coord (same as unnormalized) int ny; // normalized y coord int ch; // canvas height, for normalization String s; // the text string /* * Get t's coordinates, normalizing y. */ ch = getHeight(); nx = t.x; ny = ch - t.y; /* * Get the string and its to-be-rendered bounding rect. */ s = t.s; Rectangle2D br = g2.getFont().getStringBounds(s, g2.getFontRenderContext()); /* * Draw t; unfilled is the only way. */ g2.setColor(t.getColor()); g2.drawString(s, nx, ny = t.y + (int) br.getHeight()); // System.out.println(g2.getFont().getSize()); int height_fudge = 3; if ((! moving) && t.selected) { drawHandles(nx, t.y, (int) br.getWidth(), (int) br.getHeight() + height_fudge); // p(t.getArea() + " " + (br.getWidth() * br.getHeight())); } } /** * Draw eight 4x4 rectangles on the corners and edge midpoints of the * rectangle defined by the given x,y,width,height parameters. */ private void drawHandles(int nx, int ny, int w, int h) { /* * Debug by setting handles to different colors. */ int lbx = nx - 2; int lby = ny + h - 2; g2.setColor(Color.BLACK); // RED); g2.fillRect(lbx, lby, 4, 4); int bx = nx + w/2 - 2; int by = lby; g2.setColor(Color.BLACK); // ORANGE); g2.fillRect(bx, by, 4, 4); int rbx = nx + w - 2; int rby = lby; g2.setColor(Color.BLACK); // YELLOW); g2.fillRect(rbx, rby, 4, 4); int rx = rbx; int ry = rby - h/2; g2.setColor(Color.BLACK); // GREEN); g2.fillRect(rx, ry, 4, 4); int rtx = rbx; int rty = rby - h; g2.setColor(Color.BLACK); // BLUE); g2.fillRect(rtx, rty, 4, 4); int tx = rtx - w/2; int ty = rty; g2.setColor(Color.BLACK); // MAGENTA); g2.fillRect(tx, ty, 4, 4); int ltx = lbx; int lty = rty; g2.setColor(Color.BLACK); // GRAY); g2.fillRect(ltx, lty, 4, 4); int lx = ltx; int ly = ry; g2.setColor(Color.BLACK); // BLACK); g2.fillRect(lx, ly, 4, 4); } Graphics2D getGraphics2D() { return g2; } void p(String s) {System.out.println(s);} }