
import java.util.Vector;

import java.awt.Container;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Dimension;

import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.AffineTransform;

import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseEvent;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JMenuBar;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JRadioButton;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.ButtonGroup;
import javax.swing.BoxLayout;
import javax.swing.BorderFactory;

class MyShape {
	public static boolean enableCompositing = false;

	public static final int TRIANGLE = 0;
	public static final int RECTANGLE = 1;
	public static final int ELLIPSE = 2;

	public int type; // TRIANGLE, RECTANGLE, or ELLIPSE
	public Color color; // may have a non-zero alpha for compositing
	public float rotationAngle; // clockwise, in radians
	public float x_center, y_center; // center the shape, and center of rotation

	// These are used for a triangle.
	// The average of these three points is (x_center, y_center).
	public float [] xpoints = new float[3];
	public float [] ypoints = new float[3];

	// These are used for rectangle and ellipse.
	// They are the dimensions of the shape *before* rotation.
	public float width, height;

	public void translate( float deltax, float deltay ) {
		x_center += deltax;
		y_center += deltay;
		if ( type == TRIANGLE ) {
			for ( int i = 0; i < 3; ++i ) {
				xpoints[i] += deltax;
				ypoints[i] += deltay;
			}
		}
	}

	public void draw( Graphics2D g2, boolean isFilled, boolean isHilited ) {
		Color c = new Color(
			isHilited ? 255-(255-color.getRed())/2 : color.getRed(),
			isHilited ? 255-(255-color.getGreen())/2 : color.getGreen(),
			isHilited ? 255-(255-color.getBlue())/2 : color.getBlue(),
			enableCompositing ? color.getAlpha() : 255
		);

		AffineTransform originalTransform = g2.getTransform();
		if ( rotationAngle != 0 ) {
			// apply rotation around the center of the shape
			AffineTransform newTransform = new AffineTransform();
			newTransform.translate( x_center, y_center );
			newTransform.rotate( rotationAngle );
			newTransform.translate( -x_center, -y_center );
			g2.transform( newTransform );
		}
		switch (type) {
			case TRIANGLE: {
				GeneralPath p = new GeneralPath();
				p.moveTo(xpoints[0],ypoints[0]);
				p.lineTo(xpoints[1],ypoints[1]);
				p.lineTo(xpoints[2],ypoints[2]);
				p.closePath();
				if ( isFilled ) {
					g2.setColor( c );
					g2.fill( p );
				}
				g2.setColor( isHilited ? Color.black : Color.gray );
				g2.draw( p );
			} break;
			case RECTANGLE: {
				Rectangle2D r = new Rectangle2D.Double(
					x_center-width/2, y_center-height/2, width, height
				);
				if ( isFilled ) {
					g2.setColor( c );
					g2.fill( r );
				}
				g2.setColor( isHilited ? Color.black : Color.gray );
				g2.draw( r );
			} break;
			case ELLIPSE: {
				Ellipse2D e = new Ellipse2D.Double(
					x_center-width/2, y_center-height/2, width, height
				);
				if ( isFilled ) {
					g2.setColor( c );
					g2.fill( e );
				}
				g2.setColor( isHilited ? Color.black : Color.gray );
				g2.draw( e );
			} break;
		}
		// restore the original transform
		g2.setTransform(originalTransform);
	}

	// Performs a "left-hand-turn" test.
	//
	// Given two vectors a and b
	// that lie on the xy plane of a right-handed 3-space,
	// b is a left-hand-turn with respect to a
	// if the z component of (a x b) is positive.
	// Equivalently, given a directed line segment ab and a point p
	// that lie on the xy plane of a right-handed 3-space,
	// p is on the left-hand-side of ab
	// if the z component of (b-a)x(p-b) is positive.
	//
	private static boolean leftHandTest(
		float ax, float ay, float bx, float by, float px, float py
	) {
		return ((bx)-(ax))*((py)-(by)) - ((by)-(ay))*((px)-(bx)) > 0;
	}

