Button5

Main.java  [download]

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
 * Main program for Button5
 */
public class Main extends JFrame {
    public static void main (String [] args) {
	java.awt.EventQueue.invokeLater (new Runnable() {
            public void run() {
		new Main ();
            }
        });
    }

    public Main () {
	// Window setup
	setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
	setSize (500, 500);
	Container content = getContentPane();
	content.setLayout (new BorderLayout());

	// Our canvas
	Model model; //*1 Pass object after it exists
	MyCanvas canvas = new MyCanvas (); //*1
	content.add (canvas, BorderLayout.CENTER);
	
	// Model object, holds the "application" data
	Model model = new Model (5, canvas); //*1

	// Didn't have this when we called MyCanvas constructor
	canvas.setModel (model); //*1

	// Grid container to hold our control panels
	JPanel controls = new JPanel ();
	controls.setLayout (new GridLayout(2, 1));
	controls.add (new ControlGlobal (model));
	controls.add (new ControlSingle (model));
	content.add (controls, BorderLayout.SOUTH);

	setVisible (true);
    }
}

MyCanvas.java  [download]

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

public class MyCanvas extends JPanel implements MouseListener {
    private Model model;

    MyCanvas () {
	setBorder (new LineBorder(Color.BLUE));
	addMouseListener (this);
    }

    /**
     * Couldn't pass model in constructor,
     * cause it doesn't exist yet
     */
    public void setModel (Model model) {
	this.model = model;
    }

    /** Tell parent to do the job */
    public void paintComponent (Graphics g) { //*1 Just pass jobs to parent
	super.paintComponent(g);
	model.draw (g); //*1
    }

    /** MouseListener callbacks */
    public void mousePressed (MouseEvent event) { //*1
	// Tell parent to do the job
	model.doMouse (event.getPoint()); //*1
    }
    public void mouseClicked (MouseEvent event) {}
    public void mouseReleased (MouseEvent event) {}
    public void mouseEntered (MouseEvent event) {}
    public void mouseExited (MouseEvent event) {}
}

Model.java  [download]

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;

/**
 * The "model" (vs. user interface) part of this program
 */
public class Model {
    /** Model or application data */
    private ArrayList<Square> squares = new ArrayList<Square> (); //*1 Use ArrayList
    private boolean drawX = false;

    private MyCanvas canvas;
    private Square current;

    public Model (int n, MyCanvas canvas) {
	// Stash ivar
	this.canvas = canvas;

	// Create the n objects
	for (int i=0; i<n; i++) {
	    squares.add (new Square(canvas)); //*1
	}

	// Initialize to some arbitrary square so current is never null
	current = squares.get(0); //*1
    }

    public void draw (Graphics g) {//*3 Handle drawing background X
	// The background X
	if (drawX) { //*3
	    Dimension size = canvas.getSize(); //*3
	    g.drawLine (0, 0, size.width, size.height); //*3
	    g.drawLine (0, size.height, size.width, 0); //*3
	}

	// Enumerate our list
	for (Square s: squares) { //*1
	    // N.B. (s==current) is a boolean expr,
	    // evaluates to true or false
	    s.draw (g, s==current); //*2 We tell Square if it's current or not
	}
    }

    public void setX (boolean newX) {
	drawX = newX;
	canvas.repaint();
    }

    Square getCurrent () {
	return current;
    }

    public void doMouse (Point p) {
	for (Square s: squares) { //*2
	    if (s.isInside(p)) { //*2
		current = s; //*2
		break; //*2
	    }
	}
	
	canvas.repaint();
    }
}

Square.java  [download]

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;

/**
 * Holds part of the Model data
 */
public class Square {
    /** Classvar to generate unique positions for each instance */
    static private int squareCount = 0; //*1 Keep count in classvar

    /** Enumerated type for which action to take on a square */
    public enum ButtonAction {LEFT, RIGHT, BIGGER, SMALLER};

    private MyCanvas canvas;

    /** Info about the square */
    private Rectangle loc;

    public Square (MyCanvas canvas) {
	this.canvas = canvas;

	// Keep count
	squareCount++; //*1

	// Initial values
	loc = new Rectangle (30*squareCount, 20*squareCount, 50, 50); //*1
    }

    public void draw (Graphics g, boolean isCurrent) {
	Graphics2D g2 = (Graphics2D) g;

	if (isCurrent)g2.fill (loc);
	else g2.draw (loc);
    }

    public boolean isInside (Point p) { //*2 Preferable to manual formula
	return loc.contains (p); //*2
    }

