JMU
Using the Command Pattern to Decentralize Control
An Introduction with Examples in Java


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


The Setting
A Noncohesive Design
images/TextEditor_NotCohesive.gif
An Implementation of the Noncohesive Design
javaexamples/action/notcohesive/TextEditorApplication.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;

/**
 * An example of an encapsulation of a text editor
 * that is not cohesive.
 * 
 *  @author Prof. David Bernstein, James Madison University
 */
public class TextEditorApplication 
  implements ActionListener, CaretListener, MouseListener
{
  private Caret              caret;
  private JButton            copyButton, cutButton, pasteButton;
  private JFrame             window;
  private JMenu              menu;
  private JMenuBar           menuBar;
  private JMenuItem          copyItem, cutItem, pasteItem;
  private JMenuItem          copyPopup, cutPopup, pastePopup;
  private JPopupMenu         popup;    
  private JTextArea          ta;
  private JToolBar           toolBar;
  private String             clipboard;
  
  /**
   * The entry point of the application.
   * 
   * @param args  The command line arguments (which are ignored)
   */
  public static void main(String[] args)
  {
    new TextEditorApplication();
  }
  
  /**
   * Default Constructor.
   */
  public TextEditorApplication()
  {
    window = new JFrame();
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
    clipboard = null;

    ta = new JTextArea();
    caret = ta.getCaret();
    ta.setLineWrap(true);
    ta.setWrapStyleWord(true);
    ta.addMouseListener(this);
    ta.addCaretListener(this);

    constructControls();
    performLayout();

    disableControls();
    window.setVisible(true);
  }
  
  /**
   * Handle actionPerformed messages.
   * 
   * @param ae  The event that generated the message
   */
  public void actionPerformed(ActionEvent ae)
  {
    String ac = ae.getActionCommand();
    
    if      (ac.equals("Copy"))  copy();
    else if (ac.equals("Cut"))   cut();
    else if (ac.equals("Paste")) paste();
  }
  
  /**
   * Handle caretUpdate messages.
   *
   * @param evt   The event that generated the message
   */
  public void caretUpdate(CaretEvent evt)
  {
    disableControls();
    
    if (clipboard != null)
    {
      pasteButton.setEnabled(true);
      pasteItem.setEnabled(true);
      pastePopup.setEnabled(true);
    }
    
    if (ta.getSelectedText() != null)
    {
      cutButton.setEnabled(true);
      cutItem.setEnabled(true);
      cutPopup.setEnabled(true);

      copyButton.setEnabled(true);
      copyItem.setEnabled(true);
      copyPopup.setEnabled(true);
    }
  }
  
  /**
   * Construct the JPopupMenu, JMenu, and JToolBar.
   */
  private void constructControls()
  {
      popup   = new JPopupMenu();
      menu    = new JMenu("Edit");
      toolBar = new JToolBar("Edit");

      ImageIcon icon;
      
      // Copy controls
      icon = new ImageIcon("copy.gif");
      copyItem = constructJMenuItem("Copy", icon);
      menu.add(copyItem);

      copyPopup = constructJMenuItem("Copy", icon);
      popup.add(copyPopup);
  
      copyButton = constructJButton("Copy", icon);
      toolBar.add(copyButton);
      
      // Cut controls
      icon = new ImageIcon("cut.gif");
      cutItem = constructJMenuItem("Cut", icon);
      menu.add(cutItem);

      cutPopup = constructJMenuItem("Cut", icon);
      popup.add(cutPopup);
  
      cutButton = constructJButton("Cut", icon);
      toolBar.add(cutButton);
      
      // Paste controls
      icon = new ImageIcon("paste.gif");
      pasteItem = constructJMenuItem("Paste", icon);
      menu.add(pasteItem);

      pastePopup = constructJMenuItem("Paste", icon);
      popup.add(pastePopup);
  
      pasteButton = constructJButton("Paste", icon);
      toolBar.add(pasteButton);
      
      
      menuBar = new JMenuBar();
      menuBar.add(menu);
  }

  /**
   * Construct a JButton.
   * 
   * @param actionCommand  The action command to use
   * @param icon           The Icon to use
   * @return               The JButton
   */
  private JButton constructJButton(String actionCommand, Icon icon)
  {
    JButton button = new JButton(actionCommand, icon);
    button.addActionListener(this);
    return button;
  }

  /**
   * Construct a JMenuItem.
   * 
   * @param actionCommand  The action command to use
   * @param icon           The Icon to use
   * @return               The JButton
   */
  private JMenuItem constructJMenuItem(String actionCommand, Icon icon)
  {
    JMenuItem item = new JMenuItem(actionCommand, icon);
    item.addActionListener(this);
    return item;
  }    

  /**
   * Copy text to the "clipboard".
   */
  private void copy()
  {
      int  dot, mark;
      
      dot  = caret.getDot();        
      mark = caret.getMark();
      clipboard = ta.getSelectedText();
      ta.select(-1, -1);
      caret.setDot(mark); // Causes a CaretEvent to be fired
      caret.setDot(dot);  // Positions the Caret at the right position
      ta.requestFocus();
  }

  /**
   * Cut text to the "clipboard".
   */
  private void cut()
  {
      int dot;        

      dot = caret.getDot();        
      clipboard = ta.getSelectedText();
      ta.replaceSelection(null);
      ta.select(-1, -1);
      caret.setDot(dot);
      ta.requestFocus();
  }
  
  /**
   * Disable all of the controls.
   */
  private void disableControls()
  {
    copyButton.setEnabled(false);
    copyItem.setEnabled(false);
    copyPopup.setEnabled(false);

    cutButton.setEnabled(false);
    cutItem.setEnabled(false);
    cutPopup.setEnabled(false);

    pasteButton.setEnabled(false);
    pasteItem.setEnabled(false);
    pastePopup.setEnabled(false);
  }
  
  /**
   * Handle mouseClicked messages.
   *
   * @param evt   The event that generated the message
   */
  public void mouseClicked(MouseEvent evt)
  {
  }

  /**
   * Handle mouseEntered messages.
   *
   * @param evt   The event that generated the message
   */
  public void mouseEntered(MouseEvent evt)
  {
  }

  /**
   * Handle mouseExited messages.
   *
   * @param evt   The event that generated the message
   */
  @Override
  public void mouseExited(MouseEvent evt)
  {
  }

  /**
   * Handle mousePressed messages.
   *
   * @param evt   The event that generated the message
   */
  @Override
  public void mousePressed(MouseEvent evt)
  {
    if (evt.isPopupTrigger() && !popup.isVisible()) 
    {
        Point    p;

        p = evt.getPoint();           
        popup.show(window, p.x, p.y);
    }
  }

  /**
   * Handle mouseReleased messages.
   *
   * @param evt   The event that generated the message
   */
  @Override
  public void mouseReleased(MouseEvent evt)
  {
     if (evt.isPopupTrigger() && !popup.isVisible()) 
     {
         Point    p;

         p = evt.getPoint();           
         popup.show(window, p.x, p.y);
     }
  }

  /**
   * Paste text from the "clipboard".
   */
  private void paste()
  {
      int dot, offset;        

      dot = caret.getDot();
      offset = clipboard.length();
      
      ta.replaceSelection(clipboard);
      clipboard = null;

      caret.setDot(dot + offset);
      ta.requestFocus();
  }
  
  /**
   * Layout this component.
   */
  private void performLayout()
  {
    JPanel cp = (JPanel)window.getContentPane();
    cp.setLayout(new BorderLayout());
    window.setSize(400,400);

    JScrollPane scrollPane = new JScrollPane(ta);
    scrollPane.setHorizontalScrollBarPolicy(
              JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS
              );
    scrollPane.setVerticalScrollBarPolicy(
              JScrollPane.VERTICAL_SCROLLBAR_ALWAYS
              );
    cp.add(scrollPane, BorderLayout.CENTER);
    
    window.setJMenuBar(menuBar);

    JPanel toolbars = new JPanel(new FlowLayout(FlowLayout.LEFT));
    toolbars.add(toolBar);
    cp.add(toolbars, BorderLayout.NORTH);        
  }
}
        