	boolean isPointInsideShape( float x, float y ) {
		if ( type == RECTANGLE || type == ELLIPSE ) {
			AffineTransform newTransform = new AffineTransform();
			newTransform.rotate( -rotationAngle );
			newTransform.translate( -x_center, -y_center );
			Point2D.Float p1 = new Point2D.Float( x, y );
			Point2D.Float p2 = new Point2D.Float();
			newTransform.transform( p1, p2 );
			if ( type == RECTANGLE ) {
				if ( Math.abs(p2.getX()) <= width/2 && Math.abs(p2.getY()) <= height/2 )
					return true;
			}
			else if ( type == ELLIPSE ) {
				double x_over_a = p2.getX() / (width/2);
				double y_over_b = p2.getY() / (height/2);

				if ( x_over_a*x_over_a + y_over_b*y_over_b <= 1 )
					return true;
			}
		}
		else if ( type == TRIANGLE ) {
			// Do three "left-hand-turn" tests
			// to see if the intersection point is inside the triangle.
			// If all 3 tests turn out false, or all true,
			// then the point is inside the triangle.
			boolean test1 = leftHandTest(xpoints[0],ypoints[0],xpoints[1],ypoints[1],x,y);
			boolean test2 = leftHandTest(xpoints[1],ypoints[1],xpoints[2],ypoints[2],x,y);
			boolean test3 = leftHandTest(xpoints[2],ypoints[2],xpoints[0],ypoints[0],x,y);
			if ( test1 == test2 && test2 == test3 ) {
				return true;
			}
		}
		return false;
	}
}

class MyCanvas extends JPanel implements MouseListener, MouseMotionListener {

	SimplePaint simplePaint;

	ColorPaletteWidget colorPalette = new ColorPaletteWidget();
	public void setColorPaletteVisible( boolean flag ) {
		colorPalette.setVisible( flag );
	}

	RadialMenuWidget radialMenu = new RadialMenuWidget();

	// Stores all the shapes on the canvas,
	// except for any shape currently being created.
	Vector< MyShape > shapes = new Vector< MyShape >();

	int mouse_x, mouse_y, old_mouse_x, old_mouse_y;

	// These are used during creation of a new shape.
	//
	boolean is2ndPointBeingDraggedOut = false;
	boolean is3rdPointBeingDraggedOut = false;
	int x1, y1, x2, y2, x3, y3;
	MyShape shapeBeingDraggedOut = null;

	// Used for moving a shape that's already been created.
	int currentlyHilitedShape = -1; // -1 means none
	boolean isHilitedShapeBeingMoved = false;

	// Used for scrolling.
	//
	float offset_x = 0;
	float offset_y = 0;
	boolean isScrolling = false;

	// Computes the angle of rotation from a to b around the origin,
	// allowing for changes in radius.
	// The y axis increases downward,
	// and clockwise rotations correspond to positive angles.
	// The angle returned is in the interval [-pi,pi] (I think)
	static float computeAngle(
		float a_x,
		float a_y,
		float b_x,
		float b_y
	) {
		// Compute the dot product of a and b
		double dotProduct = a_x*b_x + a_y*b_y;

		// Compute the z component of the cross product of a and b
		// (Note that the x and y components of the cross product are zero,
		// because the z components of a and b are both zero)
		double crossProduct_z = a_x*b_y - a_y*b_x;

		double sineOfAngle = Math.abs(crossProduct_z)
			/ ( Math.sqrt(a_x*a_x+a_y*a_y) * Math.sqrt(b_x*b_x+b_y*b_y) );

		// Due to numerical inaccuracies, the sine we computed
		// may be slightly more than 1.
		// Calling arcsin on such a value could be bad, so we don't.
		double angle = ( sineOfAngle >= 1 ) ? Math.PI/2 : Math.asin( sineOfAngle );

		if ( dotProduct < 0 )
			angle = Math.PI - angle;

		if ( crossProduct_z < 0 )
			angle = - angle;

		return (float)angle;
	}

