acm.program.ProgramMenuBar Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javakarel Show documentation
Show all versions of javakarel Show documentation
This the original Stanford Karel for Java, packaged for Maven. ACM Library is included. See also https://cs.stanford.edu/people/eroberts/karel-the-robot-learns-java.pdf
The newest version!
/*
* @(#)ProgramMenuBar.java 1.99.1 08/12/08
*/
// ************************************************************************
// * Copyright (c) 2008 by the Association for Computing Machinery *
// * *
// * The Java Task Force seeks to impose few restrictions on the use of *
// * these packages so that users have as much freedom as possible to *
// * use this software in constructive ways and can make the benefits of *
// * that work available to others. In view of the legal complexities *
// * of software development, however, it is essential for the ACM to *
// * maintain its copyright to guard against attempts by others to *
// * claim ownership rights. The full text of the JTF Software License *
// * is available at the following URL: *
// * *
// * http://www.acm.org/jtf/jtf-software-license.pdf *
// * *
// ************************************************************************
// REVISION HISTORY
//
// -- V2.0 --
// Feature enhancement 2-Mar-07 (ESR)
// 1. Added menu options to implement the "Export Applet" and
// "Submit Project" items.
//
// Feature enhancement 21-May-08 (ESR)
// 1. Significant redesign of package to support easier extensions.
// 2. Added program argument to the constructor.
package acm.program;
import acm.util.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
/* Class: ProgramMenuBar */
/**
* This class standardizes the menu bars used in the ACM program package.
* The fundamental principles behind the design of this package are:
*
*
* - The most common menu operations (including, for example, the standard
* editing operations cut, copy, and paste) should
* always be available and require no action on the part of the programmer.
*
* - It should be easy to extend the menu bar without interfering with the
* standard operations.
*
* - Menu bars should work in a familiar way on each of the major platforms.
* In particular, Macintosh users expect there to be a single menu bar
* at the top of the screen rather than a menu bar in each window.
*
*
*
These goals turn out to be difficult to achieve simultaneously. In
* particular, supporting both Macintosh-style and Windows-style menu bars
* requires creating a parallel MenuBar
structure behind the
* underlying JMenuBar
, which accounts for much of the complexity
* in this implementation.
*
*
Using the ProgramMenuBar
class
*
* The ProgramMenuBar
class supports two distinct disciplines
* for listening for menu actions:
*
*
* - Focused items correspond to actions that are relevant only to the
* component with the keyboard focus (such as Cut, Copy, and
* Paste). Clients create focused items by calling
*
createFocusedItem
.
* Activating a focused item passes an action event to the listener set by calling
* setFocusedListener
,
* which should be called whenever a component interested in responding to menu
* actions gains the keyboard focus.
*
* - Program items correspond to actions that are relevant throughout
* the lifetime of the program (such as Quit and Print).
* Clients create program items by calling
*
createProgramItem
.
* Activating a program item passes an action event to the menuAction
* method in the Program
object that created the menu bar.
*
*/
public class ProgramMenuBar extends JMenuBar implements Iterable {
/* Constant: SHIFT */
/**
* Constant indicating that an accelerator key requires the SHIFT modifier.
*/
public static final int SHIFT = 0x20000;
/* Constructor: ProgramMenuBar(program) */
/**
* Creates an empty ProgramMenuBar
.
*
* Example: ProgramMenuBar mbar = new ProgramMenuBar(owner);
* @param owner The Program
that owns this menu bar.
*/
public ProgramMenuBar(Program owner) {
program = owner;
menuBarListener = new ProgramMenuBarListener(this);
focusedListener = null;
accelerators = new HashMap();
focusedItems = new HashSet();
macMenuBarFlag = true;
addMenus();
}
/* Method: getProgram() */
/**
* Returns the Program
object associated with this menu.
*
* Example: Program program = mbar.getProgram();
* @return The program associated with this menu bar
*/
public Program getProgram() {
return program;
}
/* Method: createStandardItem(action) */
/**
* Creates one of the standard menu items implemented by the
* ProgramMenuBar
class. The menu item is identified
* by its action command.
*
* Example: JMenuItem item = mbar.createStandardItem(action);
* @param action The action command identifying the menu item to be created
* @return a new menu item
*/
public JMenuItem createStandardItem(String action) {
JMenuItem item = null;
if (action.equals("Quit")) {
item = createProgramItem(action);
if (Platform.isMac()) {
setAccelerator(item, 'Q');
} else {
item.setName("Exit");
}
} else if (action.equals("Cut")) {
item = createFocusedItem(action, 'X');
if (!Platform.isMac()) item.setName("Cut (x)");
} else if (action.equals("Copy")) {
item = createFocusedItem(action, 'C');
if (!Platform.isMac()) item.setName("Copy (c)");
} else if (action.equals("Paste")) {
item = createFocusedItem(action, 'V');
if (!Platform.isMac()) item.setName("Paste (v)");
} else if (action.equals("Select All")) {
item = createFocusedItem(action, 'A');
} else if (action.equals("Save")) {
item = createFocusedItem(action, 'S');
} else if (action.equals("Save As")) {
item = createFocusedItem(action);
} else if (action.equals("Print")) {
item = createProgramItem(action, 'P');
item.setName("Print...");
} else if (action.equals("Print Console")) {
item = createProgramItem(action);
} else if (action.equals("Script")) {
item = createProgramItem(action);
item.setName("Script...");
} else if (action.equals("Export Applet")) {
item = createProgramItem(action);
item.setName("Export Applet...");
} else if (action.equals("Submit Project")) {
item = createProgramItem(action);
item.setName("Submit Project...");
} else {
throw new ErrorException("Illegal standard menu item: " + action);
}
return item;
}
/* Method: createProgramItem(action) */
/**
* Creates a program menu item with the specified action command. The
* initial item has the same label as the action command, but clients
* can change this name by calling setName
on the item.
*
* Example: JMenuItem item = createProgramItem(action);
* @param action The action command generated by this menu item
* @return a new menu item
*/
public JMenuItem createProgramItem(String action) {
JMenuItem item = new JMenuItem(action);
item.setActionCommand(action);
item.addActionListener(menuBarListener);
return item;
}
/* Method: createProgramItem(action, key) */
/**
* Creates a program menu item with the specified action command and accelerator key.
*
* Example: JMenuItem item = createProgramItem(action, key);
* @param action The action command generated by this menu item
* @param key The integer value of the keystroke accelerator
* @return a new menu item
*/
public JMenuItem createProgramItem(String action, int key) {
JMenuItem item = createProgramItem(action);
setAccelerator(item, key);
return item;
}
/* Method: createFocusedItem(action) */
/**
* Creates a focused menu item with the specified action command.
*
* Example: JMenuItem item = createFocusedItem(action);
* @param action The action command generated by this menu item
* @return a new menu item
*/
public JMenuItem createFocusedItem(String action) {
JMenuItem item = createProgramItem(action);
focusedItems.add(item);
return item;
}
/* Method: createFocusedItem(action, key) */
/**
* Creates a focused menu item with the specified action command and accelerator key.
*
* Example: JMenuItem item = createFocusedItem(action, key);
* @param action The action command generated by this menu item
* @param key The integer value of the keystroke accelerator
* @return a new menu item
*/
public JMenuItem createFocusedItem(String action, int key) {
JMenuItem item = createFocusedItem(action);
setAccelerator(item, key);
return item;
}
/* Method: isFocusedItem(item) */
/**
* Returns true
if the item is a focused item.
*
* Example: if (mbar.isFocusedItem(item)) . . .
* @param item A menu item installed in the menu bar
* @return true
if the item is a program item
*/
public boolean isFocusedItem(JMenuItem item) {
return focusedItems.contains(item);
}
/* Method: setAccelerator(item, key) */
/**
* Sets the accelerator for the item as appropriate to the operating system
* conventions.
*
* Example: mbar.setAccelerator(item, key);
* @param item The menu item triggered by this accelerator
* @param key The integer value of the keystroke accelerator
*/
public void setAccelerator(JMenuItem item, int key) {
int mask = (Platform.isMac()) ? KeyEvent.META_MASK : KeyEvent.CTRL_MASK;
if (key > 0x10000) {
key -= SHIFT;
mask |= KeyEvent.SHIFT_MASK;
}
KeyStroke stroke = KeyStroke.getKeyStroke((char) key, mask);
accelerators.put(stroke, item);
if (Platform.isMac()) {
item.setAccelerator(stroke);
} else {
item.setMnemonic(key);
}
}
/* Method: setEnabled(action, flag) */
/**
* Enables or disables any menu items that generate the specified action command.
*
* Example: mbar.setEnabled(action, flag);
* @param action The action command triggered by the menu item
* @param flag true
to enable the item, false
to disable it
*/
public void setEnabled(String action, boolean flag) {
int nMenus = getMenuCount();
for (int i = 0; i < nMenus; i++) {
setEnabled(getMenu(i), action, flag);
}
}
/* Method: install(comp) */
/**
* Installs the menu bar in the JFrame
or Program
* object enclosing the component comp
.
*
* Example: mbar.install(comp);
* @param comp A descendant of the frame in which the menu is to be installed
*/
public void install(Component comp) {
Component contentPane = program.getContentPane();
while (comp != null && !(comp instanceof JFrame)) {
comp = comp.getParent();
if (comp == contentPane && program.isAppletMode()) {
if (!Platform.isMac() || !macMenuBarFlag) {
program.setJMenuBar(this);
}
return;
}
}
if (comp == null) return;
JFrame frame = (JFrame) comp;
if (Platform.isMac() && macMenuBarFlag) {
if (oldStyleMenuBar == null) {
oldStyleMenuBar = createOldStyleMenuBar();
}
frame.setMenuBar(oldStyleMenuBar);
} else {
frame.setJMenuBar(this);
frame.validate();
}
}
/* Method: setMacMenuBarFlag(flag) */
/**
* Sets a flag indicating whether applications running on the Macintosh
* should use standard Mac menus. The default is true
.
* Setting this value to false
means that Mac programs
* use the same in-window JMenuBar
approach used on other
* platforms.
*
* Example: setMacMenuBarFlag(flag);
* @param flag true
to use Mac menu style; false
otherwise
*/
public void setMacMenuBarFlag(boolean flag) {
macMenuBarFlag = flag;
}
/* Method: getMacMenuBarFlag() */
/**
* Retrieves the setting of the Mac menu bar flag.
*
* Example: boolean flag = getMacMenuBarFlag();
* @return true
if Mac menu style is supported; false
otherwise
*/
public boolean getMacMenuBarFlag() {
return macMenuBarFlag;
}
/* Method: fireActionListeners(e) */
/**
* Fires the action listeners responsible for handling the specified event.
* The process of choosing the appropriate handlers takes into account
* whether the action command is designated as program or focused.
* @param e event source
*/
public void fireActionListeners(ActionEvent e) {
if (focusedListener != null && focusedItems.contains(e.getSource())) {
focusedListener.actionPerformed(e);
} else {
program.menuAction(e);
}
}
/* Method: fireAccelerator(e) */
/**
* Triggers the accelerator associated with the keystroke implied by the key event.
* This method returns true
if such an accelerator exists.
* @param e event source
* @return fired
*/
public boolean fireAccelerator(KeyEvent e) {
KeyStroke stroke = KeyStroke.getKeyStrokeForEvent(e);
JMenuItem item = accelerators.get(stroke);
if (item != null) {
item.doClick(0);
return true;
}
return false;
}
/* Method: setFocusedListener(listener) */
/**
* Registers a listener that responds while the caller holds the keyboard
* focus. The caller should register its listener when it acquires the
* keyboard focus and set it to null
when it loses it.
*
* Example: setFocusedListener(listener);
* @param listener An ActionListener
that responds to focused items
*/
public void setFocusedListener(ActionListener listener) {
focusedListener = listener;
}
/* Method: iterator() */
/**
* Returns an iterator that enumerates the individual menu items under the
* control of the menu bar.
*
* Example: Iterator iterator = mbar.iterator();
* @return An iterator that enumerates the menu items
*/
public Iterator iterator() {
ArrayList itemList = new ArrayList();
for (int i = 0; i < getMenuCount(); i++) {
addItemToList(itemList, getMenu(i));
}
return itemList.iterator();
}
/* Protected methods */
/* Protected method: addMenus() */
/**
* Adds menus to the menu bar. Subclasses that wish to change the composition
* of the menu bar beyond the default File
and Edit
* menus should override this method with one that adds the desired menus.
*/
protected void addMenus() {
addFileMenu();
addEditMenu();
}
/* Protected method: addFileMenu() */
/**
* Installs the File
menu.
*
* Example: mbar.addFileMenu();
*/
protected void addFileMenu() {
JMenu fileMenu = new JMenu("File");
fileMenu.setMnemonic('F');
addFileMenuItems(fileMenu);
add(fileMenu);
}
/* Protected method: addEditMenu() */
/**
* Installs the Edit
menu.
*
* Example: mbar.addEditMenu();
*/
protected void addEditMenu() {
JMenu editMenu = new JMenu("Edit");
editMenu.setMnemonic('E');
addEditMenuItems(editMenu);
add(editMenu);
}
/* Protected method: addFileMenuItems(menu) */
/**
* Adds the standard File
items to the specified menu. Subclasses
* can override this method to change the list of items.
*
* Example: mbar.addFileMenuItems(menu);
* @param menu The menu to which the File
items are added
*/
protected void addFileMenuItems(JMenu menu) {
menu.add(createStandardItem("Save"));
menu.add(createStandardItem("Save As"));
menu.addSeparator();
menu.add(createStandardItem("Print"));
menu.add(createStandardItem("Print Console"));
menu.add(createStandardItem("Script"));
menu.addSeparator();
menu.add(createStandardItem("Export Applet"));
menu.add(createStandardItem("Submit Project"));
menu.addSeparator();
menu.add(createStandardItem("Quit"));
}
/* Protected method: addEditMenuItems(menu) */
/**
* Adds the standard Edit
items to the specified menu. Subclasses
* can override this method to change the list of items.
*
* Example: mbar.addEditMenuItems(menu);
* @param menu The menu to which the Edit
items are added
*/
protected void addEditMenuItems(JMenu menu) {
menu.add(createStandardItem("Cut"));
menu.add(createStandardItem("Copy"));
menu.add(createStandardItem("Paste"));
menu.add(createStandardItem("Select All"));
}
/* Private method: addItemToList(itemList, item) */
/**
* Adds the specified menu item to the list. If item
is itself
* a menu, this method expands the item recursively.
*
* Example: mbar.addItemToList(itemList, item);
* @param itemList The ArrayList
to which items are added
* @param item The item to be added
*/
private void addItemToList(ArrayList itemList, JMenuItem item) {
if (item == null) return;
if (item instanceof JMenu) {
JMenu menu = (JMenu) item;
for (int i = 0; i < menu.getItemCount(); i++) {
addItemToList(itemList, menu.getItem(i));
}
} else {
itemList.add(item);
}
}
/* Private method: createOldStyleMenuBar */
/**
* Creates a MenuBar
that has the same effect as the
* specified JMenuBar
.
*
* Example: MenuBar oldMenuBar = mbar.createOldStyleMenuBar();
* @return A MenuBar
whose actions are paired with the original
*/
private MenuBar createOldStyleMenuBar() {
MenuBar mbar = new MenuBar();
int nMenus = getMenuCount();
for (int i = 0; i < nMenus; i++) {
mbar.add(createOldStyleMenu(getMenu(i)));
}
return mbar;
}
/* Private method: createOldStyleMenu */
/**
* Creates a Menu
that has the same effect as the
* specified JMenu
.
*/
private Menu createOldStyleMenu(JMenu jmenu) {
Menu menu = new Menu(jmenu.getText());
int nItems = jmenu.getItemCount();
for (int i = 0; i < nItems; i++) {
menu.add(createOldStyleMenuItem(jmenu.getItem(i)));
}
return menu;
}
/* Private method: createOldStyleMenuItem */
/**
* Creates a MenuItem
that has the same effect as the
* specified JMenuItem
.
*/
private MenuItem createOldStyleMenuItem(Object jitem) {
if (jitem == null) {
return new MenuItem("-");
} else if (jitem instanceof JMenu) {
return createOldStyleMenu((JMenu) jitem);
} else if (jitem instanceof JCheckBoxMenuItem) {
return new OldStyleCheckBoxMenuItem((JCheckBoxMenuItem) jitem);
} else if (jitem instanceof JMenuItem) {
return new OldStyleMenuItem((JMenuItem) jitem);
}
throw new ErrorException("Unsupported menu item type");
}
/* Private method: setEnabled(menu, action, flag) */
/**
* Updates the enabled state of everything in the menu that has the specified action.
*/
private void setEnabled(JMenu item, String action, boolean flag) {
JMenu menu = item;
int nItems = menu.getItemCount();
for (int i = 0; i < nItems; i++) {
JMenuItem subItem = menu.getItem(i);
if (subItem != null) setEnabled(subItem, action, flag);
}
}
/* Private method: setEnabled(item, action, flag) */
/**
* Updates the enabled state of the menu item if it has the specified action.
*/
private void setEnabled(JMenuItem item, String action, boolean flag) {
if (action.equals(item.getActionCommand())) item.setEnabled(flag);
}
/* Private instance variables */
private Program program;
private ActionListener menuBarListener;
private ActionListener focusedListener;
private HashMap accelerators;
private HashSet focusedItems;
private MenuBar oldStyleMenuBar;
private boolean macMenuBarFlag;
}
/* Package class: ProgramMenuBarListener */
/**
* This class implements the listener for the standard menu items that
* forwards their action back to the program.
*/
class ProgramMenuBarListener implements ActionListener {
/* Constructor: ProgramMenuBarListener(mbar) */
/**
* Creates a new listener for the standard menu items that will be added to this
* menu bar.
*/
public ProgramMenuBarListener(ProgramMenuBar mbar) {
menuBar = mbar;
}
/* Method: actionPerformed(e) */
/**
* Responds to an action event in the corresponding menu. The effect of an
* action event is to forward the action command back to the program.
*/
public void actionPerformed(ActionEvent e) {
menuBar.fireActionListeners(e);
}
/* Private instance variables */
private ProgramMenuBar menuBar;
}
/* Package class: OldStyleMenuItem */
/**
* This class represents a standard Macintosh MenuItem
that listens to
* a JMenuItem
and tracks its changes.
*/
class OldStyleMenuItem extends MenuItem implements ActionListener, ChangeListener {
/* Constructor: OldStyleMenuItem(jitem) */
/**
* Creates a new MenuItem
that tracks the changes in the specified
* JMenuItem
.
*/
public OldStyleMenuItem(JMenuItem jitem) {
super(jitem.getText());
twin = jitem;
addActionListener(this);
twin.addChangeListener(this);
setEnabled(twin.isEnabled());
KeyStroke accelerator = twin.getAccelerator();
if (accelerator != null) setShortcut(createShortcut(accelerator));
}
/* Method: actionPerformed(e) */
/**
* Responds to an action event in the Mac menu and forwards it along to
* the actual JMenuItem
that the client has created.
*/
public void actionPerformed(ActionEvent e) {
if (e != e) /* Avoid the unused parameter warning */;
twin.doClick(0);
}
/* Method: stateChanged(e) */
/**
* Monitors the state of the JMenuItem
and replicates changes
* in the enabled state.
*/
public void stateChanged(ChangeEvent e) {
setEnabled(twin.isEnabled());
}
/* Private method: createShortcut(accelerator) */
/**
* Creates an old-style menu shortcut from the new-style accelerator.
*/
private MenuShortcut createShortcut(KeyStroke accelerator) {
boolean isShifted = (accelerator.getModifiers() & Event.SHIFT_MASK) != 0;
return new MenuShortcut(accelerator.getKeyCode(), isShifted);
}
/* Private instance variables */
private JMenuItem twin;
}
/* Package class: OldStyleCheckBoxMenuItem */
/**
* This class represents a standard Macintosh CheckBoxMenuItem
that
* listens to a JCheckBoxMenuItem
and tracks its changes.
*/
class OldStyleCheckBoxMenuItem extends CheckboxMenuItem implements ActionListener, ChangeListener {
/* Constructor: OldStyleCheckBoxMenuItem(jitem) */
/**
* Creates a new CheckBoxMenuItem
that tracks the changes in the specified
* JCheckBoxMenuItem
.
*/
public OldStyleCheckBoxMenuItem(JCheckBoxMenuItem jitem) {
super(jitem.getText());
twin = jitem;
addActionListener(this);
twin.addChangeListener(this);
setState(twin.getState());
setEnabled(twin.isEnabled());
KeyStroke accelerator = twin.getAccelerator();
if (accelerator != null) setShortcut(createShortcut(accelerator));
}
/* Method: actionPerformed(e) */
/**
* Responds to an action event in the Mac menu and forwards it along to
* the actual JMenuItem
that the client has created.
*/
public void actionPerformed(ActionEvent e) {
if (e != e) /* Avoid the unused parameter warning */;
twin.doClick(0);
}
/* Method: stateChanged(e) */
/**
* Monitors the state of the JMenuItem
and replicates changes
* in the enabled state.
*/
public void stateChanged(ChangeEvent e) {
setState(twin.getState());
setEnabled(twin.isEnabled());
}
/* Private method: createShortcut(accelerator) */
/**
* Creates an old-style menu shortcut from the new-style accelerator.
*/
private MenuShortcut createShortcut(KeyStroke accelerator) {
boolean isShifted = (accelerator.getModifiers() & Event.SHIFT_MASK) != 0;
return new MenuShortcut(accelerator.getKeyCode(), isShifted);
}
/* Private instance variables */
private JCheckBoxMenuItem twin;
}