Improving the Design
A Design with Centralized Control
images/TextEditor_Centralized.gif
An Implementation with Centralized Control
javaexamples/action/TextEditor.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;

/**
 * An encapsulation of a simple TextEditor that supports
 * copy, cut, and paste operations.
 * 
 * @author Prof. David Bernstein, James Madison University
 */
public class TextEditor extends JPanel
{
  private static final long serialVersionUID = 1L;
  
  private Caret              caret;
  private JTextArea          ta;
  private String             clipboard;
  
  /**
   * Default Constructor.
   */
  public TextEditor()
  {
    super();
    ta = new JTextArea();
    caret = ta.getCaret();
    ta.setLineWrap(true);
    ta.setWrapStyleWord(true);
    
    performLayout();
  }
  
  /**
   * Add a CaretListener.
   * 
   * @param cl  The CaretListener to add.
   */
  public void addCaretListener(CaretListener cl)
  {
    ta.addCaretListener(cl);
  }
  
  /**
   * Add a MouseListener.
   * 
   * @param ml  The MouseListener to add.
   */
  public void addMouseListener(MouseListener ml)
  {
    ta.addMouseListener(ml);
  }

  /**
   * Copy text to the "clipboard".
   */
  public void copy()
  {
      int  dot, mark;
      
      dot  = caret.getDot();        
      mark = caret.getMark();
      clipboard = ta.getSelectedText();
      ta.select(-1, -1);
      caret.setDot(mark); // Causes a CaretEvent to be fired
      caret.setDot(dot);  // Positions the Caret at the right position
      ta.requestFocus();
  }

