JMU
GUI Layout Using the M-V-C Pattern
A Discussion with Examples in Java


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Motivation
Using the M-V-C Pattern
An Abstract Design
Possible Ways to Access Components in the Model
The Model Interfaces
javaexamples/gui/mvc/JContainerModel.java
        import java.awt.*;
import javax.swing.*;


/**
 * The requirements of a container that uses the MVC pattern
 * for layout.
 *
 * @version 1.0
 * @author  Prof. David Bernstein, James Madison University
 */
public interface JContainerModel
{
    /**
     * Gets a component in this container.
     *
     * @param name   The name of the component
     * @return       The component (or null)
     */
    public abstract JComponent getComponentNamed(String name);

    /**
     * Set the view associated with this container
     *
     * @param view   The view
     */
    public abstract void setJContainerView(JContainerView view);


    // These methods are implement by Container objects
    // and are included as part of this interface for
    // convenience
    public abstract Component add(Component comp);
    public abstract Component add(Component comp, int index);
    public abstract void      add(Component comp, Object constraints);
    public abstract Component add(String    name, Component comp);
    public abstract Color     getBackground();
    public abstract void      setFont(Font f);
    public abstract void      setLayout(LayoutManager mgr);
}
        
The View Interfaces
javaexamples/gui/mvc/JContainerView.java
        /**
 * The requirements of the view for a container that uses the MVC pattern
 * for layout.
 *
 * @version 1.0
 * @author  Prof. David Bernstein, James Madison University
 */
public interface JContainerView
{
    /**
     * Set the text for all of the tool tips
     */
    public abstract void setToolTipText();

    /**
     * Setup this view (Required by JContainerView)
     */
    public abstract void setupView();
}
        
Different Ways to Construct the Model and View
An Example: The Model
javaexamples/gui/mvc/JListEditor.java
        import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;



/**
 * A component used to edit a JList containing Strings.
 *
 * @version 2.0 (Uses the MVC Pattern)
 * @author  Prof. David Bernstein, James Madison University
 */
