/* 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. A menu can be popped up on a shape (by right-clicking or performing some othe platform-dependent action). This menu allows the user to change the size and color of a shape. It is also possible to delete the shape and to bring it to the front. This file defines the applet class plus several other classes used by the applet, namely: ShapeCanvas, Shape, RectShape, OvalShape, and RoundRectShape. David Eck July 28, 1998 */ import java.awt.*; import java.awt.event.*; import java.applet.Applet; import java.util.Vector; public class ShapeDrawWithMenu extends Applet { 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. (The pop-up menu is created by the canvas.) setBackground(Color.lightGray); ShapeCanvas canvas = new ShapeCanvas(); // create the canvas Choice colorChoice = new Choice(); // color choice menu colorChoice.add("Red"); colorChoice.add("Green"); colorChoice.add("Blue"); colorChoice.add("Cyan"); colorChoice.add("Magenta"); colorChoice.add("Yellow"); colorChoice.add("Black"); colorChoice.add("White"); colorChoice.addItemListener(canvas); Button rectButton = new Button("Rect"); // buttons for adding shapes rectButton.addActionListener(canvas); Button ovalButton = new Button("Oval"); ovalButton.addActionListener(canvas); Button roundRectButton = new Button("RoundRect"); roundRectButton.addActionListener(canvas); Panel bottom = new Panel(); // 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); setLayout(new BorderLayout(3,3)); add("Center",canvas); // add canvas and controls to the applet add("South",bottom); } public Insets getInsets() { // Says how much space to leave between the edges of the applet and the // components in the applet. return new Insets(3,3,3,3); } } // end class ShapeDraw class ShapeCanvas extends Canvas implements ActionListener, ItemListener, 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. A pop-up menu is // added to the canvas that can be used to performa certain actions // on the shapes; Image offScreenCanvas = null; // off-screen image used for double buffering Graphics offScreenGraphics; // graphics context for drawing to offScreenCanvas Vector shapes = new Vector(); // 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, // and set up the pop-up menu setBackground(Color.white); addMouseListener(this); addMouseMotionListener(this); popup = new PopupMenu(); popup.add("Red"); popup.add("Green"); popup.add("Blue"); popup.add("Cyan"); popup.add("Magenta"); popup.add("Yellow"); popup.add("Black"); popup.add("White"); popup.addSeparator(); popup.add("Big"); popup.add("Medium"); popup.add("Small"); popup.addSeparator(); popup.add("Delete"); popup.add("Bring To Front"); add(popup); popup.addActionListener(this); } // end construtor synchronized public void paint(Graphics g) { // In the paint method, everything is drawn to an off-screen canvas, and then // that canvas is copied onto the screen. makeOffScreenCanvas(); g.drawImage(offScreenCanvas,0,0,this); } public void update(Graphics g) { // Update method is called when canvas is to be redrawn. // Just call the paint method. paint(g); } void makeOffScreenCanvas() { // Erase the off-screen canvas and redraw all the shapes in the list. // (First, if canvas has not yet been created, then create it.) if (offScreenCanvas == null) { offScreenCanvas = createImage(getSize().width,getSize().height); offScreenGraphics = offScreenCanvas.getGraphics(); } offScreenGraphics.setColor(getBackground()); offScreenGraphics.fillRect(0,0,getSize().width,getSize().height); int top = shapes.size(); for (int i = 0; i < top; i++) { Shape s = (Shape)shapes.elementAt(i); s.draw(offScreenGraphics); } } public void itemStateChanged(ItemEvent evt) { // This is called to respond to item events. Such events // can only be sent by the color choice menu, // so respond by setting the current color according to // the selected item in that menu. Choice colorChoice = (Choice)evt.getItemSelectable(); switch (colorChoice.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; } } 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. This // also be a command from a pop-up menu. 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()); else doPopupMenuCommand(command); } synchronized 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 50-by-30. // Then redraw the canvas to show the newly added shape. shape.setColor(currentColor); shape.reshape(3,3,50,30); shapes.addElement(shape); repaint(); } // ------------ This rest of the class implements dragging and the pop-up menu --------------------- PopupMenu popup; Shape selectedShape = null; // This is null unless a menu has been popped up on this shape. Shape draggedShape = null; // This is null unless a shape has been selected for dragging. int prevDragX; // During dragging, these record the x and y coordinates of the int prevDragY; // previous position of the mouse. Shape clickedShape(int x, int y) { // Find the frontmost shape at coordinates (x,y); return null if there is none. for ( int i = shapes.size() - 1; i >= 0; i-- ) { // check shapes from front to back Shape s = (Shape)shapes.elementAt(i); if (s.containsPoint(x,y)) return s; } return null; } void doPopupMenuCommand(String command) { // Handle a command from the pop-up menu. if (selectedShape == null) // should be impossible return; if (command.equals("Red")) selectedShape.setColor(Color.red); else if (command.equals("Green")) selectedShape.setColor(Color.green); else if (command.equals("Blue")) selectedShape.setColor(Color.blue); else if (command.equals("Cyan")) selectedShape.setColor(Color.cyan); else if (command.equals("Magenta")) selectedShape.setColor(Color.magenta); else if (command.equals("Yellow")) selectedShape.setColor(Color.yellow); else if (command.equals("Black")) selectedShape.setColor(Color.black); else if (command.equals("White")) selectedShape.setColor(Color.white); else if (command.equals("Big")) selectedShape.resize(75,45); else if (command.equals("Medium")) selectedShape.resize(50,30); else if (command.equals("Small")) selectedShape.resize(25,15); else if (command.equals("Delete")) shapes.removeElement(selectedShape); else if (command.equals("Bring To Front")) { shapes.removeElement(selectedShape); shapes.addElement(selectedShape); } repaint(); } synchronized 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 if (evt.isPopupTrigger()) { // If this is a pop-up menu event that selectedShape = clickedShape(x,y); // occurred over a shape, record which shape if (selectedShape != null) // it is and show the menu. popup.show(this,x,y); } else { draggedShape = clickedShape(x,y); if (draggedShape != null) { prevDragX = x; prevDragY = y; if (evt.isShiftDown()) { // Bring the shape to the front by moving it to shapes.removeElement(draggedShape); // the end of the list of shapes. shapes.addElement(draggedShape); repaint(); // repaint canvas to show shape in front of other shapes } } } } synchronized public void mouseDragged(MouseEvent evt) { // User has moved the mouse. Move the dragged shape by the same amount. if (draggedShape != null) { int x = evt.getX(); int y = evt.getY(); draggedShape.moveBy(x - prevDragX, y - prevDragY); prevDragX = x; prevDragY = y; repaint(); // redraw canvas to show shape in new position } } synchronized 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 (draggedShape != null) { draggedShape.moveBy(x - prevDragX, y - prevDragY); if ( draggedShape.left >= getSize().width || draggedShape.top >= getSize().height || draggedShape.left + draggedShape.width < 0 || draggedShape.top + draggedShape.height < 0 ) { // shape is off-screen shapes.removeElement(draggedShape); // remove shape from list of shapes } draggedShape = null; repaint(); } else if (evt.isPopupTrigger()) { // If this is a pop-up menu event that selectedShape = clickedShape(x,y); // occurred over a shape, record the if (selectedShape != null) // shape and show the menu. popup.show(this,x,y); } } 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 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 resize(int width,int height) { // Set the size without changing the position this.width = width; this.height = height; } void moveTo(int x, int y) { // Move upper left corner to the point (x,y) this.left = x; this.top = y; } 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 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); } } 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; } } 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); } }