  /**
   * Cut text to the "clipboard".
   */
  public void cut()
  {
      int dot;        

      dot = caret.getDot();        
      clipboard = ta.getSelectedText();
      ta.replaceSelection(null);
      ta.select(-1, -1);
      caret.setDot(dot);
      ta.requestFocus();
  }
  
  /**
   * Get the "clipboard" for this TextEditor.
   * 
   * @return The "clipboard"
   */
  public String getClipboard()
  {
    return clipboard;
  }
  
  /**
   * Get the currently selected text for this TextEditor.
   * 
   * @return The text
   */
  public String getSelectedText()
  {
    return ta.getSelectedText();
  }

  /**
   * Paste text from the "clipboard".
   */
  public void paste()
  {
      int dot, offset;        

      dot = caret.getDot();
      offset = clipboard.length();
      
      ta.replaceSelection(clipboard);
      clipboard = null;

      caret.setDot(dot + offset);
      ta.requestFocus();
  }
  
  /**
   * Layout this component.
   */
  private void performLayout()
  {
    setLayout(new BorderLayout());
    
    JScrollPane scrollPane = new JScrollPane(ta);
    scrollPane.setHorizontalScrollBarPolicy(
              JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS
              );
    scrollPane.setVerticalScrollBarPolicy(
              JScrollPane.VERTICAL_SCROLLBAR_ALWAYS
              );

    add(scrollPane, BorderLayout.CENTER);
  }
}
        
An Implementation with Centralized Control (cont.)
javaexamples/action/centralizedcontrol/TextEditorGUI.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

/**
 * An example of an encapsulation of a text editor
 * that separates the GUI from the actual editor.
 * The GUI handles all of the events centrally.
 * 
 *  @author Prof. David Bernstein, James Madison University
 */