	public MyCanvas( SimplePaint sp ) {
		simplePaint = sp;
		setBorder( BorderFactory.createLineBorder( Color.black ) );
		setBackground( Color.white );
		addMouseListener( this );
		addMouseMotionListener( this );

		radialMenu.setItemLabelAndID( RadialMenuWidget.CENTRAL_ITEM,           "",            sp.TOOL_SELECT_AND_MOVE );
		radialMenu.setItemLabelAndID( 1, sp.toolNames[ sp.TOOL_SELECT_AND_MOVE  ],            sp.TOOL_SELECT_AND_MOVE );
		radialMenu.setItemLabelAndID( 2, sp.toolNames[ sp.TOOL_CREATE_RECTANGLE ],            sp.TOOL_CREATE_RECTANGLE );
		radialMenu.setItemLabelAndID( 3, sp.toolNames[ sp.TOOL_CREATE_SQUARE    ],            sp.TOOL_CREATE_SQUARE );
		radialMenu.setItemLabelAndID( 4, sp.toolNames[ sp.TOOL_CREATE_ELLIPSE   ],            sp.TOOL_CREATE_ELLIPSE );
		radialMenu.setItemLabelAndID( 5, sp.toolNames[ sp.TOOL_CREATE_CIRCLE    ],            sp.TOOL_CREATE_CIRCLE );
		radialMenu.setItemLabelAndID( 6, sp.toolNames[ sp.TOOL_CREATE_TRIANGLE  ],            sp.TOOL_CREATE_TRIANGLE );
		radialMenu.setItemLabelAndID( 7, sp.toolNames[ sp.TOOL_CREATE_EQUILATERAL_TRIANGLE ], sp.TOOL_CREATE_EQUILATERAL_TRIANGLE );

	}
	public Dimension getPreferredSize() {
		return new Dimension( 512, 512 );
	}
	public void clear() {
		shapes.clear();
		repaint();
	}
	public void paintComponent( Graphics g ) {
		super.paintComponent( g );
		Graphics2D g2 = (Graphics2D)g;
		AffineTransform originalTransform = g2.getTransform();
		AffineTransform newTransform = new AffineTransform();
		newTransform.translate( offset_x, offset_y );
		g2.transform( newTransform );

		for ( int i = 0; i < shapes.size(); ++i ) {
			MyShape shape = shapes.elementAt(i);
			shape.draw( g2, true, i == currentlyHilitedShape );
		}
		if ( is2ndPointBeingDraggedOut || is3rdPointBeingDraggedOut ) {
			assert shapeBeingDraggedOut != null;
			shapeBeingDraggedOut.draw( g2, false, true );
		}

		g2.setTransform(originalTransform);

		if ( colorPalette.isVisible() )
			colorPalette.draw( g, getWidth(), getHeight() );
		if ( radialMenu.isVisible() )
			radialMenu.draw( g, getWidth(), getHeight() );
	}

	private static void usePointsSpecifiedByMouseToComputeShape(
		int currentTool,
		float x1, float y1, float x2, float y2, float x3, float y3,
		Color color,
		MyShape s
	) {
		float delta_x, delta_y, diagonal;
		s.color = color;
		switch ( currentTool ) {
			case SimplePaint.TOOL_CREATE_CIRCLE:
				s.type = MyShape.ELLIPSE;
				s.x_center = x1;
				s.y_center = y1;
				s.rotationAngle = 0;
				delta_x = x2 - x1;
				delta_y = y2 - y1;
				s.width = s.height = 2*(float)Math.sqrt(delta_x*delta_x + delta_y*delta_y);
				break;
			case SimplePaint.TOOL_CREATE_SQUARE:
				s.type = MyShape.RECTANGLE;
				s.x_center = (x1+x2)/2;
				s.y_center = (y1+y2)/2;
				delta_x = x2 - x1;
				delta_y = y2 - y1;
				diagonal = (float)Math.sqrt(delta_x*delta_x + delta_y*delta_y);
				s.width = s.height = diagonal/(float)Math.sqrt(2);
				s.rotationAngle = computeAngle(
					1, 0,
					delta_x, delta_y
				);
				s.rotationAngle -= (float)Math.PI/4;
				break;
			case SimplePaint.TOOL_CREATE_RECTANGLE:
			case SimplePaint.TOOL_CREATE_ELLIPSE:
				if ( currentTool == SimplePaint.TOOL_CREATE_RECTANGLE )
					s.type = MyShape.RECTANGLE;
				else
					s.type = MyShape.ELLIPSE;
				s.x_center = (x1+x2)/2;
				s.y_center = (y1+y2)/2;
				s.width = Math.abs(x2 - x1);
				s.height = Math.abs(y2 - y1);

				s.rotationAngle = computeAngle(
					x2 - s.x_center, y2 - s.y_center,
					x3 - s.x_center, y3 - s.y_center
				);
				break;
			case SimplePaint.TOOL_CREATE_EQUILATERAL_TRIANGLE:
				s.type = MyShape.TRIANGLE;

				// change the 3rd point to enforce an equilateral triangle
				float delta_x12 = x2 - x1;
				float delta_y12 = y2 - y1;
				float midpoint_x12 = x1 + delta_x12/2;
				float midpoint_y12 = y1 + delta_y12/2;
				float tangent = (float)Math.tan( Math.PI/3 );
				x3 = midpoint_x12 + tangent * delta_y12/2;
				y3 = midpoint_y12 - tangent * delta_x12/2;

				s.rotationAngle = 0;
				s.xpoints[0] = x1;
				s.xpoints[1] = x2;
				s.xpoints[2] = x3;
				s.ypoints[0] = y1;
				s.ypoints[1] = y2;
				s.ypoints[2] = y3;
				s.x_center = (x1+x2+x3)/3;
				s.y_center = (y1+y2+y3)/3;
				break;
			case SimplePaint.TOOL_CREATE_TRIANGLE:
				s.type = MyShape.TRIANGLE;
				s.rotationAngle = 0;
				s.xpoints[0] = x1;
				s.xpoints[1] = x2;
				s.xpoints[2] = x3;
				s.ypoints[0] = y1;
				s.ypoints[1] = y2;
				s.ypoints[2] = y3;
				s.x_center = (x1+x2+x3)/3;
				s.y_center = (y1+y2+y3)/3;
				break;
		}
	}

