/* The ShapeDraw applet lets the user add small colored shapes to a drawing area and then drag them around. The shapes are rectangles, ovals, and roundrects. The user adds a shape to the canvas by clicking on a button. The shape is added at the upper left corner of the canvas. The color of the shape is given by the current setting of a pop-up menu. The user can drag the shapes with the mouse. Ordinarily, the shapes maintain a given back-to-front order. However, if the user shift-clicks on a shape, that shape will be brought to the front. This file defines the applet class plus several other classes used by the applet, namely: ShapeCanvas, Shape, RectShape, OvalShape, and RoundRectShape. These classes are nested inside the main class, so the compiled class files for these classes will have named like ShapeDraw$ShapeCanvas.class. All the class files are necessary to run the applet. This applet requires Java version 1.2 or higher. David Eck Original veraion July 28, 1998 Revised May 18, 2002 to use Swing and ArrayList and to use nested classes. */ import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.ArrayList; public class ShapeDraw extends JApplet { public void init() { // Set up the applet's GUI. It consists of a canvas, or drawing area, // plus a row of controls below the canvas. The controls include three // buttons which are used to add shapes to the canvas and a Choice menu // that is used to select the color used for a shape when it is created. // The canvas is set as the "listener" for these controls so that it can // respond to the user's actions. setBackground(Color.lightGray); ShapeCanvas canvas = new ShapeCanvas(); // create the canvas JComboBox colorChoice = new JComboBox(); // color choice menu colorChoice.addItem("Red"); colorChoice.addItem("Green"); colorChoice.addItem("Blue"); colorChoice.addItem("Cyan"); colorChoice.addItem("Magenta"); colorChoice.addItem("Yellow"); colorChoice.addItem("Black"); colorChoice.addItem("White"); colorChoice.addActionListener(canvas); JButton rectButton = new JButton("Rect"); // buttons for adding shapes rectButton.addActionListener(canvas); JButton ovalButton = new JButton("Oval"); ovalButton.addActionListener(canvas); JButton roundRectButton = new JButton("RoundRect"); roundRectButton.addActionListener(canvas); JPanel bottom = new JPanel(); // a Panel to hold the control buttons bottom.setLayout(new GridLayout(1,4,3,3)); bottom.add(rectButton); bottom.add(ovalButton); bottom.add(roundRectButton); bottom.add(colorChoice); getContentPane().setLayout(new BorderLayout(3,3)); getContentPane().add("Center",canvas); // add canvas and controls to the applet getContentPane().add("South",bottom); } // end init() public Insets getInsets() { // Allows 3 pixels of extra space around the edges of the applet // that will appear in the background color. return new Insets(3,3,3,3); } // end getInsets() //---- Nested class definitions --- // // The remainder of the ShapeDraw class consists of static nested class definitions. // These are just like regular classes, except that they are defined inside // another class (and hence have full names, when used outside this class, such // as ShapeDraw.ShapeCanvas). static class ShapeCanvas extends JPanel implements ActionListener, MouseListener, MouseMotionListener { // This class represents a "canvas" that can display colored shapes and // let the user drag them around. It uses an off-screen images to // make the dragging look as smooth as possible. ArrayList shapes = new ArrayList(); // holds a list of the shapes that are displayed on the canvas Color currentColor = Color.red; // current color; when a shape is created, this is its color ShapeCanvas() { // Constructor: set background color to white set up listeners to respond to mouse actions setBackground(Color.white); addMouseListener(this); addMouseMotionListener(this); } public void paintComponent(Graphics g) { // In the paint method, all the shapes in ArrayList are // copied onto the canvas. g.setColor(getBackground()); g.fillRect(0,0,getSize().width,getSize().height); int top = shapes.size(); for (int i = 0; i < top; i++) { Shape s = (Shape)shapes.get(i); s.draw(g); } } public void actionPerformed(ActionEvent evt) { // Called to respond to action events. The three shape-adding // buttons have been set up to send action events to this canvas. // Respond by adding the appropriate shape to the canvas. if (evt.getSource() instanceof JComboBox) { switch ( ((JComboBox)evt.getSource()).getSelectedIndex() ) { case 0: currentColor = Color.red; break; case 1: currentColor = Color.green; break; case 2: currentColor = Color.blue; break; case 3: currentColor = Color.cyan; break; case 4: currentColor = Color.magenta; break; case 5: currentColor = Color.yellow; break; case 6: currentColor = Color.black; break; case 7: currentColor = Color.white; break; } } else { String command = evt.getActionCommand(); if (command.equals("Rect")) addShape(new RectShape()); else if (command.equals("Oval")) addShape(new OvalShape()); else if (command.equals("RoundRect")) addShape(new RoundRectShape()); } } void addShape(Shape shape) { // Add the shape to the canvas, and set its size/position and color. // The shape is added at the top-left corner, with size 80-by-50. // Then redraw the canvas to show the newly added shape. shape.setColor(currentColor); shape.reshape(3,3,80,50); shapes.add(shape); repaint(); } // -------------------- This rest of this class implements dragging ---------------------- Shape shapeBeingDragged = null; // This is null unless a shape is being dragged. // A non-null value is used as a signal that dragging // is in progress, as well as indicating which shape // is being dragged. int prevDragX; // During dragging, these record the x and y coordinates of the int prevDragY; // previous position of the mouse. public void mousePressed(MouseEvent evt) { // User has pressed the mouse. Find the shape that the user has clicked on, if // any. If there is a shape at the position when the mouse was clicked, then // start dragging it. If the user was holding down the shift key, then bring // the dragged shape to the front, in front of all the other shapes. int x = evt.getX(); // x-coordinate of point where mouse was clicked int y = evt.getY(); // y-coordinate of point for ( int i = shapes.size() - 1; i >= 0; i-- ) { // check shapes from front to back Shape s = (Shape)shapes.get(i); if (s.containsPoint(x,y)) { shapeBeingDragged = s; prevDragX = x; prevDragY = y; if (evt.isShiftDown()) { // Bring the shape to the front by moving it to shapes.remove(s); // the end of the list of shapes. shapes.add(s); repaint(); // repaint canvas to show shape in front of other shapes } return; } } } public void mouseDragged(MouseEvent evt) { // User has moved the mouse. Move the dragged shape by the same amount. int x = evt.getX(); int y = evt.getY(); if (shapeBeingDragged != null) { shapeBeingDragged.moveBy(x - prevDragX, y - prevDragY); prevDragX = x; prevDragY = y; repaint(); // redraw canvas to show shape in new position } } public void mouseReleased(MouseEvent evt) { // User has released the mouse. Move the dragged shape, then set // shapeBeingDragged to null to indicate that dragging is over. // If the shape lies completely outside the canvas, remove it // from the list of shapes (since there is no way to ever move // it back onscreen). int x = evt.getX(); int y = evt.getY(); if (shapeBeingDragged != null) { shapeBeingDragged.moveBy(x - prevDragX, y - prevDragY); if ( shapeBeingDragged.left >= getSize().width || shapeBeingDragged.top >= getSize().height || shapeBeingDragged.left + shapeBeingDragged.width < 0 || shapeBeingDragged.top + shapeBeingDragged.height < 0 ) { // shape is off-screen shapes.remove(shapeBeingDragged); // remove shape from list of shapes } shapeBeingDragged = null; repaint(); } } public void mouseEntered(MouseEvent evt) { } // Other methods required for MouseListener and public void mouseExited(MouseEvent evt) { } // MouseMotionListener interfaces. public void mouseMoved(MouseEvent evt) { } public void mouseClicked(MouseEvent evt) { } } // end class ShapeCanvas // ------- Nested class definitions for the abstract Shape class and three ----- // -------------------- concrete subclasses of Shape. -------------------------- static abstract class Shape { // A class representing shapes that can be displayed on a ShapeCanvas. // The subclasses of this class represent particular types of shapes. // When a shape is first constucted, it has height and width zero // and a default color of white. int left, top; // Position of top left corner of rectangle that bounds this shape. int width, height; // Size of the bounding rectangle. Color color = Color.white; // Color of this shape. void reshape(int left, int top, int width, int height) { // Set the position and size of this shape. this.left = left; this.top = top; this.width = width; this.height = height; } void moveBy(int dx, int dy) { // Move the shape by dx pixels horizontally and dy pixels veritcally // (by changing the position of the top-left corner of the shape). left += dx; top += dy; } void setColor(Color color) { // Set the color of this shape this.color = color; } boolean containsPoint(int x, int y) { // Check whether the shape contains the point (x,y). // By default, this just checks whether (x,y) is inside the // rectangle that bounds the shape. This method should be // overridden by a subclass if the default behaviour is not // appropriate for the subclass. if (x >= left && x < left+width && y >= top && y < top+height) return true; else return false; } abstract void draw(Graphics g); // Draw the shape in the graphics context g. // This must be overriden in any concrete subclass. } // end of class Shape static class RectShape extends Shape { // This class represents rectangle shapes. void draw(Graphics g) { g.setColor(color); g.fillRect(left,top,width,height); g.setColor(Color.black); g.drawRect(left,top,width,height); } } static class OvalShape extends Shape { // This class represents oval shapes. void draw(Graphics g) { g.setColor(color); g.fillOval(left,top,width,height); g.setColor(Color.black); g.drawOval(left,top,width,height); } boolean containsPoint(int x, int y) { // Check whether (x,y) is inside this oval, using the // mathematical equation of an ellipse. double rx = width/2.0; // horizontal radius of ellipse double ry = height/2.0; // vertical radius of ellipse double cx = left + rx; // x-coord of center of ellipse double cy = top + ry; // y-coord of center of ellipse if ( (ry*(x-cx))*(ry*(x-cx)) + (rx*(y-cy))*(rx*(y-cy)) <= rx*rx*ry*ry ) return true; else return false; } } static class RoundRectShape extends Shape { // This class represents rectangle shapes with rounded corners. // (Note that it uses the inherited version of the // containsPoint(x,y) method, even though that is not perfectly // accurate when (x,y) is near one of the corners.) void draw(Graphics g) { g.setColor(color); g.fillRoundRect(left,top,width,height,width/3,height/3); g.setColor(Color.black); g.drawRoundRect(left,top,width,height,width/3,height/3); } } } // end class ShapeDraw