public class JListEditor extends    JPanel
                         implements ActionListener, DocumentListener,
                                    ListSelectionListener, JContainerModel
{
  private static final long serialVersionUID = 1L;
  private static final String DEFAULT_TITLE = "Current Entries:";
  
  protected DefaultListModel<String>     lines;
  protected HashMap<String, JComponent>  components;
  protected JButton                      deleteButton, addButton;
  protected JLabel                       titleLabel;
  protected JList<String>                list;
  protected JContainerView               view;
  protected JTextField                   valueField;
  protected String                       title;

  /**
   * Construct a new JListEditor that displays 
   * the elements in the specified non-null model.
   *
   * @param dataModel  The (non-null) model to use
   *
   */
  public JListEditor(ListModel<String> dataModel)
  {
    super();

    list  = new JList<String>(dataModel);
    lines = (DefaultListModel<String>)dataModel;
    constructComponents();
  }

  /**
   * Construct a new JListEditor that displays 
   * the elements in the specified array.
   *
   * @param listData The array of elements to use
   *
   */
  public JListEditor(String[] listData)
  {
    super();

    list = new JList<String>(listData);
    lines = (DefaultListModel<String>)list.getModel();
    constructComponents();
  }

  /**
   * Construct a new JListEditor with an empty model.
   */
  public JListEditor()
  {
    super();

    lines = new DefaultListModel<String>();
    list = new JList<String>(lines);
    constructComponents();
  }

  /**
   * Handle actionPerformed messages.
   *
   * @param e     The ActionEvent that generated the message.
   */
  public void actionPerformed(ActionEvent e)
  {
    String ac = e.getActionCommand();
    if  (ac.equals("add_JListEditor"))        handleAdd();
    else if (ac.equals("delete_JListEditor")) handleDelete();
  }

  /**
   * Add a ListSelectionListener.
   *
   * @param listener   The ListSelectionListener to add
   */
  public void addListSelectionListener(ListSelectionListener listener)
  {
    list.addListSelectionListener(listener);
  }

  /**
   * Add a new line.
   *
   * @param value   The value to add
   */
  private void addLine(String value)
  {
    lines.addElement(value);
  }

  /**
   * Handle changedUpdate messages. 
   *
   * @param evt   The DocuementEvent that generated the message
   */
  public void changedUpdate(DocumentEvent evt)
  {
    // Do nothing
  }

  /**
   * Check to see if all of the fields are filled-in.
   */
  public void checkFields()
  {
    if (!valueField.getText().equals("")) 
    {
      addButton.setEnabled(true);
      deleteButton.setEnabled(false);
    }
  }

  /**
   * Construct the components used by this JListEditor
   */
  protected void constructComponents()
  {
    components = new HashMap<String, JComponent>();

    list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
    list.addListSelectionListener(this);
    components.put("list", list);

    valueField = new JTextField();
    valueField.getDocument().addDocumentListener(this);
    components.put("valueField", valueField);

    addButton = new JButton("Add");
    addButton.setEnabled(false);
    addButton.setActionCommand("add_JListEditor");
    addButton.addActionListener(this);
    components.put("addButton", addButton);

    deleteButton = new JButton("Delete");
    deleteButton.setEnabled(false);
    deleteButton.setActionCommand("delete_JListEditor");
    deleteButton.addActionListener(this);
    components.put("deleteButton", deleteButton);

    title = DEFAULT_TITLE;
    titleLabel = new JLabel(title, JLabel.LEFT);
    components.put("title", titleLabel);

    checkFields();

    // Set the view
    //setJContainerView(new JListEditorTraditionalView(this));
    setJContainerView(new JListEditorModernView(this));
  }

  /**
   * Gets a component in this container.
   *
   * @param name   The name of the component
   * @return       The component (or null)
   */
  public JComponent getComponentNamed(String name)
  {
    return (JComponent)components.get(name);
  }

  /**
   * Returns the data model that holds the list 
   * of items displayed by the JList component.
   *
   * @return The ListModel that provides the list of items
   */
  public ListModel<String> getModel()
  {
    return list.getModel();
  }

  /**
   * Returns the index of the currently selected 
   * item in the list.
   *
   * @return The index of the currently selected item
   */
  public int getSelectedIndex()
  {
    return list.getSelectedIndex();
  }

  /**
   * Returns the indices of the currently selected items.
   *
   * @return The indices of the currently selected items
   */
  public int[] getSelectedIndices()
  {
    return list.getSelectedIndices();
  }

  /**
   * Returns the currently selected item in the list.
   *
   * @return The currently selected item
   */
  public Object getSelectedValue()
  {
    return list.getSelectedValue();
  }

  /**
   * Return and array containing the currently selected items.
   *
   * @return The currently selected items
   */
  public java.util.List<String> getSelectedValuesList()
  {
    return list.getSelectedValuesList();
  }

  /**
   * Handle the Delete button.
   */
  private void handleDelete()
  {
    int i = list.getSelectedIndex();

    if (i >= 0) 
    {
      lines.removeElementAt(i);
      deleteButton.setEnabled(false);
      addButton.setEnabled(false);
      valueField.setText("");
    }
  }

  /**
   * Handle the Add button.
   */
  private void handleAdd()
  {
    String value = valueField.getText();

    if ( !value.equals(""))  
    {
      for (int i=0; i < lines.size(); i++) 
      {
        String s = (String)lines.elementAt(i);

        if (s.equals(value) ) {

          lines.removeElementAt(i);
          break; // Out of the for loop
        }
      }
      addLine(value);

      addButton.setEnabled(false);
      list.setSelectedIndex(lines.size()-1);
    }

    valueField.setText("");
  }

  /**
   * Handle insertUpdate messages.
   *
   * @param evt   The DocumentEvent that generated the message
   */
  public void insertUpdate(DocumentEvent evt)
  {
    // Regardless of which field/document generated the event
    checkFields();
  }

  /**
   * Handle removeUpdate messages.
   *
   * @param evt   The DocumentEvent that generated the message
   */
  public void removeUpdate(DocumentEvent evt)
  {
    // Regardless of which field/document generated the event
    checkFields();
  }

  /**
   * Sets the background color for this JListEditor.
   *
   * @param c   The Color to use
   */
  public void setBackground(Color c)
  {
    Component[]   comps;
    int           i;

    super.setBackground(c);
    comps = getComponents();
    for (i=0; i < comps.length; i++) 
    {
      comps[i].setBackground(c);
    }
  }

  /**
   * Set the view associated with this container.
   *
   * @param view   The view
   */
  public void setJContainerView(JContainerView view)
  {
    this.view = view;
    view.setupView();
    view.setToolTipText();
    invalidate();
    validate();
  }

  /**
   * Set the title of this JListEditor.
   *
   * @param title The new title 
   */
  public void setTitle(String title)
  {
    this.title = title;
    titleLabel.setText(title);
  }

  /**
   * Handle valueChanged messages.
   * 
   * @param lse The ListSelectionEvent that generated the message 
   */
  public void valueChanged(ListSelectionEvent lse)
  {
    int i = list.getSelectedIndex();

    if (i >= 0) 
    {
      String s = (String)list.getSelectedValue();
      valueField.setText(s);
      checkFields();

      addButton.setEnabled(false);
      deleteButton.setEnabled(true);
    } 
    else 
    {
      deleteButton.setEnabled(false);
    }
  }

}
        