	private static int numPointsUsedToComputeShape( int currentTool ) {
		switch ( currentTool ) {
			case SimplePaint.TOOL_CREATE_SQUARE:
			case SimplePaint.TOOL_CREATE_CIRCLE:
				return 2;
			case SimplePaint.TOOL_CREATE_RECTANGLE:
			case SimplePaint.TOOL_CREATE_ELLIPSE:
				return 3;
			case SimplePaint.TOOL_CREATE_EQUILATERAL_TRIANGLE:
				return 2;
			case SimplePaint.TOOL_CREATE_TRIANGLE:
				return 3;
		}
		return -1; // error: unknown shape
	}

	public void mouseClicked( MouseEvent e ) { }
	public void mouseEntered( MouseEvent e ) { }
	public void mouseExited( MouseEvent e ) { }

	private void completeCreationOfShape() {
		shapes.addElement( shapeBeingDraggedOut );
		shapeBeingDraggedOut = null;
		is2ndPointBeingDraggedOut = false;
		is3rdPointBeingDraggedOut = false;
	}

	private void computeHilitedShape() {
		for ( int i = shapes.size()-1; i >= 0; --i ) {
			MyShape shape = shapes.elementAt(i);
			if ( shape.isPointInsideShape(
				mouse_x-offset_x,
				mouse_y-offset_y
			) ) {
				if ( currentlyHilitedShape != i ) {
					currentlyHilitedShape = i;
					repaint();
				}
				return;
			}
		}
		// no shape was found to be under the mouse
		if ( currentlyHilitedShape != -1 ) {
			currentlyHilitedShape = -1;
			repaint();
		}
	}

