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); } }