An Example (cont.): An Abstract View
javaexamples/gui/mvc/JListEditorView.java
        import javax.swing.*;


/**
 * An abstract "view" for a JListEditor.
 *
 * @version 2.0 (Uses the MVC Pattern)
 * @author  Prof. David Bernstein, James Madison University
 */
public abstract class JListEditorView implements JContainerView
{
  protected JContainerModel       container;


  /**
   * Explicit Value Constructor.
   *
   * @param jle    The JListEditor (i.e., the JContainerModel)
   */
  public JListEditorView(JListEditor jle)
  {
    this.container = jle;
  }


  /**
   * Set the text for all of the tool tips.
   */
  public void setToolTipText()
  {
    setToolTipText("list", "The current list of items.");
    setToolTipText("valueField", "Enter a new item here.");
    setToolTipText("addButton", "Add the new item.");
    setToolTipText("deleteButton", "Delete the selected entry.");
  }

  /**
   * Set the tool tip text for a particular component.
   *
   * @param name  The name of the component
   * @param text  The text of the tool tip
   */
  protected void setToolTipText(String name, String text)
  {
    JComponent     c;

    c = container.getComponentNamed(name);
    if (c != null) c.setToolTipText(text);
  }

  /**
   * Setup this view (Required by JContainerView)
   */
  public abstract void setupView();

}


        
An Example (cont.): One Concrete View
javaexamples/gui/mvc/JListEditorTraditionalView.java
        import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;

/**
 * A traditional "view" for a JListEditor.
 *
 * @version 2.0 (Uses the MVC Pattern)
 * @author  Prof. David Bernstein, James Madison University
 */
public class JListEditorTraditionalView extends JListEditorView
{
  /**
   * Explicit Value Constructor
   *
   * @param jle    The JListEditor (i.e., the JContainerModel)
   */
  public JListEditorTraditionalView(JListEditor jle)
  {
    super(jle);
  }