	public void mousePressed( MouseEvent e ) {
		old_mouse_x = mouse_x;
		old_mouse_y = mouse_y;
		mouse_x = e.getX();
		mouse_y = e.getY();

		if ( radialMenu.isVisible() || (SwingUtilities.isLeftMouseButton(e) && e.isControlDown()) ) {
			int returnValue = radialMenu.pressEvent( mouse_x, mouse_y );
			if ( returnValue == CustomWidget.S_REDRAW )
				repaint();
			if ( returnValue != CustomWidget.S_EVENT_NOT_CONSUMED )
				return;
		}
		if ( colorPalette.isVisible() ) {
			int returnValue = colorPalette.pressEvent( mouse_x, mouse_y );
			if ( returnValue == CustomWidget.S_REDRAW )
				repaint();
			if ( returnValue != CustomWidget.S_EVENT_NOT_CONSUMED )
				return;
		}
		if (
			is2ndPointBeingDraggedOut
			|| is3rdPointBeingDraggedOut
			|| isHilitedShapeBeingMoved
			|| isScrolling
		) {
			return;
		}
		if ( SwingUtilities.isLeftMouseButton(e) ) {
			if ( simplePaint.currentTool == SimplePaint.TOOL_SELECT_AND_MOVE ) {
				if ( currentlyHilitedShape > -1 ) {
					isHilitedShapeBeingMoved = true;
				}
			}
			else {
				// begin creating a new shape
				is2ndPointBeingDraggedOut = true;
				x1 = x2 = x3 = mouse_x;
				y1 = y2 = y3 = mouse_y;
				shapeBeingDraggedOut = new MyShape();
				usePointsSpecifiedByMouseToComputeShape(
					simplePaint.currentTool,
					x1-offset_x, y1-offset_y,
					x2-offset_x, y2-offset_y,
					x3-offset_x, y3-offset_y,
					colorPalette.getCurrentlySelectedColor(),
					shapeBeingDraggedOut
				);
				repaint();
			}
		}
		else if ( SwingUtilities.isRightMouseButton(e) ) {
			isScrolling = true;
		}
	}

	public void mouseReleased( MouseEvent e ) {
		old_mouse_x = mouse_x;
		old_mouse_y = mouse_y;
		mouse_x = e.getX();
		mouse_y = e.getY();

		if ( radialMenu.isVisible() ) {
			int returnValue = radialMenu.releaseEvent( mouse_x, mouse_y );

			int itemID = radialMenu.getItemID(radialMenu.getSelection());
			if ( 0 <= itemID && itemID < SimplePaint.NUM_TOOLS ) {
				simplePaint.setCurrentTool(itemID);
			}

			if ( returnValue == CustomWidget.S_REDRAW )
				repaint();
			if ( returnValue != CustomWidget.S_EVENT_NOT_CONSUMED )
				return;
		}
		if ( colorPalette.isVisible() ) {
			int returnValue = colorPalette.releaseEvent( mouse_x, mouse_y );
			if ( returnValue == CustomWidget.S_REDRAW )
				repaint();
			if ( returnValue != CustomWidget.S_EVENT_NOT_CONSUMED )
				return;
		}
		if ( simplePaint.currentTool == SimplePaint.TOOL_SELECT_AND_MOVE ) {
			if ( SwingUtilities.isLeftMouseButton(e) ) {
				isHilitedShapeBeingMoved = false;
			}
		}
		else {
			if ( is2ndPointBeingDraggedOut ) {
				if ( SwingUtilities.isLeftMouseButton(e) ) {
					if ( numPointsUsedToComputeShape(simplePaint.currentTool) == 2 ) {
						completeCreationOfShape();
						repaint();
					}
					else {
						is2ndPointBeingDraggedOut = false;
						is3rdPointBeingDraggedOut = true;
					}
				}
			}
			else if ( is3rdPointBeingDraggedOut ) {
				if ( SwingUtilities.isLeftMouseButton(e) ) {
					completeCreationOfShape();
					repaint();
				}
			}
		}
		if ( isScrolling ) {
			if ( SwingUtilities.isRightMouseButton(e) ) {
				isScrolling = false;
			}
		}
	}

	public void mouseMoved( MouseEvent e ) {
		if ( is2ndPointBeingDraggedOut || is3rdPointBeingDraggedOut ) {
			mouseDragged(e);
			return;
		}

		old_mouse_x = mouse_x;
		old_mouse_y = mouse_y;
		mouse_x = e.getX();
		mouse_y = e.getY();

		if ( radialMenu.isVisible() ) {
			int returnValue = radialMenu.moveEvent( mouse_x, mouse_y );
			if ( returnValue == CustomWidget.S_REDRAW )
				repaint();
			if ( returnValue != CustomWidget.S_EVENT_NOT_CONSUMED )
				return;
		}
		if ( colorPalette.isVisible() ) {
			int returnValue = colorPalette.moveEvent( mouse_x, mouse_y );
			if ( returnValue == CustomWidget.S_REDRAW )
				repaint();
			if ( returnValue != CustomWidget.S_EVENT_NOT_CONSUMED )
				return;
		}
		if ( isHilitedShapeBeingMoved || isScrolling ) {
			return;
		}

		if ( simplePaint.currentTool == SimplePaint.TOOL_SELECT_AND_MOVE ) {
			computeHilitedShape();
		}
	}

