// SlateAppMenuBar.java
package version1;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import slate.*;
/**
* The menu bar for the application.
* This menu bar adds a few menus--for clearing the slate
* switching between different drawing modes
* @author Ramnivas Laddad
*/
public class SlateAppMenuBar extends JMenuBar {
private Slate _slate;
/**
* Construct the menu bar
* Creates sub menus for various operations.
* @param slate the slate to draw
*/
public SlateAppMenuBar(Slate slate) {
_slate = slate;
createSlateMenus();
}
/**
* Private method that does actual work of creating menus
*/
private void createSlateMenus() {
JMenu slateMenu = add(new JMenu("Slate"));
slateMenu.add(new AbstractAction("Clear") {
public void actionPerformed(ActionEvent e) {
_slate.getModel().removeAllShapes();
}
});
JMenuItem shapeModeMenu = slateMenu.add(new JMenu("Set Shape Mode"));
String[] shapeModeStrings
= new String[]{"Rectangle", "Ellipse", "Line"};
ButtonGroup shapeModeButtonGroup = new ButtonGroup();
ItemListener toggleShapeModeItemListener
= new ToggleShapeModeItemListener(_slate);
for (int size = shapeModeStrings.length, i = 0; i < size; ++i) {
JRadioButtonMenuItem shapeModeSubMenu
= new JRadioButtonMenuItem(shapeModeStrings[i]);
shapeModeButtonGroup.add(shapeModeSubMenu);
shapeModeMenu.add(shapeModeSubMenu);
shapeModeSubMenu.addItemListener(toggleShapeModeItemListener);
if ( i == 0 ) {
shapeModeSubMenu.doClick(); // select first menu
}
}
}
}
/**
* Common mouse listener for various drawing modes.
* The change of mode is effected by installing different
* MouseListener to the slate. Each mosue listener interprets
* the mouse actions that is suitable for type of shape it draws.
* This class is suitable to be extended by Shapes that are defined
* by two location: the starting point and the ending point.
*
* The sub-classes must provide implementation for the abstract method
* createShape().
*
* This class could also implement MouseMotionListener if we want to give
* feedback to user about the Shape that will get drawn on mouse release
* But for now, keep it simple.
*/
abstract class ShapeDrawMouseListener extends MouseAdapter {
private Point _pressPoint;
/**
* Creates a shape defined by provided point
* Abstract method to be implemented by sub-classes to return
* a Shape object by interpreting the starting and ending point
* in some way.
* @param startPoint the point at which the shape starts
* @param endPoint the point at which the shape ends
* @return shape defined by startPoint and endPoint
*/
public abstract Shape createShape(Point startPoint, Point endPoint);
/**
* Respond to mouse pressed event
* Stores the location of mouse pressed to be used at starting point.
* @param e mouse event
*/
public void mousePressed(MouseEvent e) {
_pressPoint = e.getPoint();
}
/**
* Respond to mouse release event
* Creates a shape (by calling abstract method) and adds it to the
* slate
* @param e mouse event
*/
public void mouseReleased(MouseEvent e) {
SlateModel slateModel = ((Slate)e.getSource()).getModel();
slateModel.addShape(createShape(_pressPoint, e.getPoint()));
}
//-------------------------------------------------------------
// Protected methods
//-------------------------------------------------------------
/**
* Convenience method that returns the bound given two points
* @param first a point
* @param second another point
* @return smallest rectangle that bound both the supplied points
*/
protected Rectangle getRectBound(Point first, Point second) {
Rectangle boundingRect = new Rectangle(first);
boundingRect.add(second);
return boundingRect;
}
}
/**
* MouseListener that draws a rectangle.
*/
class RectangleShapeDrawMouseListener extends ShapeDrawMouseListener {
/**
* Implement the abstract method in base class to create a rectangle.
* This rectangle is bounded by the starting and ending points given
* @param startPoint the point at which the shape starts
* @param endPoint the point at which the shape ends
* @return rectangle bound by startPoint and endPoint
*/
public Shape createShape(Point startPoint, Point endPoint) {
Rectangle boundingRect = getRectBound(startPoint, endPoint);
return new Rectangle2D.Float(boundingRect.x, boundingRect.y,
boundingRect.width, boundingRect.height);
}
}
/**
* MouseListener that draws a ellipse.
*/
class EllipseShapeDrawMouseListener extends ShapeDrawMouseListener {
/**
* Implement the abstract method in base class to create a ellipse.
* This ellipse is bounded by the starting and ending points given
* @param startPoint the point at which the shape starts
* @param endPoint the point at which the shape ends
* @return ellipse bound by startPoint and endPoint
*/
public Shape createShape(Point startPoint, Point endPoint) {
Rectangle boundingRect = getRectBound(startPoint, endPoint);
return new Ellipse2D.Float(boundingRect.x, boundingRect.y,
boundingRect.width, boundingRect.height);
}
}
/**
* MouseListener that draws a line.
*/
class LineShapeDrawMouseListener extends ShapeDrawMouseListener {
/**
* Implement the abstract method in base class to create a line.
* This line starts at the starting and ends at the ending point given
* @param startPoint the point at which the shape starts
* @param endPoint the point at which the shape ends
* @return line bound by startPoint and endPoint
*/
public Shape createShape(Point startPoint, Point endPoint) {
return new Line2D.Float(startPoint, endPoint);
}
}
/**
* A common item listener for all shape mode switching menus
* When a menu switching mode is activated, it removes the last installed
* ShapeDrawMouseListener and installs a new one that corresponds to the
* new shape mode.
*/
class ToggleShapeModeItemListener implements ItemListener {
private Slate _slate;
private ShapeDrawMouseListener _currentListener;
/**
* Constructor.
* @param slate slate for which mouse listeners are switched
*/
public ToggleShapeModeItemListener(Slate slate){
_slate = slate;
}
/**
* Respond to change in items state by switching drawing mode.
* @param e item event
*/
public void itemStateChanged(ItemEvent e) {
JRadioButtonMenuItem rb = (JRadioButtonMenuItem) e.getSource();
if(! rb.isSelected()) {
return;
}
String buttonText = rb.getText();
_slate.removeMouseListener(_currentListener);
if (buttonText.equals("Rectangle")) {
_currentListener = new RectangleShapeDrawMouseListener();
} else if (buttonText.equals("Ellipse")) {
_currentListener = new EllipseShapeDrawMouseListener();
} else if (buttonText.equals("Line")) {
_currentListener = new LineShapeDrawMouseListener();
}
_slate.addMouseListener(_currentListener);
}
}