public class TextEditorGUI 
  implements ActionListener, CaretListener, MouseListener
{
  private JButton            copyButton, cutButton, pasteButton;
  private JFrame             window;
  private JMenu              menu;
  private JMenuBar           menuBar;
  private JMenuItem          copyItem, cutItem, pasteItem;
  private JMenuItem          copyPopup, cutPopup, pastePopup;
  private JPopupMenu         popup;    
  private JToolBar           toolBar;
  private TextEditor         textEditor;
  
  /**
   * The entry point of the application.
   * 
   * @param args  The command line arguments (which are ignored)
   */
  public static void main(String[] args)
  {
    new TextEditorGUI();
  }
  
  /**
   * Default Constructor.
   */
  public TextEditorGUI()
  {
    window = new JFrame();
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
    textEditor = new TextEditor();
    textEditor.addCaretListener(this);
    textEditor.addMouseListener(this);

    constructControls();
    performLayout();

    disableControls();
    window.setVisible(true);
  }
  
  /**
   * Handle actionPerformed messages.
   * 
   * @param ae  The event that generated the message
   */
  public void actionPerformed(ActionEvent ae)
  {
    String ac = ae.getActionCommand();
    
    if      (ac.equals("Copy"))  textEditor.copy();
    else if (ac.equals("Cut"))   textEditor.cut();
    else if (ac.equals("Paste")) textEditor.paste();
  }
  
  /**
   * Handle caretUpdate messages.
   *
   * @param evt   The event that generated the message
   */
  public void caretUpdate(CaretEvent evt)
  {
    disableControls();
    
    if (textEditor.getClipboard() != null)
    {
      pasteButton.setEnabled(true);
      pasteItem.setEnabled(true);
      pastePopup.setEnabled(true);
    }
    
    if (textEditor.getSelectedText() != null)
    {
      cutButton.setEnabled(true);
      cutItem.setEnabled(true);
      cutPopup.setEnabled(true);

      copyButton.setEnabled(true);
      copyItem.setEnabled(true);
      copyPopup.setEnabled(true);
    }
  }
  
  /**
   * Construct the JPopupMenu, JMenu, and JToolBar.
   */
  private void constructControls()
  {
      popup   = new JPopupMenu();
      menu    = new JMenu("Edit");
      toolBar = new JToolBar("Edit");

      ImageIcon icon;
      
      // Copy controls
      icon = new ImageIcon("copy.gif");
      copyItem = constructJMenuItem("Copy", icon);
      menu.add(copyItem);

      copyPopup = constructJMenuItem("Copy", icon);
      popup.add(copyPopup);
  
      copyButton = constructJButton("Copy", icon);
      toolBar.add(copyButton);
      
      // Cut controls
      icon = new ImageIcon("cut.gif");
      cutItem = constructJMenuItem("Cut", icon);
      menu.add(cutItem);

      cutPopup = constructJMenuItem("Cut", icon);
      popup.add(cutPopup);
  
      cutButton = constructJButton("Cut", icon);
      toolBar.add(cutButton);
      
      // Paste controls
      icon = new ImageIcon("paste.gif");
      pasteItem = constructJMenuItem("Paste", icon);
      menu.add(pasteItem);

      pastePopup = constructJMenuItem("Paste", icon);
      popup.add(pastePopup);
  
      pasteButton = constructJButton("Paste", icon);
      toolBar.add(pasteButton);
      
      menuBar = new JMenuBar();
      menuBar.add(menu);
  }

  /**
   * Construct a JButton.
   * 
   * @param actionCommand  The action command to use
   * @param icon           The Icon to use
   * @return               The JButton
   */
  private JButton constructJButton(String actionCommand, Icon icon)
  {
    JButton button = new JButton(actionCommand, icon);
    button.addActionListener(this);
    return button;
  }

  /**
   * Construct a JMenuItem.
   * 
   * @param actionCommand  The action command to use
   * @param icon           The Icon to use
   * @return               The JButton
   */
  private JMenuItem constructJMenuItem(String actionCommand, Icon icon)
  {
    JMenuItem item = new JMenuItem(actionCommand, icon);
    item.addActionListener(this);
    return item;
  }    
  
  /**
   * Disable all of the controls.
   */
  private void disableControls()
  {
    copyButton.setEnabled(false);
    copyItem.setEnabled(false);
    copyPopup.setEnabled(false);

    cutButton.setEnabled(false);
    cutItem.setEnabled(false);
    cutPopup.setEnabled(false);

    pasteButton.setEnabled(false);
    pasteItem.setEnabled(false);
    pastePopup.setEnabled(false);
  }
  
  /**
   * Handle mouseClicked messages.
   *
   * @param evt   The event that generated the message
   */
  public void mouseClicked(MouseEvent evt)
  {
  }

  /**
   * Handle mouseEntered messages.
   *
   * @param evt   The event that generated the message
   */
  public void mouseEntered(MouseEvent evt)
  {
  }

  /**
   * Handle mouseExited messages.
   *
   * @param evt   The event that generated the message
   */
  public void mouseExited(MouseEvent evt)
  {
  }

  /**
   * Handle mousePressed messages.
   *
   * @param evt   The event that generated the message
   */
  public void mousePressed(MouseEvent evt)
  {
    if (evt.isPopupTrigger() && !popup.isVisible()) 
    {
        Point    p;

        p = evt.getPoint();           
        popup.show(window, p.x, p.y);
    }
  }

  /**
   * Handle mouseReleased messages.
   *
   * @param evt   The event that generated the message
   */
  public void mouseReleased(MouseEvent evt)
  {
     if (evt.isPopupTrigger() && !popup.isVisible()) 
     {
         Point    p;

         p = evt.getPoint();           
         popup.show(window, p.x, p.y);
     }
  }
  
  /**
   * Layout this component.
   */
  private void performLayout()
  {
    JPanel cp = (JPanel)window.getContentPane();
    cp.setLayout(new BorderLayout());
    window.setSize(400,400);

    cp.add(textEditor, BorderLayout.CENTER);
    
    window.setJMenuBar(menuBar);

    JPanel toolbars = new JPanel(new FlowLayout(FlowLayout.LEFT));
    toolbars.add(toolBar);
    cp.add(toolbars, BorderLayout.NORTH);        
  }
}
        
