Button4 (other files are same as before)

Main.java  [download]

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

/**
 * Main program for Button4
 */
public class Main extends JFrame {
    /** The squares */
    private Square square1, square2, square3; //*2 Polymorphism on Square

    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
	MyCanvas canvas = new MyCanvas (this);
	content.add (canvas, BorderLayout.CENTER);

	/*
	 * Now create the square and its associated buttons
	 */
	square1 = new Square (canvas); //*2

	// Control panel for the square
	JPanel controls1 = new JPanel ();
	controls1.setBorder (new LineBorder(Color.BLUE));
	controls1.setLayout (new FlowLayout());

	// Create our buttons by instantiating them
	// turns out we don't need to remember them,
	// just to add() them to panel layout
	controls1.add (new ButtonLeft (square1)); //*1 Users of button classes are unchanged
	controls1.add (new ButtonRight (square1)); //*1
	controls1.add (new ButtonBigger (square1)); //*1
	controls1.add (new ButtonSmaller (square1)); //*1
	controls1.add (new ButtonFill (square1)); //*1

	/*
	 * Another square
	 */
	square2 = new ColSquare (canvas, Color.RED); //*2
	
	JPanel controls2 = new JPanel ();
	controls2.setBorder (new LineBorder(Color.BLUE));
	controls2.setLayout (new FlowLayout());
	
	// Create our buttons
	controls2.add (new ButtonLeft (square2));
	controls2.add (new ButtonRight (square2));
	controls2.add (new ButtonBigger (square2));
	controls2.add (new ButtonSmaller (square2));
	controls2.add (new ButtonFill (square2));

	/*
	 * A third square, of a different color
	 */
	square3 = new ColSquare (canvas, Color.GREEN); //*2
	
	JPanel controls3 = new JPanel ();
	controls3.setBorder (new LineBorder(Color.BLUE));
	controls3.setLayout (new FlowLayout());
	
	// Create our buttons
	controls3.add (new ButtonLeft (square3));
	controls3.add (new ButtonRight (square3));
	controls3.add (new ButtonBigger (square3));
	controls3.add (new ButtonSmaller (square3));
	controls3.add (new ButtonFill (square3));

	// Need a grid container to hold our panels
	JPanel controls = new JPanel ();
	controls.setLayout (new GridLayout(3, 1));
	controls.add (controls1);
	controls.add (controls2);
	controls.add (controls3);
	content.add (controls, BorderLayout.SOUTH);

	setVisible (true);
    }

    public void drawSquares (Graphics g) {
	// For each square (this could be an array)
	square1.draw (g); //*2
	square2.draw (g); //*2
	square3.draw (g); //*2
    }
}

ColSquare.java  [download]

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

/**
 * Specialized subclass of Square
 */
public class ColSquare extends Square {
    private Color color; //*1 Adds new feature to Square

    /** Everything the same as superclass, except stash color */
    public ColSquare (MyCanvas canvas, Color color) { //*3 Pass one arg, keep the other
	super (canvas); //*3
	this.color = color; //*3
    }

    /** Piggybacks on super class' code */
    public void draw (Graphics g) {
	g.setColor (color); //*1
	super.draw (g); //*2 Exploit Square.draw() code
	g.setColor (Color.BLACK); //*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 Square square; //*1 Common to all our buttons
    protected Square.ButtonAction action; //*1

    public MyButton (Square square, Square.ButtonAction action) {
	this.square = square; //*1
	this.action = action; //*1
	setBorder (new LineBorder(Color.GREEN, 2)); //*1
	addActionListener (this); //*2 Shared routine, parameterized by "action" supplied by subclass
    }

    public void actionPerformed (ActionEvent e) { //*2
	square.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 (Square square, Square.ButtonAction action, Direction direction) { //*1 Common to all our arrow buttons
	super (square, action); //*2 Pass required args to chained constructor

    	if (direction==Direction.LEFTARROW) { //*3 Strip this arg and use it here
	    setIcon (new ImageIcon ("leftArrow.gif")); //*4 Encapsulates icon details
	}
	else if (direction==Direction.RIGHTARROW) { //*3
	    setIcon (new ImageIcon ("rightArrow.gif")); //*4
        }
    }
}

TextButton.java  [download]

import java.awt.*;

/**
 * Base class for our buttons that have text labels
 */
public abstract class TextButton extends MyButton {
    public TextButton (Square square, Square.ButtonAction action, String label) {
	super (square, action); //*2 Pass required arg to chained constructor
	setText (label); //*1 Common to all our text buttons
	setFont (new Font("SansSerif", Font.ITALIC, 14)); //*1
	setForeground (Color.BLUE); //*1
    }
}

ButtonLeft.java  [download]

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

public class ButtonLeft extends ArrowButton {
    public ButtonLeft (Square square) {
	super (square, Square.ButtonAction.LEFT, Direction.LEFTARROW); //*1 Supply relevant args and let superclasses do the rest
    }
}

ButtonRight.java  [download]

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

public class ButtonRight extends ArrowButton {
    public ButtonRight (Square square) {
	super (square, Square.ButtonAction.RIGHT, Direction.RIGHTARROW); //*1 Supply relevant args to superclasses
    }
}

ButtonSmaller.java  [download]

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

public class ButtonSmaller extends TextButton {
    public ButtonSmaller (Square square) {
	super (square, Square.ButtonAction.SMALLER, "Smaller"); //*1 Supply relevant args to superclasses
    }
}

ButtonBigger.java  [download]

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

public class ButtonBigger extends TextButton {
    public ButtonBigger (Square square) {
	super (square, Square.ButtonAction.BIGGER, "Bigger"); //*1 Supply relevant args to superclasses
    }
}

ButtonFill.java  [download]

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

public class ButtonFill extends TextButton {
    private boolean buttonState = false; //*2 We remember current state, so can toggle it
    
    public ButtonFill (Square square) {
	super (square, null, "Filled"); //*1 Supply relevant args to superclasses
    }

    // We override this one
    public void actionPerformed (ActionEvent e) { //*3 We override callback
	buttonState = ! buttonState; //*2
	if (buttonState) square.doAction (Square.ButtonAction.FILL); //*3
	else square.doAction (Square.ButtonAction.EMPTY); //*3

	// Also update button label
	if (buttonState) setText ("Empty"); //*4 Also update button label
	else setText ("Filled"); //*4
    }
}