  /**
   * Setup this view (Required by JContainerView)
   */
  public void setupView()
  {
    JComponent    c;
    JPanel        center, south, s1, s2;
    JScrollPane   listPane;


    container.setFont(new Font("Sans Serif",Font.PLAIN,12));

    container.setLayout(new BorderLayout());

    c = container.getComponentNamed("titleLabel");
    if (c != null) container.add(c, BorderLayout.NORTH);

    c = container.getComponentNamed("list");
    if (c != null) 
    {
      center = new JPanel();
      center.setLayout(new BorderLayout());
      center.setBorder(BorderFactory.createBevelBorder(
          BevelBorder.LOWERED));
      listPane = new JScrollPane(c);
      center.add(listPane, BorderLayout.CENTER);
      container.add(center, BorderLayout.CENTER);
    }


    south = new JPanel();
    south.setLayout(
        new BoxLayout(south, BoxLayout.Y_AXIS));
    south.setBorder(
        BorderFactory.createMatteBorder(6,6,6,6,
            container.getBackground()));

    s1 = new JPanel();
    s1.setLayout(new BorderLayout());
    c = container.getComponentNamed("valueField");
    if (c!= null) s1.add(c);

    s2 = new JPanel();
    s2.setLayout(new BoxLayout(s2, BoxLayout.X_AXIS));
    s2.add(Box.createHorizontalGlue());
    c = container.getComponentNamed("addButton");
    if (c != null) s2.add(c);
    c = container.getComponentNamed("deleteButton");
    if (c != null) s2.add(c);

    south.add(Box.createVerticalStrut(10));
    south.add(s1);
    south.add(Box.createVerticalStrut(5));
    south.add(s2);
    south.add(Box.createVerticalStrut(10));

    container.add(south, BorderLayout.SOUTH);
    container.add(new JLabel("  "), BorderLayout.EAST);
    container.add(new JLabel("  "), BorderLayout.WEST);
  }
}

        
An Example (cont.): Another Concrete View
javaexamples/gui/mvc/JListEditorModernView.java
        import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;


/**
 * A modern "view" for a JListEditor.
 *
 * @version 2.0 (Uses the MVC Pattern)
 * @author  Prof. David Bernstein, James Madison University
 */
public class JListEditorModernView extends JListEditorView
{

  /**
   * Explicit Value Constructor.
   *
   * @param jle    The JListEditor (i.e., the JContainerModel)
   */
  public JListEditorModernView(JListEditor jle)
  {
    super(jle);
  }

  /**
   * Setup this view.
   */
  public void setupView()
  {
    JComponent    c;
    JPanel        center, north, n1, n2, south, s1;
    JScrollPane   listPane;


    container.setFont(new Font("Sans Serif",Font.PLAIN,12));

    container.setLayout(new BorderLayout());

    c = container.getComponentNamed("titleLabel");
    if (c != null) container.add(c, BorderLayout.NORTH);

    c = container.getComponentNamed("list");
    if (c != null) 
    {
      center = new JPanel();
      center.setLayout(new BorderLayout());
      center.setBorder(BorderFactory.createBevelBorder(
          BevelBorder.LOWERED));
      listPane = new JScrollPane(c);
      center.add(listPane, BorderLayout.CENTER);
      container.add(center, BorderLayout.CENTER);
    }

    south = new JPanel();
    south.setLayout(
        new BoxLayout(south, BoxLayout.Y_AXIS));
    south.setBorder(
        BorderFactory.createMatteBorder(6,6,6,6,
            container.getBackground()));

    s1 = new JPanel();
    s1.setLayout(new BoxLayout(s1, BoxLayout.X_AXIS));
    s1.add(Box.createHorizontalGlue());
    c = container.getComponentNamed("deleteButton");
    if (c != null) s1.add(c);


    south.add(Box.createVerticalStrut(10));
    south.add(s1);
    south.add(Box.createVerticalStrut(10));


    north = new JPanel();
    north.setLayout(
        new BoxLayout(north, BoxLayout.Y_AXIS));
    north.setBorder(
        BorderFactory.createMatteBorder(6,6,6,6,
            container.getBackground()));

    n1 = new JPanel();
    n1.setLayout(new BorderLayout());
    c = container.getComponentNamed("valueField");
    if (c!= null) n1.add(c, BorderLayout.CENTER);


    n2 = new JPanel();
    n2.setLayout(new BoxLayout(n2, BoxLayout.X_AXIS));
    n2.add(Box.createHorizontalGlue());
    c = container.getComponentNamed("addButton");
    if (c != null) n2.add(c);


    north.add(Box.createVerticalStrut(10));
    north.add(n1);
    north.add(Box.createVerticalStrut(5));
    north.add(n2);
    north.add(Box.createVerticalStrut(10));


    container.add(north, BorderLayout.NORTH);
    container.add(south, BorderLayout.SOUTH);
    container.add(new JLabel("  "), BorderLayout.EAST);
    container.add(new JLabel("  "), BorderLayout.WEST);
  }
}


        
Adding Flexibility