Decentralizing Control
A Design with Decentralized Control
images/command-pattern_actions.gif
An Implementation with Decentralized Control (cont.)
javaexamples/action/EditorAction.java
import javax.swing.*;
import javax.swing.event.*;


/**
 * An abstract action for an Editor
 *
 * @author  Prof. David Bernstein, James Madison University
 */
public abstract class EditorAction extends AbstractAction 
    implements CaretListener
{
  private static final long serialVersionUID = 1L;

  private TextEditor    editor;

  /**
   * Explicit Value Constructor.
   *
   * @param editor  The Editor that is the target of this command.
   */
  public EditorAction(TextEditor editor, String name, Icon icon)
  {
    super(name, icon);
    this.editor = editor;
    setEnabled(false);
    editor.addCaretListener(this);
  }

  /**
   * Handle caretUpdate messages.
   *
   * @param evt   The event that caused the message to be generated
   */
  public void caretUpdate(CaretEvent evt)
  {
    setEnabled(editor.getSelectedText() != null, editor.getClipboard() != null);
  }

  /**
   * Get the Editor associated with this EditorAction.
   *
   * @return  The Editor
   */
  protected TextEditor getEditor()
  {
    return this.editor;
  }

  /**
   * Set the enabled state of this Action based on whether
   * text is selected and whether the "clipboard" is populated.
   *
   * @param textIsSelected     true if there is text selected
   * @param textIsOnClipboard  true if the "clipboard" is populated
   */
  protected abstract void setEnabled(boolean textIsSelected, boolean textIsOnClipboard);

}
        
An Implementation with Decentralized Control (cont.)
javaexamples/action/CopyAction.java
import java.awt.event.*;
import javax.swing.*;

/**
 * A "copy action" for an Editor.
 *
 * @author  Prof. David Bernstein, James Madison University
 */
public class CopyAction extends EditorAction
{
  private static final long serialVersionUID = 1L;

  /**
   * Explicit Value Constructor
   *
   * @param editor    The editor to copy from
   */
  public CopyAction(TextEditor editor)
  {
    super(editor, "Copy", new ImageIcon("copy.gif"));
    putValue(Action.SHORT_DESCRIPTION, "Copy the selected text");
  }

  /**
   * Handle actionPerformed messages.
   *
   * @param ae    The event that caused the message to be generated
   */
  public void actionPerformed(ActionEvent ae)
  {
    getEditor().copy();
  }

  /**
   * Set the enabled state of this Action based on whether
   * text is selected and whether the "clipboard" is populated.
   *
   * @param textIsSelected     true if there is text selected
   * @param textIsOnClipboard  true if the "clipboard" is populated
   */
  protected void setEnabled(boolean textIsSelected, boolean textIsOnClipboard)
  {
    setEnabled(textIsSelected);
  }
}
        
An Implementation with Decentralized Control (cont.)
javaexamples/action/CutAction.java
import java.awt.event.*;
import javax.swing.*;

/**
 * A "cut action" for an Editor.
 *
 * @author  Prof. David Bernstein, James Madison University
 */
public class CutAction extends EditorAction
{
  private static final long serialVersionUID = 1L;

  /**
   * Explicit Value Constructor
   *
   * @param editor    The editor to cut from
   */
  public CutAction(TextEditor editor)
  {
    super(editor, "Cut", new ImageIcon("cut.gif"));

    putValue(Action.SHORT_DESCRIPTION, "Cut the selected text");
  }

  /**
   * Handle actionPerformed messages.
   *
   * @param ae    The event that caused the message to be generated
   */
  public void actionPerformed(ActionEvent ae)
  {
    getEditor().cut();
  }

  /**
   * Set the enabled state of this Action based on whether
   * text is selected and whether the "clipboard" is populated.
   *
   * @param textIsSelected     true if there is text selected
   * @param textIsOnClipboard  true if the "clipboard" is populated
   */
  protected void setEnabled(boolean textIsSelected, boolean textIsOnClipboard)
  {
    setEnabled(textIsSelected);
  }
}
        
An Implementation with Decentralized Control (cont.)
javaexamples/action/PasteAction.java
import java.awt.event.*;
import javax.swing.*;


/**
 * A "paste action" for a TextEditor.
 *
 * @author  Prof. David Bernstein, James Madison University
 */
public class PasteAction extends EditorAction
{
  private static final long serialVersionUID = 1L;

