My original example didn't address the issue of setting the selection
state of toggle buttons and checkboxes. I couldn't resist the urge to
fix it so here is yet another attempt at an example of handling AbstractActions
in Swing. I've added a quick and dirty toggle button solution. (Alan's suggestion
of subclassing AbstractAction and using PropertyChangeListeners is probably
a better way to do it)
------------------- cut here -----------------------
import java.awt.*;
import java.awt.event.*;
import java.util.Hashtable;
import java.util.ArrayList;
import javax.swing.*;
/**
* An example of using a common set of AbstractActions for handling
* JToolBar, JMenuBar and JPopup menu responses. Support is included
* to synchronize toggle buttons and checkboxes.
*
* @author R. Kevin Cole, (e-mail address removed)
*/
public class ActionExample
{
public static final Integer ACTION1_KEY = new Integer(1);
public static final Integer ACTION2_KEY = new Integer(2);
public static final Integer ACTION3_KEY = new Integer(3);
public static final Integer ACTION4_KEY = new Integer(4);
public static final Integer ABOUT_KEY = new Integer(5);
public static final Integer EXIT_KEY = new Integer(6);
/** Map of AbstractActions to Action Keywords */
static final Hashtable actionTable = new Hashtable(89);
/** Map of a list of toggleComponents to Actions */
static final Hashtable toggleListTable = new Hashtable();
/** Our popup menu */
protected final JPopupMenu popup = new JPopupMenu();
/** The frame in which we are embedded */
JFrame frame;
public ActionExample(final JFrame frame)
{
this.frame = frame;
buildActionTable();
buildPopupMenu();
JTextPane textPane = new JTextPane();
textPane.setEditable(false);
textPane.setText("\n\tUse the MenuBar, ToolBar or right-click for a popup
menu.\n\n"
+ "\tAction1 disables itself and enables Action2.\n"
+ "\tAction2 disables itself and enables Action1.\n"
+ "\tAction3 and Action4 support toggle buttons and checkboxes.\n"
+ "\tThe About action enables Actions 1 and 2 and selects Actions 3 and
4.\n\n"
+ "\tAll components use a common set of actions.\n"
+ "\tThe actions and corresponding components\n"
+ "\tare enabled and disabled in concert.\n\n"
+ "\tFor larger swing applications, I place all actions\n"
+ "\tin a separate package which maps to a directory structure\n"
+ "\tsomething like this...\n\n"
+ "\tmypackage \n"
+ "\t\tActionExample.java\n"
+ "\tmypackage/action\n"
+ "\t\tExitAction.java\n"
+ "\t\tAction1.java\n"
+ "\t\tAction2.java\n"
+ "\t\tAboutAction.java\n"
);
frame.getContentPane().add(new MyToolBar(actionTable), BorderLayout.NORTH);
frame.getContentPane().add(new JScrollPane(textPane), BorderLayout.CENTER);
textPane.addMouseListener(new MousePopupListener());
frame.setJMenuBar(new MyMenuBar(actionTable));
frame.setSize(640, 480);
// send window closing events to our "Exit" action handler.
frame.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
getAction(ActionExample.EXIT_KEY).actionPerformed(null);
}
});
}
/** Enable/disable an AbstractAction
* @param actionName the key that maps this action in actionTable
* @param b true to enable or false to disable the action
*/
public static synchronized void setActionEnabled(final Integer actionKey, final
boolean b)
{
AbstractAction aa = (AbstractAction) actionTable.get(actionKey);
if(aa != null)
{
aa.setEnabled(b);
}
}
/** coordinate toggle buttons such as JCheckBoxMenuItem and JToggleButton. */
public static synchronized void registerToggleAction( AbstractButton btn, Integer key
)
{
Action action = (Action) actionTable.get(key);
ArrayList list = (ArrayList) toggleListTable.get(action);
if( list == null )
throw new IllegalArgumentException("Key does not map to an existing action");
list.add(btn);
}
/** Enable/disable an AbstractAction
* @param actionName the key that maps this action in actionTable
* @param b true to enable or false to disable the action
*/
public static synchronized void setActionSelected(final Integer actionKey, final
boolean b)
{
AbstractAction aa = (AbstractAction) actionTable.get(actionKey);
if(aa != null)
{
ArrayList list = (ArrayList) toggleListTable.get(aa);
if( list != null )
{
int size = list.size();
for( int i = 0; i < size; i++ )
{
AbstractButton btn = (AbstractButton) list.get(i);
btn.setSelected(b);
}
}
}
}
/** @return the action from the action-map that is associated with
* this action key
*/
public AbstractAction getAction(final Integer actionKey)
{
return (AbstractAction) actionTable.get(actionKey);
}
/** Populate a map of actions.
*/
private void buildActionTable()
{
actionTable.put(ActionExample.ABOUT_KEY, new AboutAction());
actionTable.put(ActionExample.EXIT_KEY, new ExitAction());
actionTable.put(ActionExample.ACTION1_KEY, new Action1());
actionTable.put(ActionExample.ACTION2_KEY, new Action2());
Action action = new Action3();
toggleListTable.put( action, new ArrayList() );
actionTable.put(ActionExample.ACTION3_KEY, action );
action = new Action4();
toggleListTable.put( action, new ArrayList() );
actionTable.put(ActionExample.ACTION4_KEY, action );
// initialize the ACTION2_KEY components.
ActionExample.setActionEnabled(ActionExample.ACTION2_KEY, false);
}
/** Build our JPopupMenu
*/
public void buildPopupMenu()
{
this.popup.add(new JMenuItem((Action)
actionTable.get(ActionExample.ACTION1_KEY)));
this.popup.add(new JMenuItem((Action)
actionTable.get(ActionExample.ACTION2_KEY)));
JCheckBoxMenuItem item = new JCheckBoxMenuItem((Action)
actionTable.get(ActionExample.ACTION3_KEY));
ActionExample.registerToggleAction(item, ActionExample.ACTION3_KEY);
this.popup.add(item);
item = new JCheckBoxMenuItem((Action) actionTable.get(ActionExample.ACTION4_KEY));
ActionExample.registerToggleAction(item, ActionExample.ACTION4_KEY);
this.popup.add(item);
this.popup.add(new JSeparator());
this.popup.add(new JMenuItem((Action) actionTable.get(ActionExample.EXIT_KEY)));
}
// --------------- a test routine --------------------
public static void main(String[] argv) throws Exception
{
JFrame frame = new JFrame("Action Example");
final ActionExample instance = new ActionExample(frame);
frame.setVisible(true);
}
/** Handle mouse clicks
*/
class MousePopupListener extends MouseAdapter
{
public void mouseReleased(final MouseEvent e)
{
if(e.isPopupTrigger())
{
Point pt = SwingUtilities.convertPoint((Component) e.getSource(),
e.getX(),
e.getY(), frame);
popup.show(frame, pt.x, pt.y);
return;
}
}
}
// -------------- A MenuBar and ToolBar using our common actions ------------------
/** Our implementation of a JMenuBar.
*/
public class MyMenuBar extends JMenuBar
{
public MyMenuBar(final Hashtable actions)
{
JMenu menu = new JMenu("File");
menu.setMnemonic('F');
menu.add(new JMenuItem((Action) actions.get(ActionExample.ACTION1_KEY)));
menu.add(new JMenuItem((Action) actions.get(ActionExample.ACTION2_KEY)));
menu.add(new JSeparator());
JCheckBoxMenuItem item = new JCheckBoxMenuItem((Action)
actions.get(ActionExample.ACTION3_KEY));
ActionExample.registerToggleAction(item, ActionExample.ACTION3_KEY);
menu.add(item);
item = new JCheckBoxMenuItem((Action) actions.get(ActionExample.ACTION4_KEY));
ActionExample.registerToggleAction(item, ActionExample.ACTION4_KEY);
menu.add(item);
menu.add(new JMenuItem((Action) actions.get(ActionExample.EXIT_KEY)));
add(menu);
menu= new JMenu("Help");
menu.setMnemonic('H');
menu.add(new JMenuItem((Action) actions.get(ActionExample.ABOUT_KEY)));
add(menu);
}
}
/** Our implementation of a JToolBar
*/
public class MyToolBar extends JToolBar
{
public MyToolBar(final Hashtable actions)
{
super();
add( new JButton((Action) actions.get(ActionExample.ACTION1_KEY)) );
add( new JButton((Action) actions.get(ActionExample.ACTION2_KEY)) );
JToggleButton btn = new JToggleButton((Action)
actions.get(ActionExample.ACTION3_KEY));
ActionExample.registerToggleAction( btn, ActionExample.ACTION3_KEY );
add(btn);
btn = new JToggleButton((Action) actions.get(ActionExample.ACTION4_KEY));
ActionExample.registerToggleAction( btn, ActionExample.ACTION4_KEY );
add(btn);
}
}
// -------------- Our common actions ------------------
/** Close the application.
*/
public class ExitAction extends AbstractAction
{
public ExitAction()
{
putValue(Action.NAME, "exit");
putValue(Action.SHORT_DESCRIPTION, "Exit this application");
putValue(Action.ACTION_COMMAND_KEY, "exit");
putValue(Action.MNEMONIC_KEY, new Integer(java.awt.event.KeyEvent.VK_X));
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("F10"));
// putValue(Action.SMALL_ICON, Icons.getInstance().get(Icons.SMALL_EXIT));
}
public void actionPerformed(final ActionEvent ae)
{
System.out.println("Exit action performed.");
System.exit(0);
}
}
/** Test action1
*/
public class Action1 extends AbstractAction
{
public Action1()
{
putValue(Action.NAME, "action1");
putValue(Action.SHORT_DESCRIPTION, "Action 1");
putValue(Action.ACTION_COMMAND_KEY, "action1");
putValue(Action.MNEMONIC_KEY, new Integer(java.awt.event.KeyEvent.VK_1));
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("F1"));
// putValue(Action.SMALL_ICON, Icons.getInstance().get(Icons.SMALL_ACTION1));
}
public void actionPerformed(final ActionEvent ae)
{
System.out.println("Action 1 performed.");
ActionExample.setActionEnabled(ActionExample.ACTION1_KEY, false);
ActionExample.setActionEnabled(ActionExample.ACTION2_KEY, true);
}
}
/** Test action2
*/
public class Action2 extends AbstractAction
{
public Action2()
{
putValue(Action.NAME, "action2");
putValue(Action.SHORT_DESCRIPTION, "Action 2");
putValue(Action.ACTION_COMMAND_KEY, "action2");
putValue(Action.MNEMONIC_KEY, new Integer(java.awt.event.KeyEvent.VK_2));
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("F2"));
// putValue(Action.SMALL_ICON, Icons.getInstance().get(Icons.SMALL_ACTION2));
}
public void actionPerformed(final ActionEvent ae)
{
System.out.println("Action 2 performed.");
ActionExample.setActionEnabled(ActionExample.ACTION1_KEY, true);
ActionExample.setActionEnabled(ActionExample.ACTION2_KEY, false);
}
}
/** action3 is used by toggle buttons.
*/
public class Action3 extends AbstractAction
{
public Action3()
{
putValue(Action.NAME, "action3");
putValue(Action.SHORT_DESCRIPTION, "Toggle Action 3");
putValue(Action.ACTION_COMMAND_KEY, "action3");
putValue(Action.MNEMONIC_KEY, new Integer(java.awt.event.KeyEvent.VK_3));
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("F3"));
// putValue(Action.SMALL_ICON, Icons.getInstance().get(Icons.SMALL_ACTION2));
}
public void actionPerformed(final ActionEvent ae)
{
AbstractButton btn = (AbstractButton) ae.getSource();
System.out.println("Action 3 performed.");
ActionExample.setActionSelected(ActionExample.ACTION3_KEY, btn.isSelected() );
}
}
/** action4 is used by a different set of toggle buttons.
*/
public class Action4 extends AbstractAction
{
public Action4()
{
putValue(Action.NAME, "action4");
putValue(Action.SHORT_DESCRIPTION, "Toggle Action 4");
putValue(Action.ACTION_COMMAND_KEY, "action4");
putValue(Action.MNEMONIC_KEY, new Integer(java.awt.event.KeyEvent.VK_4));
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("F4"));
// putValue(Action.SMALL_ICON, Icons.getInstance().get(Icons.SMALL_ACTION2));
}
public void actionPerformed(final ActionEvent ae)
{
AbstractButton btn = (AbstractButton) ae.getSource();
System.out.println("Action 3 performed.");
ActionExample.setActionSelected(ActionExample.ACTION4_KEY, btn.isSelected() );
}
}
/** The "About" action
*/
public class AboutAction extends AbstractAction
{
public AboutAction()
{
putValue(Action.NAME, "about");
putValue(Action.SHORT_DESCRIPTION, "About the author");
putValue(Action.ACTION_COMMAND_KEY, "about");
putValue(Action.MNEMONIC_KEY, new Integer(java.awt.event.KeyEvent.VK_A));
putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("F9"));
// putValue(Action.SMALL_ICON, Icons.getInstance().get(Icons.SMALL_ABOUT));
}
public void actionPerformed(final ActionEvent ae)
{
System.out.println("About performed.");
JOptionPane.showMessageDialog(frame
, "ActionExample illustrates one way of handling\n"
+ "AbstractActions in a Swing application.\n\n"
+ "The author can be reached at (e-mail address removed)\n\n"
, "About ActionExample"
, JOptionPane.INFORMATION_MESSAGE );
ActionExample.setActionEnabled(ActionExample.ACTION1_KEY, true);
ActionExample.setActionEnabled(ActionExample.ACTION2_KEY, true);
ActionExample.setActionSelected(ActionExample.ACTION3_KEY, true );
ActionExample.setActionSelected(ActionExample.ACTION4_KEY, true );
}
}
}
------------------- cut here -----------------------