|
Using the Command Pattern to Decentralize Control
An Introduction with Examples in Java |
|
Prof. David Bernstein |
| Computer Science Department |
| bernstdh@jmu.edu |
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);
}
}
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);
}
}
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);
}
}
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);
}
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);
}
}
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);
}
}
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);
}
}
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);
}
}