  /**
   * Explicit Value Constructor
   *
   * @param editor    The editor to paste to
   */
  public PasteAction(TextEditor editor)
  {
    super(editor, "Paste", new ImageIcon("paste.gif"));
    putValue(Action.SHORT_DESCRIPTION, "Paste from the clipboard");
  }

  /**
   * Handle actionPerformed messages.
   *
   * @param ae    The event that caused the message to be generated
   */
  public void actionPerformed(ActionEvent ae)
  {
    getEditor().paste();
  }

  /**
   * Set the enabled state of this Action based on whether
   * text is selected and whether the "clipboard" is populated.
   *
   * @param textIsSelected     true if there is text selected
   * @param textIsOnClipboard  true if the "clipboard" is populated
   */
  protected void setEnabled(boolean textIsSelected, boolean textIsOnClipboard)
  {
    setEnabled(textIsOnClipboard);
  }
}
        
An Implementation with Decentralized Control (cont.)
javaexamples/action/TextEditorGUI.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
 * An example of an encapsulation of a text editor
 * that separates the GUI from the actual editor.
 * 
 * In this case, the GUI distributes control to individual
 * Action objects.
 * 
 *  @author Prof. David Bernstein, James Madison University
 */
public class TextEditorGUI implements MouseListener
{
  private JFrame             window;
  private JMenu              menu;
  private JMenuBar           menuBar;
  private JPopupMenu         popup;    
  private JToolBar           toolBar;
  private TextEditor         textEditor;
  
  /**
   * The entry point of the application.
   * 
   * @param args  The command line arguments (which are ignored)
   */
  public static void main(String[] args)
  {
    new TextEditorGUI();
  }
  
  /**
   * Default Constructor.
   */
  public TextEditorGUI()
  {
    window = new JFrame();
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
    menuBar = new JMenuBar();
    
    textEditor = new TextEditor();
    textEditor.addMouseListener(this);

    CopyAction  copy  = new CopyAction(textEditor);
    CutAction   cut   = new CutAction(textEditor);
    PasteAction paste = new PasteAction(textEditor); 
    constructControls(copy, cut, paste); 
    
    performLayout();

    window.setVisible(true);
  }
  
  /**
   * Construct the JPopupMenu, JMenu, and JToolBar and
   * add an array of Action objects to them.
   */
  private void constructControls(Action... actions)
  {
    JButton     button;
    String      sd;

    popup   = new JPopupMenu();
    menu    = new JMenu("Edit");
    toolBar = new JToolBar("Edit");

    for (int i=0; i<actions.length; i++)
    {
      popup.add(actions[i]);
      menu.add(actions[i]);
      button = toolBar.add(actions[i]);

      sd  = (String)actions[i].getValue(Action.SHORT_DESCRIPTION);
      if (sd != null) button.setToolTipText(sd);
    }
    
    menuBar.add(menu);
  }
  
  /**
   * Handle mouseClicked messages.
   *
   * @param evt   The event that generated the message
   */
  public void mouseClicked(MouseEvent evt)
  {
  }

  /**
   * Handle mouseEntered messages.
   *
   * @param evt   The event that generated the message
   */
  public void mouseEntered(MouseEvent evt)
  {
  }

  /**
   * Handle mouseExited messages.
   *
   * @param evt   The event that generated the message
   */
  public void mouseExited(MouseEvent evt)
  {
  }

  /**
   * Handle mousePressed messages.
   *
   * @param evt   The event that generated the message
   */
  public void mousePressed(MouseEvent evt)
  {
    if (evt.isPopupTrigger() && !popup.isVisible()) 
    {
        Point    p;

        p = evt.getPoint();           
        popup.show(window, p.x, p.y);
    }
  }

  /**
   * Handle mouseReleased messages.
   *
   * @param evt   The event that generated the message
   */
  public void mouseReleased(MouseEvent evt)
  {
     if (evt.isPopupTrigger() && !popup.isVisible()) 
     {
         Point    p;

         p = evt.getPoint();           
         popup.show(window, p.x, p.y);
     }
  }
  
  /**
   * Layout this component.
   */
  private void performLayout()
  {
    JPanel cp = (JPanel)window.getContentPane();
    cp.setLayout(new BorderLayout());
    window.setSize(400,400);

    cp.add(textEditor, BorderLayout.CENTER);
    
    window.setJMenuBar(menuBar);

    JPanel toolbars = new JPanel(new FlowLayout(FlowLayout.LEFT));
    toolbars.add(toolBar);
    cp.add(toolbars, BorderLayout.NORTH);        
  }
}