diff --git a/java/3-0-MVC/hellomvc1/Controller.java b/java/3-0-MVC/hellomvc1/Controller.java
new file mode 100644
index 0000000000000000000000000000000000000000..e50adaee423ff65f50fa470b5023e1a6dd05c597
--- /dev/null
+++ b/java/3-0-MVC/hellomvc1/Controller.java
@@ -0,0 +1,21 @@
+// HelloMVC: a simple MVC example
+// the model is just a counter
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+// (C) Joseph Mack 2011, jmack (at) wm7d (dot) net, released under GPL v3 (or any later version)
+
+import java.awt.event.*;
+
+class Controller implements ActionListener {
+
+	Model model;
+
+	Controller(Model model) {
+		this.model = model;
+	}
+
+	// event from the view's button
+	public void actionPerformed(java.awt.event.ActionEvent e){
+		System.out.println("Controller: changing Model");
+		model.incrementCounter();
+	}
+}
diff --git a/java/3-0-MVC/hellomvc1/Main.java b/java/3-0-MVC/hellomvc1/Main.java
new file mode 100644
index 0000000000000000000000000000000000000000..2d27accf22ab51264dccbdba39409d0aa7af90c0
--- /dev/null
+++ b/java/3-0-MVC/hellomvc1/Main.java
@@ -0,0 +1,36 @@
+// HelloMVC: a simple MVC example
+// the model is just a counter
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+
+/**
+ * One view.  Separate controller.
+ */
+
+import javax.swing.*;
+import java.awt.Dimension;
+import java.awt.event.*;
+
+public class Main {
+
+	public static void main(String[] args){
+		JFrame frame = new JFrame("HelloMVC1");
+
+		// create Model and initialize it
+		Model model = new Model();
+		// create Controller, tell it about model
+		Controller controller = new Controller(model);
+		// create View, tell it about model and controller
+		View view = new View(model, controller);
+		// tell Model about View.
+		model.setView(view);
+
+		// add view (view is a JPanel)
+		frame.getContentPane().add(view);
+
+		// setup window
+		frame.setPreferredSize(new Dimension(300,300));
+		frame.pack();
+		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+		frame.setVisible(true);
+	}
+}
diff --git a/java/3-0-MVC/hellomvc1/Model.java b/java/3-0-MVC/hellomvc1/Model.java
new file mode 100644
index 0000000000000000000000000000000000000000..8781d0ff624b6ffcef2625b651c4fc4cf09bb9cc
--- /dev/null
+++ b/java/3-0-MVC/hellomvc1/Model.java
@@ -0,0 +1,42 @@
+// HelloMVC: a simple MVC example
+// the model is just a counter
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+
+// View interface
+interface IView {
+	public void updateView();
+}
+
+public class Model {
+
+	// the data in the model, just a counter
+	private int counter;
+	
+	// the view (note, only supports 1 view!)
+	IView view;
+
+	// set the view observer
+	public void setView(IView view) {
+		this.view = view;
+		// update the view to current state of the model
+		view.updateView();
+	}
+
+	public int getCounterValue() {
+		return counter;
+	}
+
+	public void incrementCounter() {
+		if (counter < 5) {
+			counter++;
+			System.out.println("Model: increment counter to " + counter);
+			notifyObserver();
+		}
+	}
+
+	// notify the IView observer
+	private void notifyObserver() {
+		System.out.println("Model: notify View");
+		view.updateView();
+	}
+}
diff --git a/java/3-0-MVC/hellomvc1/View.java b/java/3-0-MVC/hellomvc1/View.java
new file mode 100644
index 0000000000000000000000000000000000000000..7989d2c981750b9db0d7d70beba56a92f51ce3d5
--- /dev/null
+++ b/java/3-0-MVC/hellomvc1/View.java
@@ -0,0 +1,42 @@
+// HelloMVC: a simple MVC example
+// the model is just a counter
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+
+import javax.swing.*;
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.*;
+
+class View extends JPanel implements IView {
+
+	// the view's main user interface
+	private JButton button;
+
+	// the model that this view is showing
+	private Model model;
+
+	View(Model model, Controller controller) {
+		
+		// create the view UI
+		button = new JButton("?");
+		button.setMaximumSize(new Dimension(100, 50));
+		button.setPreferredSize(new Dimension(100, 50));
+		// a GridBagLayout with default constraints centres
+		// the widget in the window
+		this.setLayout(new GridBagLayout());
+		this.add(button, new GridBagConstraints());
+
+		// set the model
+		this.model = model;
+		// setup the event to go to the controller
+		button.addActionListener(controller);
+	}
+
+	// IView interface
+	public void updateView() {
+		System.out.println("View: updateView");
+		button.setText(Integer.toString(model.getCounterValue()));
+	}
+} 
diff --git a/java/3-0-MVC/hellomvc1/makefile b/java/3-0-MVC/hellomvc1/makefile
new file mode 100644
index 0000000000000000000000000000000000000000..2f843f1fb83fb74868ca6b0aa023df50fa80faad
--- /dev/null
+++ b/java/3-0-MVC/hellomvc1/makefile
@@ -0,0 +1,15 @@
+# super simple makefile
+# call it using 'make NAME=name_of_code_file_without_extension'
+# (assumes a .java extension)
+NAME = "Main"
+
+all:
+	@echo "Compiling..."
+	javac *.java
+
+run: all
+	@echo "Running..."
+	java $(NAME)
+
+clean:
+	rm -rf *.class
diff --git a/java/3-0-MVC/hellomvc2/Controller.java b/java/3-0-MVC/hellomvc2/Controller.java
new file mode 100644
index 0000000000000000000000000000000000000000..910dafdeae627d1c9a935e2b4a7b20e9b0440fe9
--- /dev/null
+++ b/java/3-0-MVC/hellomvc2/Controller.java
@@ -0,0 +1,32 @@
+// HelloMVC: a simple MVC example
+// the model is just a counter
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+// (C) Joseph Mack 2011, jmack (at) wm7d (dot) net, released under GPL v3 (or any later version)
+
+import java.awt.event.*;
+
+class Controller implements ActionListener, MouseListener {
+
+	Model model;
+
+	Controller(Model model) {
+		this.model = model;
+	}
+
+	// event from the view's button
+	public void actionPerformed(java.awt.event.ActionEvent e){
+		System.out.println("Controller: changing Model (actionPerformed)");
+		model.incrementCounter();
+	}
+
+	public void mouseClicked(MouseEvent e) {
+		System.out.println("Controller: changing Model (mouseClicked)");
+		model.incrementCounter();
+	}
+
+	public void mouseEntered(MouseEvent e) { }
+	public void mouseExited(MouseEvent e) { }
+	public void mousePressed(MouseEvent e) { }
+	public void mouseReleased(MouseEvent e) { }
+
+}
diff --git a/java/3-0-MVC/hellomvc2/Main.java b/java/3-0-MVC/hellomvc2/Main.java
new file mode 100644
index 0000000000000000000000000000000000000000..5dea7f2264301add9178dec48e879f6d5cb79b0e
--- /dev/null
+++ b/java/3-0-MVC/hellomvc2/Main.java
@@ -0,0 +1,47 @@
+// HelloMVC: a simple MVC example
+// the model is just a counter
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+
+/**
+ * Two views coordinated with the observer pattern.  Separate controller.
+ * The mechanics of a separate controller are starting to break down.
+ */
+
+import javax.swing.*;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.awt.event.*;
+
+public class Main {
+
+	public static void main(String[] args){
+		JFrame frame = new JFrame("HelloMVC2");
+
+		// create Model and initialize it
+		Model model = new Model();
+		// create Controller, tell it about model
+		Controller controller = new Controller(model);
+		// create View, tell it about model and controller
+		View view = new View(model, controller);
+		// tell Model about View
+		model.addView(view);
+
+		// create second view ...
+		View2 view2 = new View2(model, controller);
+		model.addView(view2);
+
+		// create a layout panel to hold the two views
+		JPanel p = new JPanel(new GridLayout(2,1));
+		frame.getContentPane().add(p);
+
+		// add views (each view is a JPanel)
+		p.add(view);
+		p.add(view2);
+
+		// setup the window
+		frame.setPreferredSize(new Dimension(300,300));
+		frame.pack();
+		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+		frame.setVisible(true);
+	}
+}
diff --git a/java/3-0-MVC/hellomvc2/Model.java b/java/3-0-MVC/hellomvc2/Model.java
new file mode 100644
index 0000000000000000000000000000000000000000..7599422ddaf3fbe63b1612f0c43babc9079c2680
--- /dev/null
+++ b/java/3-0-MVC/hellomvc2/Model.java
@@ -0,0 +1,45 @@
+import java.util.ArrayList;
+
+// HelloMVC: a simple MVC example
+// the model is just a counter 
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+
+// View interface
+interface IView {
+	public void updateView();
+}
+
+public class Model {	
+
+	// the data in the model, just a counter
+	private int counter;	
+	
+	// all views of this model
+	private ArrayList<IView> views = new ArrayList<IView>();
+	
+	// set the view observer
+	public void addView(IView view) {
+		views.add(view);
+		view.updateView();
+	}
+	
+	public int getCounterValue() {
+		return counter;
+	}
+	
+	public void incrementCounter() {
+		if (counter < 5) {
+			counter++;
+			System.out.println("Model: increment counter to " + counter);
+			notifyObservers();
+		}
+	} 	
+	
+	// notify the IView observer
+	private void notifyObservers() {
+		for (IView view : this.views) {
+			System.out.println("Model: notify View");
+			view.updateView();
+		}
+	}
+}
diff --git a/java/3-0-MVC/hellomvc2/View.java b/java/3-0-MVC/hellomvc2/View.java
new file mode 100644
index 0000000000000000000000000000000000000000..05bf27f89122391470da347239d3d15d00156d07
--- /dev/null
+++ b/java/3-0-MVC/hellomvc2/View.java
@@ -0,0 +1,42 @@
+// HelloMVC: a simple MVC example
+// the model is just a counter
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+
+import javax.swing.*;
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.*;
+
+class View extends JPanel implements IView {
+
+	// the view's main user interface
+	private JButton button;
+
+	// the model that this view is showing
+	private Model model;
+
+	View(Model model, Controller controller) {
+		
+		// create the view UI
+		button = new JButton("?");
+		button.setMaximumSize(new Dimension(100, 50));
+		button.setPreferredSize(new Dimension(100, 50));
+		// a GridBagLayout with default constraints centres
+		// the widget in the window
+		this.setLayout(new GridBagLayout());
+		this.add(button, new GridBagConstraints());
+
+		// set the model
+		this.model = model;
+		// setup the event to go to the controller
+		button.addActionListener(controller);
+	}
+
+	// IView interface
+	public void updateView() {
+		System.out.println("View: updateView");
+		button.setText(Integer.toString(model.getCounterValue()));
+	}
+}
diff --git a/java/3-0-MVC/hellomvc2/View2.java b/java/3-0-MVC/hellomvc2/View2.java
new file mode 100644
index 0000000000000000000000000000000000000000..218afa478032e77bba6e23853e24fd7b277e9fb9
--- /dev/null
+++ b/java/3-0-MVC/hellomvc2/View2.java
@@ -0,0 +1,39 @@
+// HelloMVC: a simple MVC example
+// the model is just a counter
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+
+import javax.swing.*;
+import java.awt.FlowLayout;
+import java.awt.Color;
+import java.awt.event.*;
+import java.util.*;
+
+class View2 extends JPanel implements IView {
+
+	// the model that this view is showing
+	private Model model;
+	private JLabel label = new JLabel();
+
+	View2(Model model, Controller controller) {
+
+		// create UI
+		setBackground(Color.WHITE);
+		setLayout(new FlowLayout(FlowLayout.LEFT));
+		this.add(this.label);
+
+		// set the model
+		this.model = model;
+		
+		// setup the event to go to the controller
+		addMouseListener(controller);
+	}
+
+	// IView interface
+	public void updateView() {
+		System.out.println("View2: updateView");
+		// just displays an 'X' for each counter value
+		String s = "";
+		for (int i=0; i<this.model.getCounterValue(); i++) s = s + "X";
+		this.label.setText(s);
+	}
+}
diff --git a/java/3-0-MVC/hellomvc2/makefile b/java/3-0-MVC/hellomvc2/makefile
new file mode 100644
index 0000000000000000000000000000000000000000..2f843f1fb83fb74868ca6b0aa023df50fa80faad
--- /dev/null
+++ b/java/3-0-MVC/hellomvc2/makefile
@@ -0,0 +1,15 @@
+# super simple makefile
+# call it using 'make NAME=name_of_code_file_without_extension'
+# (assumes a .java extension)
+NAME = "Main"
+
+all:
+	@echo "Compiling..."
+	javac *.java
+
+run: all
+	@echo "Running..."
+	java $(NAME)
+
+clean:
+	rm -rf *.class
diff --git a/java/3-0-MVC/hellomvc3/Main.java b/java/3-0-MVC/hellomvc3/Main.java
new file mode 100644
index 0000000000000000000000000000000000000000..c276da56f3327bb95b9faced295c5cfdab48ba1e
--- /dev/null
+++ b/java/3-0-MVC/hellomvc3/Main.java
@@ -0,0 +1,45 @@
+// HelloMVC: a simple MVC example
+// the model is just a counter 
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+
+/**
+ * Two views with integrated controllers.
+ */
+
+import javax.swing.*;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.awt.event.*;	
+
+public class Main {
+
+	public static void main(String[] args){	
+		JFrame frame = new JFrame("HelloMVC3");
+		
+		// create Model and initialize it
+		Model model = new Model();
+		
+		// create View, tell it about model
+		View view = new View(model);
+		// tell Model about View. 
+		model.addView(view);
+		
+		// create second view ...
+		View2 view2 = new View2(model);
+		model.addView(view2);
+		
+		// create a layout panel to hold the two views
+		JPanel p = new JPanel(new GridLayout(2,1));
+		frame.getContentPane().add(p);
+
+		// add views (each view is a JPanel)
+		p.add(view);
+		p.add(view2);
+		
+		// create the window
+		frame.setPreferredSize(new Dimension(300,300));
+		frame.pack();
+		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+		frame.setVisible(true);
+	} 
+}
diff --git a/java/3-0-MVC/hellomvc3/Model.java b/java/3-0-MVC/hellomvc3/Model.java
new file mode 100644
index 0000000000000000000000000000000000000000..cda4d73a662c591329bd6fbf02c1d985cb92701c
--- /dev/null
+++ b/java/3-0-MVC/hellomvc3/Model.java
@@ -0,0 +1,45 @@
+import java.util.ArrayList;
+
+// HelloMVC: a simple MVC example
+// the model is just a counter 
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+
+// View interface
+interface IView {
+	public void updateView();
+}
+
+public class Model {	
+	
+	// the data in the model, just a counter
+	private int counter;	
+	// all views of this model
+	private ArrayList<IView> views = new ArrayList<IView>();
+
+	// set the view observer
+	public void addView(IView view) {
+		views.add(view);
+		// update the view to current state of the model
+		view.updateView();
+	}
+	
+	public int getCounterValue() {
+		return counter;
+	}
+	
+	public void incrementCounter() {
+		if (counter < 5) {
+			counter++;
+			System.out.println("Model: increment counter to " + counter);
+			notifyObservers();
+		}
+	} 	
+	
+	// notify the IView observer
+	private void notifyObservers() {
+			for (IView view : this.views) {
+				System.out.println("Model: notify View");
+				view.updateView();
+			}
+	}
+}
diff --git a/java/3-0-MVC/hellomvc3/View.java b/java/3-0-MVC/hellomvc3/View.java
new file mode 100644
index 0000000000000000000000000000000000000000..ba8cf1474b65381a4cfe14474c9403688fd1c1a1
--- /dev/null
+++ b/java/3-0-MVC/hellomvc3/View.java
@@ -0,0 +1,48 @@
+// HelloMVC: a simple MVC example
+// the model is just a counter
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+
+import javax.swing.*;
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.*;
+
+class View extends JPanel implements IView {
+
+	// the view's main user interface
+	private JButton button;
+
+	// the model that this view is showing
+	private Model model;
+
+	public View(Model model) {
+		
+		// create the view UI
+		button = new JButton("?");
+		button.setMaximumSize(new Dimension(100, 50));
+		button.setPreferredSize(new Dimension(100, 50));
+		// a GridBagLayout with default constraints centres
+		// the widget in the window
+		this.setLayout(new GridBagLayout());
+		this.add(button, new GridBagConstraints());
+
+		// set the model
+		this.model = model;
+
+		// setup the event to go to the "controller"
+		// (this anonymous class is essentially the controller)
+		button.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				model.incrementCounter();
+			}
+		});
+	}
+
+	// IView interface
+	public void updateView() {
+		System.out.println("View: updateView");
+		button.setText(Integer.toString(model.getCounterValue()));
+	}
+}
diff --git a/java/3-0-MVC/hellomvc3/View2.java b/java/3-0-MVC/hellomvc3/View2.java
new file mode 100644
index 0000000000000000000000000000000000000000..c628dd2c608b377f1feb604e7229af3df9748ac5
--- /dev/null
+++ b/java/3-0-MVC/hellomvc3/View2.java
@@ -0,0 +1,44 @@
+// HelloMVC: a simple MVC example
+// the model is just a counter 
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+
+import javax.swing.*;
+import java.awt.FlowLayout;
+import java.awt.Color;
+import java.awt.event.*;
+import java.util.*;
+
+class View2 extends JPanel implements IView {
+
+	// the model that this view is showing
+	private Model model;
+	private JLabel label = new JLabel();
+
+	View2(Model model) {
+		// create UI
+		setBackground(Color.WHITE);
+		setLayout(new FlowLayout(FlowLayout.LEFT));
+
+		// set the model
+		this.model = model;
+		
+		// setup the event to go to the "controller"
+		// (this anonymous class is essentially the controller)		
+		addMouseListener(new MouseAdapter() {
+				public void mouseClicked(MouseEvent e) {
+					model.incrementCounter();
+				}
+		});
+
+		this.add(this.label);
+	}
+
+	// IView interface
+	public void updateView() {
+		System.out.println("View2: updateView");
+		// just displays an 'X' for each counter value
+		String s = "";
+		for (int i=0; i<this.model.getCounterValue(); i++) s = s + "X";
+		this.label.setText(s);
+	}
+}
diff --git a/java/3-0-MVC/hellomvc3/makefile b/java/3-0-MVC/hellomvc3/makefile
new file mode 100644
index 0000000000000000000000000000000000000000..2f843f1fb83fb74868ca6b0aa023df50fa80faad
--- /dev/null
+++ b/java/3-0-MVC/hellomvc3/makefile
@@ -0,0 +1,15 @@
+# super simple makefile
+# call it using 'make NAME=name_of_code_file_without_extension'
+# (assumes a .java extension)
+NAME = "Main"
+
+all:
+	@echo "Compiling..."
+	javac *.java
+
+run: all
+	@echo "Running..."
+	java $(NAME)
+
+clean:
+	rm -rf *.class
diff --git a/java/3-0-MVC/hellomvc3b/Main.java b/java/3-0-MVC/hellomvc3b/Main.java
new file mode 100644
index 0000000000000000000000000000000000000000..86f9ea4700618d6358357c54299e8f9c05cb6daa
--- /dev/null
+++ b/java/3-0-MVC/hellomvc3b/Main.java
@@ -0,0 +1,42 @@
+// HelloMVC: a simple MVC example
+// the model is just a counter 
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+
+/**
+ * Views using a listener anonymous class to handle Model update "events".
+ */
+
+import javax.swing.*;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.awt.event.*;	
+
+public class Main {
+
+	public static void main(String[] args){	
+		JFrame frame = new JFrame("HelloMVC3b");
+		
+		// create Model and initialize it
+		Model model = new Model();
+		
+		// create View, tell it about model
+		View view = new View(model);
+	
+		// create second view ...
+		View2 view2 = new View2(model);
+		
+		// create a layout panel to hold the two views
+		JPanel p = new JPanel(new GridLayout(2,1));
+		frame.getContentPane().add(p);
+
+		// add views (each view is a JPanel)
+		p.add(view);
+		p.add(view2);
+		
+		// create the window
+		frame.setPreferredSize(new Dimension(300,300));
+		frame.pack();
+		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+		frame.setVisible(true);
+	} 
+}
diff --git a/java/3-0-MVC/hellomvc3b/Model.java b/java/3-0-MVC/hellomvc3b/Model.java
new file mode 100644
index 0000000000000000000000000000000000000000..cda4d73a662c591329bd6fbf02c1d985cb92701c
--- /dev/null
+++ b/java/3-0-MVC/hellomvc3b/Model.java
@@ -0,0 +1,45 @@
+import java.util.ArrayList;
+
+// HelloMVC: a simple MVC example
+// the model is just a counter 
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+
+// View interface
+interface IView {
+	public void updateView();
+}
+
+public class Model {	
+	
+	// the data in the model, just a counter
+	private int counter;	
+	// all views of this model
+	private ArrayList<IView> views = new ArrayList<IView>();
+
+	// set the view observer
+	public void addView(IView view) {
+		views.add(view);
+		// update the view to current state of the model
+		view.updateView();
+	}
+	
+	public int getCounterValue() {
+		return counter;
+	}
+	
+	public void incrementCounter() {
+		if (counter < 5) {
+			counter++;
+			System.out.println("Model: increment counter to " + counter);
+			notifyObservers();
+		}
+	} 	
+	
+	// notify the IView observer
+	private void notifyObservers() {
+			for (IView view : this.views) {
+				System.out.println("Model: notify View");
+				view.updateView();
+			}
+	}
+}
diff --git a/java/3-0-MVC/hellomvc3b/View.java b/java/3-0-MVC/hellomvc3b/View.java
new file mode 100644
index 0000000000000000000000000000000000000000..113d8ad33404a074504d55c12716bfd6025e7638
--- /dev/null
+++ b/java/3-0-MVC/hellomvc3b/View.java
@@ -0,0 +1,54 @@
+// HelloMVC: a simple MVC example
+// the model is just a counter
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+
+import javax.swing.*;
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.*;
+
+class View extends JPanel  {
+
+	// the view's main user interface
+	private JButton button;
+
+	// the model that this view is showing
+	private Model model;
+
+
+	public View(Model model) {
+		
+		// create the view UI
+		button = new JButton("?");
+		button.setMaximumSize(new Dimension(100, 50));
+		button.setPreferredSize(new Dimension(100, 50));
+		// a GridBagLayout with default constraints centres
+		// the widget in the window
+		this.setLayout(new GridBagLayout());
+		this.add(button, new GridBagConstraints());
+
+		// set the model
+		this.model = model;
+
+		// anonymous class acts as model listener
+		this.model.addView(new IView() {
+			public void updateView() {
+				System.out.println("View: updateView");
+				button.setText(Integer.toString(model.getCounterValue()));
+			}
+		});
+
+
+		// setup the event to go to the "controller"
+		// (this anonymous class is essentially the controller)
+		button.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				model.incrementCounter();
+			}
+		});
+	}
+
+
+}
diff --git a/java/3-0-MVC/hellomvc3b/View2.java b/java/3-0-MVC/hellomvc3b/View2.java
new file mode 100644
index 0000000000000000000000000000000000000000..7f2f761735326d9d54e40b641f44e1787b7c13f7
--- /dev/null
+++ b/java/3-0-MVC/hellomvc3b/View2.java
@@ -0,0 +1,47 @@
+// HelloMVC: a simple MVC example
+// the model is just a counter 
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+
+import javax.swing.*;
+import java.awt.FlowLayout;
+import java.awt.Color;
+import java.awt.event.*;
+import java.util.*;
+
+class View2 extends JPanel {
+
+	// the model that this view is showing
+	private Model model;
+	private JLabel label = new JLabel();
+
+	View2(Model model) {
+		// create UI
+		setBackground(Color.WHITE);
+		setLayout(new FlowLayout(FlowLayout.LEFT));
+
+		// set the model
+		this.model = model;
+
+		// anonymous class acts as model listener
+		this.model.addView(new IView() {
+			public void updateView() {
+				System.out.println("View2: updateView");
+				// just displays an 'X' for each counter value
+				String s = "";
+				for (int i=0; i< model.getCounterValue(); i++) s = s + "X";
+				label.setText(s);
+			}
+		});		
+			
+		// setup the event to go to the "controller"
+		// (this anonymous class is essentially the controller)		
+		addMouseListener(new MouseAdapter() {
+				public void mouseClicked(MouseEvent e) {
+					model.incrementCounter();
+				}
+		});
+
+		this.add(this.label);
+	}
+
+}
diff --git a/java/3-0-MVC/hellomvc3b/makefile b/java/3-0-MVC/hellomvc3b/makefile
new file mode 100644
index 0000000000000000000000000000000000000000..2f843f1fb83fb74868ca6b0aa023df50fa80faad
--- /dev/null
+++ b/java/3-0-MVC/hellomvc3b/makefile
@@ -0,0 +1,15 @@
+# super simple makefile
+# call it using 'make NAME=name_of_code_file_without_extension'
+# (assumes a .java extension)
+NAME = "Main"
+
+all:
+	@echo "Compiling..."
+	javac *.java
+
+run: all
+	@echo "Running..."
+	java $(NAME)
+
+clean:
+	rm -rf *.class
diff --git a/java/3-0-MVC/hellomvc4/Main.java b/java/3-0-MVC/hellomvc4/Main.java
new file mode 100644
index 0000000000000000000000000000000000000000..5a47279956493269254d9a0ef31086b5c95fc950
--- /dev/null
+++ b/java/3-0-MVC/hellomvc4/Main.java
@@ -0,0 +1,49 @@
+// HelloMVC: a simple MVC example
+// the model is just a counter 
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+
+/**
+ *  Two views with integrated controllers.  Uses java.util.Observ{er, able} instead
+ *  of custom IView.
+ */
+
+import javax.swing.*;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.awt.event.*;	
+
+public class Main {
+
+	public static void main(String[] args){	
+		JFrame frame = new JFrame("HelloMVC4");
+		
+		// create Model and initialize it
+		Model model = new Model();
+		
+		// create View, tell it about model (and controller)
+		View view = new View(model);
+		// tell Model about View. 
+		model.addObserver(view);
+		
+		// create second view ...
+		View2 view2 = new View2(model);
+		model.addObserver(view2);
+		
+		// let all the views know that they're connected to the model
+		model.notifyObservers();
+		
+		// create a layout panel to hold the two views
+		JPanel p = new JPanel(new GridLayout(2,1));
+		frame.getContentPane().add(p);
+
+		// add views (each view is a JPanel)
+		p.add(view);
+		p.add(view2);
+		
+		// setup window
+		frame.setPreferredSize(new Dimension(300,300));
+		frame.pack();
+		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+		frame.setVisible(true);
+	} 
+}
diff --git a/java/3-0-MVC/hellomvc4/Model.java b/java/3-0-MVC/hellomvc4/Model.java
new file mode 100644
index 0000000000000000000000000000000000000000..14b46c2b718a190bfd7bd5cdbc3e06638ef4185e
--- /dev/null
+++ b/java/3-0-MVC/hellomvc4/Model.java
@@ -0,0 +1,27 @@
+import java.util.Observable;
+
+// HelloMVC: a simple MVC example
+// the model is just a counter 
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+
+public class Model extends Observable {	
+	// the data in the model, just a counter
+	private int counter;	
+	
+	Model() {
+		setChanged();
+	}
+	
+	public int getCounterValue() {
+		return counter;
+	}
+	
+	public void incrementCounter() {
+		if (counter < 5) {
+			counter++;
+			System.out.println("Model: increment counter to " + counter);
+			setChanged();
+			notifyObservers();
+		}
+	} 	
+}
diff --git a/java/3-0-MVC/hellomvc4/View.java b/java/3-0-MVC/hellomvc4/View.java
new file mode 100644
index 0000000000000000000000000000000000000000..277fc55f6740f4a11d4f80c54578d367f179e51a
--- /dev/null
+++ b/java/3-0-MVC/hellomvc4/View.java
@@ -0,0 +1,52 @@
+// HelloMVC: a simple MVC example
+// the model is just a counter 
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+
+import javax.swing.*;
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.*;	
+import java.util.Observable;
+import java.util.Observer;
+
+class View extends JPanel implements Observer {
+
+	// the view's main user interface
+	private JButton button; 
+	
+	// the model that this view is showing
+	private Model model;
+	
+	View(Model model) {
+		
+		// create the view UI
+		button = new JButton("?");
+		button.setMaximumSize(new Dimension(100, 50));
+		button.setPreferredSize(new Dimension(100, 50));
+
+		// a GridBagLayout with default constraints centres
+		// the widget in the window
+		this.setLayout(new GridBagLayout());
+		this.add(button, new GridBagConstraints());
+		
+		// set the model 
+		this.model = model;
+		
+		// setup the event to go to the "controller"
+		// (this anonymous class is essentially the controller)
+		button.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				model.incrementCounter();
+			}
+		});	
+	} 
+
+	// Observer interface 
+	@Override
+	public void update(Observable arg0, Object arg1) {
+		System.out.println("View: update");
+		button.setText(Integer.toString(model.getCounterValue()));		
+	}
+} 
diff --git a/java/3-0-MVC/hellomvc4/View2.java b/java/3-0-MVC/hellomvc4/View2.java
new file mode 100644
index 0000000000000000000000000000000000000000..2a617ab28b41365d2c5dc3eb16cf78730987c7ff
--- /dev/null
+++ b/java/3-0-MVC/hellomvc4/View2.java
@@ -0,0 +1,46 @@
+// HelloMVC: a simple MVC example
+// the model is just a counter 
+// inspired by code by Joseph Mack, http://www.austintek.com/mvc/
+
+import javax.swing.*;
+import java.awt.FlowLayout;
+import java.awt.Color;
+import java.awt.event.*;
+import java.util.*;
+
+class View2 extends JPanel implements Observer {
+
+	// the model that this view is showing
+	private Model model;
+	private JLabel label = new JLabel();
+
+	View2(Model model) {
+		
+		// create UI
+		setBackground(Color.WHITE);
+		setLayout(new FlowLayout(FlowLayout.LEFT));
+		this.add(this.label);
+
+		// set the model
+		this.model = model;
+		
+		// setup the event to go to the "controller"
+		// (this anonymous class is essentially the controller)		
+		addMouseListener(new MouseAdapter() {
+				public void mouseClicked(MouseEvent e) {
+					model.incrementCounter();
+				}
+		});
+
+	}
+
+	// Observer interface 
+	@Override
+	public void update(Observable o, Object arg) {
+		System.out.println("View2: updateView");
+		// just displays an 'X' for each counter value
+		String s = "";
+		for (int i=0; i<this.model.getCounterValue(); i++) s = s + "X";
+		this.label.setText(s);
+	}
+}
diff --git a/java/3-0-MVC/hellomvc4/makefile b/java/3-0-MVC/hellomvc4/makefile
new file mode 100644
index 0000000000000000000000000000000000000000..2f843f1fb83fb74868ca6b0aa023df50fa80faad
--- /dev/null
+++ b/java/3-0-MVC/hellomvc4/makefile
@@ -0,0 +1,15 @@
+# super simple makefile
+# call it using 'make NAME=name_of_code_file_without_extension'
+# (assumes a .java extension)
+NAME = "Main"
+
+all:
+	@echo "Compiling..."
+	javac *.java
+
+run: all
+	@echo "Running..."
+	java $(NAME)
+
+clean:
+	rm -rf *.class
diff --git a/java/3-0-MVC/nomvc/Main.java b/java/3-0-MVC/nomvc/Main.java
new file mode 100644
index 0000000000000000000000000000000000000000..f3e834cb3e8867b112846d4c9173d994f3b7822b
--- /dev/null
+++ b/java/3-0-MVC/nomvc/Main.java
@@ -0,0 +1,64 @@
+import javax.swing.*;
+import java.awt.Dimension;
+import java.awt.GridLayout;
+import java.awt.event.*;
+import java.awt.*;
+
+public class Main extends JPanel {
+
+    public static void main(String[] args){
+        JFrame frame = new JFrame("NoMvc");
+        frame.getContentPane().add(new Main());
+        frame.setPreferredSize(new Dimension(300,300));
+        frame.pack();
+        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        frame.setVisible(true);
+    }
+
+    int counter = 0;
+
+    void increaseCounter() {
+        if (counter < 5) {
+            counter++;
+            String s = Integer.toString(counter);
+            button.setText(s);
+            // just displays an 'X' for each counter value
+            s = "";
+            for (int i = 0; i< counter; i++) s = s + "X";
+            label.setText(s);
+        }
+    }
+
+    JButton button;
+    JLabel label;
+
+    public Main() {
+
+        this.setLayout(new GridLayout(2,1));
+
+        JPanel topPanel = new JPanel();
+        button = new JButton("?");
+        button.setMaximumSize(new Dimension(100, 50));
+        button.setPreferredSize(new Dimension(100, 50));
+        topPanel.setLayout(new GridBagLayout());
+        topPanel.add(button, new GridBagConstraints());
+		button.addActionListener(new ActionListener() {
+                public void actionPerformed(ActionEvent e) {
+                    increaseCounter();
+                }
+            });
+		this.add(topPanel);
+
+
+        JPanel bottomPanel = new JPanel();
+        bottomPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
+        label = new JLabel("");
+        bottomPanel.add(label);
+        bottomPanel.addMouseListener(new MouseAdapter() {
+                public void mouseClicked(MouseEvent e) {
+                    increaseCounter();
+                }
+            });
+        this.add(bottomPanel);
+    }
+}
\ No newline at end of file
diff --git a/java/3-0-MVC/nomvc/makefile b/java/3-0-MVC/nomvc/makefile
new file mode 100644
index 0000000000000000000000000000000000000000..2f843f1fb83fb74868ca6b0aa023df50fa80faad
--- /dev/null
+++ b/java/3-0-MVC/nomvc/makefile
@@ -0,0 +1,15 @@
+# super simple makefile
+# call it using 'make NAME=name_of_code_file_without_extension'
+# (assumes a .java extension)
+NAME = "Main"
+
+all:
+	@echo "Compiling..."
+	javac *.java
+
+run: all
+	@echo "Running..."
+	java $(NAME)
+
+clean:
+	rm -rf *.class
diff --git a/java/3-0-MVC/readme.md b/java/3-0-MVC/readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..53226291d7cca61d8d24c23979f4eb9da42ef616
--- /dev/null
+++ b/java/3-0-MVC/readme.md
@@ -0,0 +1,42 @@
+# MVC Code Demos
+
+## Hello MVC Demos
+
+Simple MVC examples using an incremental counter model. Each directory has a make file with MVC classes. These demos were inspired by Joseph Mack: http://www.austintek.com/mvc/ 
+
+* `nomvc/` same functionality as MVC demos, but not using MVC
+* `hellomvc1/` separate view and controller classes, only 1 view
+* `hellomvc2/` separate view and controller classes, multiple views
+* `hellomvc3/` controller combined into view, multiple views
+* `hellomvc3b/` controller combined into view, views use modelListeners
+* `hellomvc4/` using Java's Observer interface and Observable base class
+
+## Triangle Demos
+
+These are more complex MVC examples using a right-angle triangle model
+
+All use the same "right-angle triangle" Model:
+
+* `model/TriangleModel.java` the model used in every example
+* `model/IView.java` the view interface used in every example
+
+There are different Views and Controllers:
+
+* `SimpleTextView.java` a very limited but simple view 
+* `TextView.java` a text view with correct controller updates and protected field
+* `SliderView` uses slider widgets to adjust side lengths
+* `SpinnerView` uses spinner widgets to adjust side lengths 
+* `GraphicalView` adjust side length using direct manipulation of a picture of the triangle
+
+These views are combined in different ways. Set makefile NAME to one of the following startup classes to run:
+
+* `Main1.java`			SimpleTextView only
+* `Main2.java`			TextView only
+* `Main3.java`			multiple views at once
+* `Main4.java`			GraphicalView demo
+* `Main5.java` 			multiple views using a tab
+
+These demos also show how to use packages to organize code and they use the `FormLayout` custom layout manager. 
+
+
+					
\ No newline at end of file
diff --git a/java/3-0-MVC/triangle/Main1.java b/java/3-0-MVC/triangle/Main1.java
new file mode 100644
index 0000000000000000000000000000000000000000..a355dedf4057a1320b95a6003151a2e6b0875583
--- /dev/null
+++ b/java/3-0-MVC/triangle/Main1.java
@@ -0,0 +1,19 @@
+import javax.swing.JFrame;
+
+import model.TriangleModel;
+
+public class Main1 {
+
+	public static void main(String[] args) {
+
+		TriangleModel model = new TriangleModel();
+		
+		view.SimpleTextView view = new view.SimpleTextView(model);
+
+		JFrame frame = new JFrame("Triangle Main1");
+		frame.getContentPane().add(view);
+		frame.pack();
+		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+		frame.setVisible(true);
+	}
+}
diff --git a/java/3-0-MVC/triangle/Main2.java b/java/3-0-MVC/triangle/Main2.java
new file mode 100644
index 0000000000000000000000000000000000000000..1bf37cfe874cd02f2a647197aa9e6110ad01a159
--- /dev/null
+++ b/java/3-0-MVC/triangle/Main2.java
@@ -0,0 +1,17 @@
+import javax.swing.JFrame;
+
+import model.TriangleModel;
+
+public class Main2 {
+
+	public static void main(String[] args) {
+		model.TriangleModel model = new TriangleModel();
+		view.TextView view = new view.TextView(model);
+
+		JFrame frame = new JFrame("Triangle Main2");
+		frame.getContentPane().add(view);
+		frame.pack();
+		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+		frame.setVisible(true);
+	}
+}
diff --git a/java/3-0-MVC/triangle/Main3.java b/java/3-0-MVC/triangle/Main3.java
new file mode 100644
index 0000000000000000000000000000000000000000..536639d2c5be9da91079f404aa5ef6aef2452a79
--- /dev/null
+++ b/java/3-0-MVC/triangle/Main3.java
@@ -0,0 +1,27 @@
+import javax.swing.JFrame;
+
+import model.TriangleModel;
+import view.*;
+import java.awt.GridLayout;
+
+public class Main3 {
+
+	public static void main(String[] args) {
+		model.TriangleModel model = new TriangleModel();
+		TextView vText = new TextView(model);
+		ButtonView vButton = new ButtonView(model);
+		SliderView vSlider = new SliderView(model);
+		SpinnerView vSpinner = new SpinnerView(model);
+
+		JFrame frame = new JFrame("Triangle Main3");
+		frame.getContentPane().setLayout(new GridLayout(2, 2));
+		frame.getContentPane().add(vText);
+		frame.getContentPane().add(vButton);
+		frame.getContentPane().add(vSlider);
+		frame.getContentPane().add(vSpinner);
+
+		frame.pack();
+		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+		frame.setVisible(true);
+	}
+}
diff --git a/java/3-0-MVC/triangle/Main4.java b/java/3-0-MVC/triangle/Main4.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb986e8594f5c80d302748616e30d6ef13d63b9b
--- /dev/null
+++ b/java/3-0-MVC/triangle/Main4.java
@@ -0,0 +1,19 @@
+import javax.swing.JFrame;
+
+import model.TriangleModel;
+import view.*;
+import java.awt.GridLayout;
+
+public class Main4 {
+
+	public static void main(String[] args) {
+		model.TriangleModel model = new TriangleModel();
+		GraphicalView vGraphical = new GraphicalView(model);
+
+		JFrame frame = new JFrame("Triangle Main4");
+		frame.getContentPane().add(vGraphical);
+		frame.pack();
+		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+		frame.setVisible(true);
+	}
+}
diff --git a/java/3-0-MVC/triangle/Main5.java b/java/3-0-MVC/triangle/Main5.java
new file mode 100644
index 0000000000000000000000000000000000000000..3337199040aa97904645cd55f44e014d09cc0e80
--- /dev/null
+++ b/java/3-0-MVC/triangle/Main5.java
@@ -0,0 +1,24 @@
+import javax.swing.*;
+
+import model.TriangleModel;
+import view.*;
+import java.awt.GridLayout;
+
+public class Main5 {
+
+	public static void main(String[] args) {
+		model.TriangleModel model = new TriangleModel();
+		GraphicalView vGraphical = new GraphicalView(model);
+		TextView vText = new TextView(model);
+
+		JFrame frame = new JFrame("Triangle Main5");
+		JTabbedPane tabs = new JTabbedPane();
+		tabs.add("Text View", vText);
+		tabs.add("Graphical View", vGraphical);
+		frame.getContentPane().add(tabs);
+
+		frame.setSize(300, 300);
+		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+		frame.setVisible(true);
+	}
+}
diff --git a/java/3-0-MVC/triangle/makefile b/java/3-0-MVC/triangle/makefile
new file mode 100644
index 0000000000000000000000000000000000000000..06bac5dc51632c642c904a38c76928bb49981fdf
--- /dev/null
+++ b/java/3-0-MVC/triangle/makefile
@@ -0,0 +1,18 @@
+# super simple makefile
+# call it using 'make NAME=name_of_code_file_without_extension'
+# (assumes a .java extension)
+NAME = "Main1"
+
+all:
+	# (a bit of a hack to compile everything each time ...)
+	@echo "Compiling..."
+	javac *.java model/*.java view/*.java
+
+run: all
+	@echo "Running..."
+	java $(NAME)
+
+clean:
+	rm -rf *.class
+	rm -rf view/*.class
+	rm -rf model/*.class
diff --git a/java/3-0-MVC/triangle/model/IView.java b/java/3-0-MVC/triangle/model/IView.java
new file mode 100644
index 0000000000000000000000000000000000000000..2d7f3f8106241a5855f51379be5bd0f03290374b
--- /dev/null
+++ b/java/3-0-MVC/triangle/model/IView.java
@@ -0,0 +1,9 @@
+package model;
+
+public interface IView {
+	/**
+	 * This method is called by the model whenever it changes state.
+	 */
+	public void updateView();
+
+}
diff --git a/java/3-0-MVC/triangle/model/TriangleModel.java b/java/3-0-MVC/triangle/model/TriangleModel.java
new file mode 100644
index 0000000000000000000000000000000000000000..c5f2e1b8bc9b22a810a63682311df1bbb4f0b03b
--- /dev/null
+++ b/java/3-0-MVC/triangle/model/TriangleModel.java
@@ -0,0 +1,79 @@
+package model;
+
+// Note!  Nothing from the view package is imported here.
+import java.util.ArrayList;
+import javax.swing.undo.AbstractUndoableEdit;
+import javax.swing.undo.UndoableEdit;
+
+public class TriangleModel {
+
+	/* A list of the model's views. */
+	private ArrayList<IView> views = new ArrayList<IView>();
+
+	// Limit the size of the triangle.
+	public static final double MAX_SIDE = 100.0;
+	public static final double MAX_HYPO = 
+	    Math.sqrt(MAX_SIDE * MAX_SIDE + MAX_SIDE * MAX_SIDE);
+
+	private double base = 50.0;  // length of the base
+	private double height = 50.0;  // height of the triangle
+
+	public TriangleModel() { }
+
+	/** Set the base to a new value. Must be between 0 and MAX_BASE. */
+	public void setBase(double theBase) {
+		double tmp = Math.max(0, theBase);
+		this.base = Math.min(tmp, MAX_SIDE);
+
+		this.updateAllViews(); // update Views!
+	}
+
+	/** Set the height to a new value. Must be between 0 and MAX_HEIGHT. */
+	public void setHeight(double theHeight) {
+		this.height = Math.min(Math.max(0, theHeight), MAX_SIDE);
+
+		this.updateAllViews(); // update Views!
+	}
+
+	/** Set both the base and the height to new values. */
+	public void setValues(double theBase, double theHeight) {
+		this.base = Math.min(Math.max(0, theBase), MAX_SIDE);
+		this.height = Math.min(Math.max(0, theHeight), MAX_SIDE);
+
+		this.updateAllViews(); // update Views!
+	}
+
+	/** Get the length of this triangle's base. */
+	public double getBase() {
+		return this.base;
+	}
+
+	/** Get this triangle's height. */
+	public double getHeight() {
+		return this.height;
+	}
+
+	/** Get the length of this triangle's hypotenuse. */
+	public double getHypotenuse() {
+		return Math.sqrt(this.base * this.base + this.height * this.height);
+	}
+
+	/** Add a new view of this triangle. */
+	public void addView(IView view) {
+		this.views.add(view);
+
+		view.updateView(); // update Views!
+	}
+
+	/** Remove a view from this triangle. */
+	public void removeView(IView view) {
+		this.views.remove(view);
+	}
+
+	/** Update all the views that are viewing this triangle. */
+	private void updateAllViews() {
+		for (IView view : this.views) {
+			view.updateView();
+		}
+	}
+}
diff --git a/java/3-0-MVC/triangle/view/ButtonView.java b/java/3-0-MVC/triangle/view/ButtonView.java
new file mode 100644
index 0000000000000000000000000000000000000000..ed73bad73b3937cc42b33e2d8bd13073dda8b37d
--- /dev/null
+++ b/java/3-0-MVC/triangle/view/ButtonView.java
@@ -0,0 +1,109 @@
+package view;
+
+import model.*;
+
+import javax.swing.*;
+import java.awt.Dimension;
+import java.awt.event.*;
+import java.text.NumberFormat;
+import java.awt.GridLayout;
+
+public class ButtonView extends JPanel {
+	private TriangleModel model;
+	private JLabel base = new JLabel("0.0");
+	private JLabel height = new JLabel("0.0");
+	private JLabel hypo = new JLabel("0.0");
+	private JButton baseUp = new JButton("+");
+	private JButton baseDn = new JButton("-");
+	private JButton heightUp = new JButton("+");
+	private JButton heightDn = new JButton("-");
+
+	private NumberFormat formatter = NumberFormat.getNumberInstance();
+	private static final int MAX_FRACTION_DIGITS = 5;
+
+	public ButtonView(TriangleModel aModel) {
+		super();
+		this.model = aModel;
+
+		this.layoutView();
+		this.registerControllers();
+
+		this.model.addView(new IView() {
+			public void updateView() {
+				base.setText(formatter.format(model.getBase()));
+				height.setText(formatter.format(model.getHeight()));
+				hypo.setText(formatter.format(model.getHypotenuse()));
+
+				// Updating the view includes enabling/disabling components!
+				baseUp.setEnabled(model.getBase() < TriangleModel.MAX_SIDE);
+				baseDn.setEnabled(model.getBase() > 1);
+				heightUp.setEnabled(model.getHeight() < TriangleModel.MAX_SIDE);
+				heightDn.setEnabled(model.getHeight() > 1);
+			}
+
+		});
+	}
+
+	private void layoutView() {
+		this.setLayout(new FormLayout());
+		this.add(new JLabel("Base:"));
+		this.add(this.groupComponents(this.baseUp, this.baseDn, this.base));
+
+		this.add(new JLabel("Height:"));
+		this.add(this
+				.groupComponents(this.heightUp, this.heightDn, this.height));
+
+		this.add(new JLabel("Hypotenuse:"));
+		this.add(this.hypo);
+
+		Dimension d = this.hypo.getPreferredSize();
+		d.width = 80;
+		this.base.setPreferredSize(d);
+		this.height.setPreferredSize(d);
+		this.hypo.setPreferredSize(d);
+
+		this.formatter.setMaximumFractionDigits(MAX_FRACTION_DIGITS);
+	}
+
+	private Box groupComponents(JButton up, JButton dn, JLabel label) {
+		Box group = Box.createHorizontalBox();
+		group.add(up);
+		group.add(dn);
+		group.add(label);
+
+		Dimension d = up.getPreferredSize();
+		d.width = Math.max(up.getPreferredSize().width,
+				dn.getPreferredSize().width);
+		up.setPreferredSize(d);
+		dn.setPreferredSize(d);
+
+		return group;
+	}
+
+	private void registerControllers() {
+		this.baseUp.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent evt) {
+				model.setBase(model.getBase() + 1);
+			}
+		});
+
+		this.baseDn.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent evt) {
+				model.setBase(model.getBase() - 1);
+			}
+		});
+
+		this.heightUp.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent evt) {
+				model.setHeight(model.getHeight() + 1);
+			}
+		});
+
+		this.heightDn.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent evt) {
+				model.setHeight(model.getHeight() - 1);
+			}
+		});
+	}
+
+}
diff --git a/java/3-0-MVC/triangle/view/FormLayout.java b/java/3-0-MVC/triangle/view/FormLayout.java
new file mode 100644
index 0000000000000000000000000000000000000000..abb9f141fc9cf7f4be0c0d9426777710d48d4b5a
--- /dev/null
+++ b/java/3-0-MVC/triangle/view/FormLayout.java
@@ -0,0 +1,216 @@
+package view;
+
+import java.awt.LayoutManager;
+import java.awt.Insets;
+import java.util.ArrayList;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+
+/**
+ * A layout manager that arranges components in a double column. In most cases
+ * the left column will hold a label and the right column will hold a component
+ * the user can manipulate. Preferred component sizes are respected as much as
+ * possible. Components in the left column are right justified; components in
+ * the right column are left justified. <img src="doc-files/FormLayout.gif">
+ * 
+ * @author Byron Weber Becker
+ */
+public class FormLayout implements LayoutManager {
+	private int hGap = 6;
+	private int vGap = 4;
+
+	/** Construct a new FormLayout object. */
+	public FormLayout() {
+		super();
+	}
+
+	/**
+	 * Construct a new FormLayout object.
+	 * 
+	 * @param hGap
+	 *            the number of hortizontal pixels between components
+	 * @param vGap
+	 *            the number of vertical pixels between components
+	 */
+	public FormLayout(int hGap, int vGap) {
+		super();
+		this.hGap = hGap;
+		this.vGap = vGap;
+	}
+
+	/**
+	 * Adds the specified component with the specified name to the layout.
+	 * 
+	 * @param name
+	 *            the component name
+	 * @param comp
+	 *            the component to be added
+	 */
+	public void addLayoutComponent(String name, Component comp) {
+	}
+
+	/**
+	 * Removes the specified component from the layout.
+	 * 
+	 * @param comp
+	 *            the component to be removed
+	 */
+	public void removeLayoutComponent(Component comp) {
+	}
+
+	/**
+	 * Calculates the preferred size dimensions for the specified panel given
+	 * the components in the specified parent container.
+	 * 
+	 * @param parent
+	 *            the component to be laid out
+	 * 
+	 * @see #minimumLayoutSize
+	 */
+	public Dimension preferredLayoutSize(Container parent) {
+		synchronized (parent.getTreeLock()) {
+			ColDims cd = colDims(parent, true);
+
+			Insets insets = parent.getInsets();
+			Dimension d = new Dimension(cd.left + cd.right + insets.left
+					+ insets.right, cd.height + insets.top + insets.bottom);
+			return d;
+		}
+	}
+
+	/*
+	 * Precondition: the caller has gotten the treelock. preferred = true for
+	 * preferred sizes; false for minimum sizes
+	 */
+	private ColDims colDims(Container parent, boolean preferred) {
+		ColDims cd = new ColDims();
+		int nComponents = parent.getComponentCount();
+		// left column
+		for (int i = 1; i < nComponents; i += 2) {
+			Component left = parent.getComponent(i - 1);
+			Component right = parent.getComponent(i);
+
+			Dimension dLeft;
+			Dimension dRight;
+			if (preferred) {
+				dLeft = left.getPreferredSize();
+				dRight = right.getPreferredSize();
+			} else {
+				dLeft = left.getMinimumSize();
+				dRight = right.getMinimumSize();
+			}
+			cd.left = (int) Math.max(cd.left, dLeft.width);
+			cd.right = (int) Math.max(cd.right, dRight.width);
+			cd.height += (int) Math.max(dLeft.height, dRight.height);
+			cd.height += this.vGap;
+		}
+		if (nComponents % 2 == 1) { // odd number of components -- get the last
+									// one on the left
+			Component left = parent.getComponent(nComponents - 1);
+
+			Dimension dLeft;
+			if (preferred) {
+				dLeft = left.getPreferredSize();
+			} else {
+				dLeft = left.getMinimumSize();
+			}
+			cd.left = (int) Math.max(cd.left, dLeft.width);
+			cd.height += dLeft.height + this.vGap;
+		}
+		cd.left += this.hGap / 2;
+		cd.right += this.hGap / 2;
+		return cd;
+	}
+
+	/**
+	 * Calculates the minimum size dimensions for the specified panel given the
+	 * components in the specified parent container.
+	 * 
+	 * @param parent
+	 *            the component to be laid out
+	 * @see #preferredLayoutSize
+	 */
+	public Dimension minimumLayoutSize(Container parent) {
+		synchronized (parent.getTreeLock()) {
+			ColDims cd = colDims(parent, false);
+
+			Insets insets = parent.getInsets();
+			Dimension d = new Dimension(cd.left + cd.right + insets.left
+					+ insets.right, cd.height + insets.top + insets.bottom);
+			return d;
+		}
+	}
+
+	/**
+	 * Lays out the container in the specified panel.
+	 * 
+	 * @param parent
+	 *            the component which needs to be laid out
+	 */
+	public void layoutContainer(Container parent) {
+		Insets insets = parent.getInsets();
+
+		synchronized (parent.getTreeLock()) {
+			ColDims cd = this.colDims(parent, true);
+
+			int desiredWidth = cd.left + cd.right + insets.left + insets.right;
+			double widthScale = Math.min(1.0, parent.getWidth()
+					/ (double) desiredWidth);
+			double heightScale = Math.min(1.0, parent.getHeight()
+					/ (double) cd.height);
+
+			double scale = Math.min(widthScale, heightScale);
+
+			// System.out.println("FormLayout.layoutContainer: widthScale = " +
+			// widthScale + "; heightScale = " + heightScale);
+
+			int midPt = (int) (((cd.left + insets.left + this.hGap) / (double) (cd.left
+					+ cd.right + insets.left + insets.right + hGap)) * parent
+					.getWidth());
+			int top = insets.top + this.vGap;
+
+			int nComponents = parent.getComponentCount();
+			for (int i = 1; i < nComponents; i += 2) {
+				Component left = parent.getComponent(i - 1);
+				Component right = parent.getComponent(i);
+				Dimension lDim = left.getPreferredSize();
+				Dimension rDim = right.getPreferredSize();
+
+				int rowHeight = (int) (Math.max(lDim.height, rDim.height) * heightScale);
+
+				// scale left side, if necessary; then position
+				if (lDim.width > desiredWidth || lDim.height > rowHeight) {
+					lDim.width = (int) (lDim.width * scale);
+					lDim.height = (int) (lDim.height * scale);
+				}
+				left.setBounds((midPt - lDim.width - this.hGap / 2), top,
+						lDim.width, lDim.height);
+
+				// scale left side, if necessary; then position
+				if (rDim.width > desiredWidth || rDim.height > rowHeight) {
+					rDim.width = (int) (rDim.width * scale);
+					rDim.height = (int) (rDim.height * scale);
+				}
+				right.setBounds(midPt + this.hGap / 2, top, rDim.width,
+						rDim.height);
+				top = top + rowHeight + this.vGap;
+			}
+			if (nComponents % 2 == 1) { // odd number of components -- get the
+										// last one on the left
+				Component left = parent.getComponent(nComponents - 1);
+				Dimension lDim = left.getPreferredSize();
+				lDim.width = (int) (lDim.width * scale);
+				lDim.height = (int) (lDim.height * scale);
+				left.setBounds((midPt - lDim.width - this.hGap / 2), top,
+						lDim.width, lDim.height);
+			}
+		}
+	}
+
+	private static class ColDims {
+		int left = 0;
+		int right = 0;
+		int height = 0;
+	}
+}
diff --git a/java/3-0-MVC/triangle/view/GraphicalView.java b/java/3-0-MVC/triangle/view/GraphicalView.java
new file mode 100644
index 0000000000000000000000000000000000000000..74a2d4514dbb682d24af2533ee10fa4ca2e02c45
--- /dev/null
+++ b/java/3-0-MVC/triangle/view/GraphicalView.java
@@ -0,0 +1,144 @@
+package view;
+
+import model.*;
+
+import javax.swing.*;
+import javax.swing.event.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.text.NumberFormat;
+
+/**
+ * A view of a right triangle that displays the triangle graphically and allows
+ * the user to change the size by dragging the image with a mouse.
+ */
+public class GraphicalView extends JComponent {
+	private TriangleModel model;
+
+	private double scale = 1.0; // how much should the triangle be scaled?
+
+	private int handleSize = 5; // size of selectable square for dragging
+
+	// To format numbers consistently in the text fields.
+	private static final NumberFormat formatter = NumberFormat
+			.getNumberInstance();
+
+	public GraphicalView(TriangleModel aModel) {
+		super();
+		this.model = aModel;
+		this.layoutView();
+		this.registerControllers();
+		this.model.addView(new IView() {
+			/** The model changed. Ask the system to repaint the triangle. */
+			public void updateView() {
+				repaint();
+			}
+		});
+	}
+
+	/** How should it look on the screen? */
+	private void layoutView() {
+		this.setPreferredSize(new Dimension(200, 200));
+		this.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+	}
+
+	/** Register event Controllers for mouse clicks and motion. */
+	private void registerControllers() {
+		MouseInputListener mil = new MController();
+		this.addMouseListener(mil);
+		this.addMouseMotionListener(mil);
+	}
+
+	/** Paint the triangle with "handle" for resizing */
+	public void paintComponent(Graphics g) {
+		super.paintComponent(g);
+		Insets insets = this.getInsets();
+
+		this.scale = Math.min((this.getWidth() - insets.left - insets.right)
+				/ (TriangleModel.MAX_SIDE + 2),
+				(this.getHeight() - insets.top - insets.bottom)
+						/ (TriangleModel.MAX_SIDE + 2));
+
+		double base = this.model.getBase();
+		double height = this.model.getHeight();
+		double hypo = this.model.getHypotenuse();
+
+		int left = this.toX(0);
+		int right = this.toX(base);
+		int bottom = this.toY(0);
+		int top = this.toY(height);
+
+		// Draw the triangle
+		g.setColor(Color.black);
+		g.drawLine(left, bottom, right, bottom);
+		g.drawLine(right, bottom, right, top);
+		g.drawLine(left, bottom, right, top);
+
+		// Label the edges
+		g.drawString(formatter.format(base), left + (right - left) / 2, bottom);
+		g.drawString(formatter.format(height), right, top + (bottom - top) / 2);
+		g.drawString(formatter.format(hypo), left + (right - left) / 2 - 15,
+				top + (bottom - top) / 2 - 15);
+
+		/** Draw "handles" for resizing the triangle. */
+		// g.fillRect(right - handleSize, bottom - handleSize, 
+		// 	handleSize * 2, handleSize * 2);
+		g.fillRect(right - handleSize, top - handleSize, 
+			handleSize * 2, handleSize * 2);
+
+	}
+
+	/** Convert from the model's X coordinate to the component's X coordinate. */
+	protected int toX(double modelX) {
+		return (int) (modelX * this.scale) + this.getInsets().left;
+	}
+
+	/** Convert from the model's Y coordinate to the component's Y coordinate. */
+	protected int toY(double modelY) {
+		return this.getHeight() - (int) (modelY * this.scale) - 1
+				- this.getInsets().bottom;
+	}
+
+	/** Convert from the component's X coordinate to the model's X coordinate. */
+	protected double fromX(int x) {
+		return (x - this.getInsets().left) / this.scale;
+	}
+
+	/** Convert from the component's Y coordinate to the model's Y coordinate. */
+	protected double fromY(int y) {
+		return (this.getHeight() - 1 - this.getInsets().bottom - y)
+				/ this.scale;
+	}
+
+	/**
+	 * Does the given point (screen coordinates) lie on the upper right corner
+	 * of the triangle?
+	 */
+	private boolean onTopCorner(int x, int y) {
+		return Math.abs(fromX(x) - this.model.getBase()) < handleSize
+				&& Math.abs(fromY(y) - this.model.getHeight()) < handleSize;
+	}
+
+	private class MController extends MouseInputAdapter {
+
+		private boolean selected = false; // did the user select the triangle to
+		// resize it?
+
+		public void mousePressed(MouseEvent e) {
+			selected = onTopCorner(e.getX(), e.getY());
+		}
+
+		// public void mouseReleased(MouseEvent e) {
+		// 	selected = onTopCorner(e.getX(), e.getY());
+		// }
+
+		/** The user is dragging the mouse. Resize appropriately. */
+		public void mouseDragged(MouseEvent e) {
+			if (selected) {
+				model.setBase(fromX(e.getX()));
+				model.setHeight(fromY(e.getY()));
+			}
+		} 
+	} // MController
+} // GraphicalView
+
diff --git a/java/3-0-MVC/triangle/view/SimpleTextView.java b/java/3-0-MVC/triangle/view/SimpleTextView.java
new file mode 100644
index 0000000000000000000000000000000000000000..ee68ab08224d2f7a5cb98baff170475cb514a706
--- /dev/null
+++ b/java/3-0-MVC/triangle/view/SimpleTextView.java
@@ -0,0 +1,73 @@
+package view;
+
+import model.IView;
+import model.TriangleModel;
+
+import javax.swing.*;
+import java.awt.event.*;
+import java.text.NumberFormat;
+
+/*
+ * View a triangle as text:  numbers for the base, height, and hypotenuse.
+ * This view leaves a lot to be desired in terms of "polish".
+ * -- inconsistent decimal precision displayed, esp. in hypotenuse
+ * -- tabbing or clicking out of a text field doesn't update
+ * -- can edit the hypotenuse field
+ * 
+ * @author Byron Weber Becker
+ */
+public class SimpleTextView extends JPanel implements IView {
+
+	private TriangleModel model;
+	
+	private JTextField baseTF = new JTextField(10);
+	private JTextField heightTF = new JTextField(10);
+	private JTextField hypoTF = new JTextField(10);
+
+	public SimpleTextView(TriangleModel aModel) {
+		super();
+		this.model = aModel;
+		this.layoutView();
+		this.registerControllers();
+
+		// Add a this view as a listener to the model
+		this.model.addView(this);
+	}
+	
+	/**
+	 * What to do when the model changes.
+	 */
+	public void updateView() {
+		baseTF.setText("" + model.getBase());
+		heightTF.setText("" + model.getHeight());
+		hypoTF.setText("" + model.getHypotenuse());
+	}
+
+	private void layoutView() {
+		this.setLayout(new FormLayout());
+		this.add(new JLabel("Base:"));
+		this.add(this.baseTF);
+		this.add(new JLabel("Height:"));
+		this.add(this.heightTF);
+		this.add(new JLabel("Hypotenuse:"));
+		this.add(this.hypoTF);
+	}
+
+	private void registerControllers() {
+		// Add a controller to interpret user actions in the base text field
+		this.baseTF.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent evt) {
+				double base = Double.parseDouble(baseTF.getText());
+				model.setBase(base);
+			}
+		});
+
+		// Add a controller to interpret user actions in the height text field
+		this.heightTF.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent evt) {
+				double height = Double.parseDouble(heightTF.getText());
+				model.setHeight(height);
+			}
+		});
+	}
+}
\ No newline at end of file
diff --git a/java/3-0-MVC/triangle/view/SliderView.java b/java/3-0-MVC/triangle/view/SliderView.java
new file mode 100644
index 0000000000000000000000000000000000000000..0384158719f23b683aee56d2b944fb9f8877cfc1
--- /dev/null
+++ b/java/3-0-MVC/triangle/view/SliderView.java
@@ -0,0 +1,68 @@
+package view;
+
+import model.*;
+
+import javax.swing.*;
+import javax.swing.event.*;
+
+public class SliderView extends JPanel {
+	private TriangleModel model;
+	private JSlider baseSlider = new JSlider(0, (int) TriangleModel.MAX_HYPO);
+	private JSlider heightSlider = new JSlider(0, (int) TriangleModel.MAX_HYPO);
+	private JSlider hypoSlider = new JSlider(0, (int) TriangleModel.MAX_HYPO);
+
+	public SliderView(TriangleModel aModel) {
+		super();
+		this.model = aModel;
+		this.layoutView();
+		this.registerControllers();
+		this.model.addView(new IView() {
+			public void updateView() {
+				baseSlider.setValue((int) model.getBase());
+				heightSlider.setValue((int) model.getHeight());
+				hypoSlider.setValue((int) model.getHypotenuse());
+			}
+		});
+	}
+
+	private void layoutView() {
+		this.setLayout(new FormLayout());
+		this.add(new JLabel("Base:"));
+		this.add(this.baseSlider);
+		this.add(new JLabel("Height:"));
+		this.add(this.heightSlider);
+		this.add(new JLabel("Hypotenuse:"));
+		this.add(this.hypoSlider);
+
+		this.baseSlider.setMajorTickSpacing((int) TriangleModel.MAX_HYPO / 10);
+		this.baseSlider.setPaintTicks(true);
+
+		this.heightSlider
+				.setMajorTickSpacing((int) TriangleModel.MAX_HYPO / 10);
+		this.heightSlider.setPaintTicks(true);
+
+		this.hypoSlider.setMajorTickSpacing((int) TriangleModel.MAX_HYPO / 10);
+		this.hypoSlider.setPaintTicks(true);
+
+		this.hypoSlider.setEnabled(false);
+	}
+
+	private void registerControllers() {
+		this.baseSlider.addChangeListener(new BaseController());
+		this.heightSlider.addChangeListener(new HeightController());
+	}
+
+	private class BaseController implements ChangeListener {
+		public void stateChanged(ChangeEvent e) {
+			double base = baseSlider.getValue();
+			model.setBase(base);
+		}
+	}
+
+	private class HeightController implements ChangeListener {
+		public void stateChanged(ChangeEvent e) {
+			double height = heightSlider.getValue();
+			model.setHeight(height);
+		}
+	}
+}
diff --git a/java/3-0-MVC/triangle/view/SpinnerView.java b/java/3-0-MVC/triangle/view/SpinnerView.java
new file mode 100644
index 0000000000000000000000000000000000000000..095eee1a50c99e2390c77566e769618522484a4b
--- /dev/null
+++ b/java/3-0-MVC/triangle/view/SpinnerView.java
@@ -0,0 +1,72 @@
+package view;
+
+import java.text.NumberFormat;
+
+import model.*;
+
+import javax.swing.*;
+import javax.swing.event.*;
+
+public class SpinnerView extends JPanel {
+	private TriangleModel model;
+
+	// The spinner requires a number model that specifies the starting
+	// value, the minimum, maximum, and step size.
+	private JSpinner base = new JSpinner(new SpinnerNumberModel(1, 1,
+			TriangleModel.MAX_SIDE, 1));
+	private JSpinner height = new JSpinner(new SpinnerNumberModel(1, 1,
+			TriangleModel.MAX_SIDE, 1));
+	private JLabel hypo = new JLabel();
+	private static final NumberFormat formatter = NumberFormat
+			.getNumberInstance();
+
+	public SpinnerView(TriangleModel aModel) {
+		super();
+		this.model = aModel;
+		this.layoutView();
+		this.registerControllers();
+
+		this.model.addView(new IView() {
+			public void updateView() {
+				base.setValue(new Double(model.getBase()));
+				height.setValue(new Double(model.getHeight()));
+				hypo.setText(formatter.format(model.getHypotenuse()));
+			}
+		});
+
+	}
+
+	private void layoutView() {
+		this.setLayout(new FormLayout());
+		this.add(new JLabel("Base:"));
+		this.add(this.base);
+		this.add(new JLabel("Height:"));
+		this.add(this.height);
+		this.add(new JLabel("Hypotenuse:"));
+		this.add(this.hypo);
+
+	}
+
+	private void registerControllers() {
+		// Instantiate an anonymous ChangeListener class.
+		// Then add it to both spinners. Having one class
+		// that handles both spinners is one style; I prefer
+		// having separate listeners, one for each widget.
+		ChangeListener sc = new ChangeListener() {
+			public void stateChanged(ChangeEvent e) {
+				JSpinner src = (JSpinner) e.getSource();
+				double val = ((Double) src.getValue()).doubleValue();
+				if (src == SpinnerView.this.base) {
+					SpinnerView.this.model.setBase(val);
+				} else if (src == SpinnerView.this.height) {
+					SpinnerView.this.model.setHeight(val);
+				} else {
+					assert false;
+				}
+			}
+		};
+
+		this.base.addChangeListener(sc);
+		this.height.addChangeListener(sc);
+	}
+}
\ No newline at end of file
diff --git a/java/3-0-MVC/triangle/view/TextView.java b/java/3-0-MVC/triangle/view/TextView.java
new file mode 100644
index 0000000000000000000000000000000000000000..e09ecc3f5f4ef31d5d1abeef57680980968bac2e
--- /dev/null
+++ b/java/3-0-MVC/triangle/view/TextView.java
@@ -0,0 +1,112 @@
+package view;
+
+import model.IView;
+import model.TriangleModel;
+
+import javax.swing.*;
+import java.awt.event.*;
+import java.text.NumberFormat;
+
+/*
+ * View a triangle as text:  numbers for the base, height, and hypotenuse.
+ * 
+ * @author Byron Weber Becker
+ */
+public class TextView extends JPanel {
+
+	private TriangleModel model;
+
+	private JTextField baseTF = new JTextField(10);
+	private JTextField heightTF = new JTextField(10);
+	private JTextField hypoTF = new JTextField(10);
+
+	// To format numbers consistently in the text fields.
+	private static final NumberFormat formatter = NumberFormat
+			.getNumberInstance();
+
+	public TextView(TriangleModel aModel) {
+		super();
+		this.model = aModel;
+		this.layoutView();
+		this.registerControllers();
+
+		// There's no need to do this for such a simple view, but for a complex
+		// view you might have several ViewUpdate objects registered with
+		// the model.
+		this.model.addView(new IView() {
+			public void updateView() {
+				baseTF.setText(formatter.format(model.getBase()));
+				heightTF.setText(formatter.format(model.getHeight()));
+			}
+		});
+
+		this.model.addView(new IView() {
+			public void updateView() {
+				hypoTF.setText(formatter.format(model.getHypotenuse()));
+			}
+		});
+	}
+
+	private void layoutView() {
+		this.setLayout(new FormLayout());
+		this.add(new JLabel("Base:"));
+		this.add(this.baseTF);
+		this.add(new JLabel("Height:"));
+		this.add(this.heightTF);
+		this.add(new JLabel("Hypotenuse:"));
+		this.add(this.hypoTF);
+
+		// Don't allow the user to edit the hypotenuse
+		this.hypoTF.setEditable(false); // can still select and copy
+		//this.hypoTF.setEnabled(false); // greyed out, can't select
+	}
+
+	private void registerControllers() {
+		this.baseTF.addActionListener(new BaseController());
+		this.baseTF.addFocusListener(new BaseFocusController());
+
+		CombinedHeightController hc = new CombinedHeightController();
+		this.heightTF.addActionListener(hc);
+		this.heightTF.addFocusListener(hc);
+	}
+
+	private class BaseController implements ActionListener {
+		public void actionPerformed(ActionEvent evt) {
+			double base = Double.parseDouble(baseTF.getText());
+			model.setBase(base);
+		}
+	}
+
+	private class BaseFocusController implements FocusListener {
+		public void focusGained(FocusEvent evt) {
+			//baseTF.selectAll();
+		}
+
+		public void focusLost(FocusEvent evt) {
+			// Note duplicated code.
+			double value = Double.parseDouble(baseTF.getText());
+			model.setBase(value);
+		}
+	}
+
+	private class CombinedHeightController implements ActionListener,
+			FocusListener {
+		private void setHeight() {
+			double height = Double.parseDouble(heightTF.getText());
+			model.setHeight(height);
+		}
+
+		public void actionPerformed(ActionEvent evt) {
+			this.setHeight();
+		}
+
+		public void focusGained(FocusEvent evt) {
+			//heightTF.selectAll();
+		}
+
+		public void focusLost(FocusEvent evt) {
+			this.setHeight();
+			baseTF.requestFocus(); // skip over hypotenuse
+		}
+	}
+}
\ No newline at end of file