    public void doAction (ButtonAction action) {
	if (action==ButtonAction.LEFT) loc.x -= 10;
	else if (action==ButtonAction.RIGHT) loc.x += 10;
	else if (action==ButtonAction.BIGGER) { loc.width += 10; loc.height += 10; }
	else if (action==ButtonAction.SMALLER) { loc.width -= 10; loc.height -= 10; }

	canvas.repaint();
    }
}

ControlPanel.java  [download]

import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;

/**
 * Base class for our 2 control panels
 */
public abstract class ControlPanel extends JPanel { //*1 Abstract by choice
    public ControlPanel () {
	// Settings
	setLayout (new FlowLayout());
	setBorder (new LineBorder(Color.BLUE));
    }
}

ControlGlobal.java  [download]

import java.awt.*;
import javax.swing.*;

/**
 * The control panel for global commands 
 */
public class ControlGlobal extends ControlPanel {
    ControlGlobal (Model model) {
	// Create our buttons by instantiating their OBJECTS
	// turns out we don't need to remember them,
	// just to add() them to our panel layout
	// Nor do we need to save model, just pass it in
	add (new ButtonX (model)); //*1 Just instantiate and add
    }
}

ControlSingle.java  [download]

import java.awt.*;
import javax.swing.*;

/**
 * The control panel for commands that control a single square
 */
public class ControlSingle extends ControlPanel {
    ControlSingle (Model model) {
	add (new ButtonLeft (model)); //*1 Just instantiate and add
	add (new ButtonRight (model)); //*1
	add (new ButtonBigger (model)); //*1
	add (new ButtonSmaller (model)); //*1
    }
}

MyButton.java  [download]

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

/**
 * Base class for all our buttons
 */
public abstract class MyButton extends JButton implements ActionListener {
    protected Model model; //*1 Same as previous, except saves model not square
    protected Square.ButtonAction action;

    public MyButton (Model model, Square.ButtonAction action) { //*1
	this.model = model; //*1
	this.action = action;
	setBorder (new LineBorder(Color.GREEN, 2));
	addActionListener (this);
    }

    public void actionPerformed (ActionEvent e) { //*2 Gets current square from model, then calls it
	model.getCurrent().doAction (action); //*2
    }
}

ArrowButton.java  [download]

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
 * Base class for our buttons that have an arrow on them
 */
public abstract class ArrowButton extends MyButton {
    protected enum Direction {LEFTARROW, RIGHTARROW};

    public ArrowButton (Model model, Square.ButtonAction action, Direction direction) {
	super (model, action); //*1 Same as previous, except saves model not square

    	if (direction==Direction.LEFTARROW) {
	    setIcon (new ImageIcon ("leftArrow.gif"));
	}
	else if (direction==Direction.RIGHTARROW) {
	    setIcon (new ImageIcon ("rightArrow.gif"));
        }
    }
}

TextButton.java  [download]

import java.awt.*;

/**
 * Base class for our buttons that have text labels
 */
public abstract class TextButton extends MyButton {
    public TextButton (Model model, Square.ButtonAction action, String label) {
	super (model, action); //*1 Same as previous, except saves model not square
	setText (label);
	setFont (new Font("SansSerif", Font.ITALIC, 14));
	setForeground (Color.BLUE);
    }
}

ButtonX.java  [download]

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ButtonX extends TextButton {
    private boolean buttonState = false;
    
    public ButtonX (Model model) {
	super (model, null, "X");
    }
    
    // We override this one, and don't use our ButtonAction
    public void actionPerformed (ActionEvent e) { //*1 We override callback
	buttonState = ! buttonState; //*1
	model.setX (buttonState); //*1
    }
}

ButtonLeft.java  [download]

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ButtonLeft extends ArrowButton {
    ButtonLeft (Model model) {
	super (model, Square.ButtonAction.LEFT, Direction.LEFTARROW); //*1 Same as previous, except saves model not square
    }
}

ButtonRight.java  [download]

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ButtonRight extends ArrowButton {
    ButtonRight (Model model) {
	super (model, Square.ButtonAction.RIGHT, Direction.RIGHTARROW); //*1 Same as previous, except saves model not square
    }
}

ButtonBigger.java  [download]

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ButtonBigger extends TextButton {
    ButtonBigger (Model model) {
	super (model, Square.ButtonAction.BIGGER, "Bigger"); //*1 Same as previous, except saves model not square
    }
}

ButtonSmaller.java  [download]

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ButtonSmaller extends TextButton {
	ButtonSmaller (Model model) {
	    super (model, Square.ButtonAction.SMALLER, "Smaller"); //*1 Same as previous, except saves model not square
    }
}