	public void mouseDragged( MouseEvent e ) {
		old_mouse_x = mouse_x;
		old_mouse_y = mouse_y;
		mouse_x = e.getX();
		mouse_y = e.getY();

		if ( radialMenu.isVisible() ) {
			int returnValue = radialMenu.dragEvent( mouse_x, mouse_y );
			if ( returnValue == CustomWidget.S_REDRAW )
				repaint();
			if ( returnValue != CustomWidget.S_EVENT_NOT_CONSUMED )
				return;
		}
		if ( colorPalette.isVisible() ) {
			int returnValue = colorPalette.dragEvent( mouse_x, mouse_y );
			if ( returnValue == CustomWidget.S_REDRAW )
				repaint();
			if ( returnValue != CustomWidget.S_EVENT_NOT_CONSUMED )
				return;
		}
		if ( isHilitedShapeBeingMoved && currentlyHilitedShape > -1 ) {
			MyShape shape = shapes.elementAt(currentlyHilitedShape);
			shape.translate( mouse_x - old_mouse_x, mouse_y - old_mouse_y );
			repaint();
		}
		else if ( is2ndPointBeingDraggedOut || is3rdPointBeingDraggedOut ) {
			if ( is2ndPointBeingDraggedOut ) {
				x2 = x3 = mouse_x;
				y2 = y3 = mouse_y;
			}
			else {
				x3 = mouse_x;
				y3 = mouse_y;
			}
			usePointsSpecifiedByMouseToComputeShape(
				simplePaint.currentTool,
				x1-offset_x, y1-offset_y,
				x2-offset_x, y2-offset_y,
				x3-offset_x, y3-offset_y,
				colorPalette.getCurrentlySelectedColor(),
				shapeBeingDraggedOut
			);
			repaint();
		}
		else if ( isScrolling ) {
			offset_x += mouse_x - old_mouse_x;
			offset_y += mouse_y - old_mouse_y;
			repaint();
		}
	}

}

public class SimplePaint implements ActionListener {

	static final String applicationName = "Simple Paint";

	JFrame frame;
	Container toolPanel;
	MyCanvas canvas;

	JMenuItem clearMenuItem, quitMenuItem, aboutMenuItem;
	JCheckBoxMenuItem toolsMenuItem, colorsMenuItem, enableCompositingMenuItem;

	public static final int TOOL_SELECT_AND_MOVE = 0;
	public static final int TOOL_CREATE_SQUARE = 1;
	public static final int TOOL_CREATE_RECTANGLE = 2;
	public static final int TOOL_CREATE_CIRCLE = 3;
	public static final int TOOL_CREATE_ELLIPSE = 4;
	public static final int TOOL_CREATE_EQUILATERAL_TRIANGLE = 5;
	public static final int TOOL_CREATE_TRIANGLE = 6;
	public static final int NUM_TOOLS = 7;

	JRadioButton [] toolButtons = new JRadioButton[ NUM_TOOLS ];
	public String [] toolNames = new String[ NUM_TOOLS ];
	public int currentTool = TOOL_CREATE_SQUARE;

	public void setCurrentTool( int tool ) {
		currentTool = tool;
		toolButtons[tool].setSelected(true);
	}

	public void actionPerformed(ActionEvent e) {
		Object source = e.getSource();
		if ( source == clearMenuItem ) {
			canvas.clear();
		}
		else if ( source == quitMenuItem ) {
			int response = JOptionPane.showConfirmDialog(
				frame,
				"Really quit?",
				"Confirm Quit",
				JOptionPane.YES_NO_OPTION
			);

			if (response == JOptionPane.YES_OPTION) {
				System.exit(0);
			}
		}
		else if ( source == toolsMenuItem ) {
			Container pane = frame.getContentPane();
			if ( toolsMenuItem.isSelected() ) {
				pane.removeAll();
				pane.add( toolPanel );
				pane.add( canvas );
			}
			else {
				pane.removeAll();
				pane.add( canvas );
			}
			frame.invalidate();
			frame.validate();
		}
		else if ( source == colorsMenuItem ) {
			canvas.setColorPaletteVisible( colorsMenuItem.isSelected() );
			canvas.repaint();
		}
		else if ( source == enableCompositingMenuItem ) {
			MyShape.enableCompositing = enableCompositingMenuItem.isSelected();
			canvas.repaint();
		}
		else if ( source == aboutMenuItem ) {
			JOptionPane.showMessageDialog(
				frame,
				"'" + applicationName + "' Sample Program\n"
					+ "Original version written January 2008",
				"About",
				JOptionPane.INFORMATION_MESSAGE
			);
		}
		else {
			for ( int i = 0; i < NUM_TOOLS; ++i ) {
				if ( source == toolButtons[i] ) {
					currentTool = i;
					return;
				}
			}
		}
	}


	// For thread safety, this should be invoked
	// from the event-dispatching thread.
	//
	private void createUI() {
		if ( ! SwingUtilities.isEventDispatchThread() ) {
			System.out.println(
				"Warning: UI is not being created in the Event Dispatch Thread!");
			assert false;
		}

		toolNames[ TOOL_SELECT_AND_MOVE ] = "Select and Move";
		toolNames[ TOOL_CREATE_SQUARE ] = "Square";
		toolNames[ TOOL_CREATE_RECTANGLE ] = "Rectangle";
		toolNames[ TOOL_CREATE_CIRCLE ] = "Circle";
		toolNames[ TOOL_CREATE_ELLIPSE ] = "Ellipse";
		toolNames[ TOOL_CREATE_EQUILATERAL_TRIANGLE ] = "Equilateral Triangle";
		toolNames[ TOOL_CREATE_TRIANGLE ] = "Triangle";

		frame = new JFrame( applicationName );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

		JMenuBar menuBar = new JMenuBar();
			JMenu menu = new JMenu("File");
				clearMenuItem = new JMenuItem("Clear");
				clearMenuItem.addActionListener(this);
				menu.add(clearMenuItem);

				menu.addSeparator();

				quitMenuItem = new JMenuItem("Quit");
				quitMenuItem.addActionListener(this);
				menu.add(quitMenuItem);
			menuBar.add(menu);
			menu = new JMenu("View");
				toolsMenuItem = new JCheckBoxMenuItem("Show Tools");
				toolsMenuItem.setSelected( true );
				toolsMenuItem.addActionListener(this);
				menu.add(toolsMenuItem);

				colorsMenuItem = new JCheckBoxMenuItem("Show Colors");
				colorsMenuItem.setSelected( true );
				colorsMenuItem.addActionListener(this);
				menu.add(colorsMenuItem);

				enableCompositingMenuItem = new JCheckBoxMenuItem("Enable Compositing");
				enableCompositingMenuItem.setSelected( MyShape.enableCompositing );
				enableCompositingMenuItem.addActionListener(this);
				menu.add(enableCompositingMenuItem);
			menuBar.add(menu);
			menu = new JMenu("Help");
				aboutMenuItem = new JMenuItem("About");
				aboutMenuItem.addActionListener(this);
				menu.add(aboutMenuItem);
			menuBar.add(menu);
		frame.setJMenuBar(menuBar);

		toolPanel = new JPanel();
		toolPanel.setLayout( new BoxLayout( toolPanel, BoxLayout.Y_AXIS ) );

		canvas = new MyCanvas(this);

		Container pane = frame.getContentPane();
		pane.setLayout( new BoxLayout( pane, BoxLayout.X_AXIS ) );
		pane.add( toolPanel );
		pane.add( canvas );

		ButtonGroup group = new ButtonGroup();
		for ( int i = 0; i < NUM_TOOLS; ++i ) {
			toolButtons[i] = new JRadioButton( toolNames[i] );
			toolButtons[i].setAlignmentX( Component.LEFT_ALIGNMENT );
			toolButtons[i].addActionListener(this);
			if ( i == currentTool )
				toolButtons[i].setSelected(true);
			toolPanel.add( toolButtons[i] );
			group.add( toolButtons[i] );
		}

		frame.pack();
		frame.setVisible( true );
	}

	public static void main( String[] args ) {
		// Schedule the creation of the UI for the event-dispatching thread.
		javax.swing.SwingUtilities.invokeLater(
			new Runnable() {
				public void run() {
					SimplePaint sp = new SimplePaint();
					sp.createUI();
				}
			}
		);
	}
}

