Section 13.8 Menus and Scroll Panes
Pop-up and pull-down menus allow an application to grow in complexity and functionality without cluttering its interface. Menus are hierarchical in nature. A particular menu is divided into a number of menu items, which can themselves be further subdivided. Java makes it simple to implement menus.
A
JMenuBar
is an implementation of a menu bar—a horizontal list of names that appears at the top of a window ( Figure 13.8.1).Almost all applications have a menu bar. To construct a menu, you add
JMenu
objects to a JMenuBar
. A JMenu
is essentially a clickable area on a menu bar that is associated with a JPopupMenu
, a small window that pops up and displays the menu’s JMenuItem
s. A menu can also contain JSeparator
s, which are dividers that can be placed between menu items to organize them into logical groupings.Subsection 13.8.1 Adding a Menu Bar to an Application
It is easy to create menus in Swing. The process involves three steps, although you needn’t perform them in this order:
- Create the individual
JMenuItem
s. - Create a
JMenu
and add theJMenuItem
s to it. - Create a
JMenuBar
and add theJMenu
s to it.
For example, suppose you’re building the interface for a text editor. A text editor typically contains at least two standard menus. The file menu is used to create new documents, open and close files, save your document, and so on. The edit menu is used to cut and paste selected text from the document.
Here’s how you would create the file menu for this program. First, you create a menu bar and make it the menu bar for the application’s
JFrame
or for the JApplet
. This is usually done in the application’s constructor or in the applet’s init()
method:JMenuBar mBar = new JMenuBar();// Create menu bar
this.setJMenuBar(mBar); // Add it to this window
The next step involves creating and adding menus and menu items to the menu bar. This is also usually done in the constructor or the
init()
method. If the menu is large, you should break this task into subtasks and define a method for each subtask.Here’s the definition of the file menu for our simple text editor:
private void initFileMenu() {
fileMenu = new JMenu("File"); // Create menu
mBar.add(fileMenu); // Add it to menu bar
openItem = new JMenuItem("Open"); // Open item
openItem.addActionListener( this );
openItem.setEnabled(false);
fileMenu.add(openItem);
saveItem = new JMenuItem("Save"); // Save item
saveItem.addActionListener(this);
saveItem.setEnabled(false);
fileMenu.add(saveItem);
fileMenu.addSeparator(); // Logical separator
quitItem = new JMenuItem("Quit"); // Quit item
quitItem.addActionListener(this);
fileMenu.add(quitItem);
} // initFileMenu()
The first two statements in the method create the file menu and add it to the menu bar. The rest of the statements create the individual menu items that make up the file menu. Note the use of a separator item after the save item. This has the effect of grouping the file-handling items (open and save) into one logical category and distinguishing them from the quit item. A separator is represented as a line in the menu ( Figure 13.8.1).
Principle 13.8.3. EFFECTIVE DESIGN: Logical Design.
In designing interfaces, an effort should be made to use visual cues, such as menu item separators and borders, to group items that are logically related. This will help to orient the user.
Note that each menu item is given an
ActionListener
. As we’ll see shortly, action events for menu items are handled the same way as action events for buttons. Finally, note how the setEnabled()
method is used to disable both the open and save menu items. Implementation of these actions is left as an exercise.Activity 13.8.1.
Try the File Menu below. Fork the repl and add another menu item to the File Menu. Add code to the actionPerformed method to print out the command selected.
Subsection 13.8.2 Menu Hierarchies
Menus can be added to other menus to create a hierarchy. For example, the edit menu will include the standard cut, copy, and paste menu items. Some edit menus also contain an “Undo” item, which can be used to undo the last editing operation that was performed. In other words, if you cut a piece of text, you can undo that operation and get that cut back. Many editors seem to allow just a single undo. If you cut two pieces of text, the first piece is lost to the user to undo. This can be an issue, especially if you didn’t mean to do the first cut.
To help remedy this type of situation, let’s add a feature to our editor that will keep track of cuts by storing them in a
ArrayList
. his function will be like an “Unlimited Undo” operation for cuts. For this example, we won’t place any limit on the size of the vector. Every cut the user makes will be inserted at the beginning of the vector. To go along with this feature we need a menu that can grow dynamically during the program. Each time the user makes a cut, the string that was cut will be added to the menu.This kind of menu should occur within the edit menu, but it will have its own items. This is a menu within a menu (Figure 13.8.1), an example of a cascading drop-down menu. The edit menu itself drops down from the menu bar, and the recent cuts menu drops down and to the right of where its arrow points. The following method was used to create the edit menu:
private void initEditMenu() {
editMenu = new JMenu("Edit"); // Create edit menu
mBar.add(editMenu); // Add to menu bar
cutItem = new JMenuItem ("Cut"); // Cut item
cutItem.addActionListener(this);
editMenu.add(cutItem);
copyItem = new JMenuItem("Copy"); // Copy item
copyItem.addActionListener(this);
editMenu.add(copyItem);
pasteItem = new JMenuItem("Paste"); // Paste item
pasteItem.addActionListener(this);
editMenu.add(pasteItem);
editMenu.addSeparator();
selectItem = new JMenuItem("Select All");// Select
selectItem.addActionListener(this);
editMenu.add(selectItem);
editMenu.addSeparator();
cutsMenu = new JMenu("Recent Cuts");//Cuts submenu
editMenu.add(cutsMenu);
} // initEditMenu()
The main difference between this method and the one used to create the file menu is that here we insert an entire submenu as one of the items in the edit menu. The
cutsMenu
will be used to hold the strings that are cut from the document. Initially, it will be empty.Activity 13.8.2.
Fork the FileMenu repl below or use the fork from the previous activity, to add the Edit menu.
Subsection 13.8.3 Handling Menu Actions
Handling
JMenuItem
actions is no different from handling JButton
actions. Whenever a user makes a menu selection, an ActionEvent
is generated. Programs that use menus must implement the actionPerformed()
method of the ActionListener
interface. In the text editor example, there are a total of six enabled menu items, including the recent cuts menu. This translates into a large if-else structure, with each clause handling a single menu item.The following
actionPerformed()
method is used to handle the menu selections for the text editor:public void actionPerformed(ActionEvent e) {
JMenuItem m = (JMenuItem)e.getSource(); // Get selected menu item
if ( m == quitItem ) { // Quit
dispose();}
} else if (m == cutItem) { // Cut the selected text
scratchPad = display.getSelectedText(); // Copy text to scratchpad
display.replaceRange("", // and delete
display.getSelectionStart(), // from the start of selection
display.getSelectionEnd()); // to the end
addRecentCut(scratchPad); // Add text to the cuts menu
} else if (m == copyItem) // Copy text to scratchpad
scratchPad = display.getSelectedText();
} else if (m == pasteItem) { // Paste scratchpad to document at caret
display.insert(scratchPad, display.getCaretPosition()); // position
} else if ( m == selectItem ) {
display.selectAll(); // Select the entire document
} else {
JMenuItem item = (JMenuItem)e.getSource(); // Default is cutsMenu
scratchPad = item.getActionCommand(); // Put cut back in scratchpad
}
} // actionPerformed()
The method begins by getting the source of the
ActionEvent
and casting it into a JMenuItem
. It then checks each case of the if-else structure. Because the actions taken by this program are fairly short, they are mostly coded within the actionPerformed()
method itself. However, for most programs it will be necessary to write a separate method corresponding to each menu item and then call the methods from actionPerformed()
.Our text editor’s main task is to implement the cut/copy/paste functions, which are simple to do in Java. The text that’s being edited is stored in a
JTextArea
, which contains instance methods that make it very easy to select, insert, and replace text. To copy a piece of text, the program need only get the text from the JTextArea
(getSelectedText()
) and assign it to the scratchpad
, which is represented as a String
. To paste a piece of text, the program inserts the contents of the scratchpad
into the JTextArea
at the location marked by the caret, a cursor-like character in the document that marks the next insertion point.The structure of this if-else statement is significant. Note how the default case of the if-else is designed. We are using the last else clause as a “catch all” condition to catch and handle selections from the
cutsMenu
. All of the other menu items can be referred to by name. However, the menu items in the cutsMenu
are just snippets of a string that the user has previously cut from the text, so they can’t be referenced by name. Luckily, we don’t really need to. For any JMenuItem
, the getActionCommand()
method returns its text, which in this case is the previously cut text. So we just assign the cut text from the menu to the scratchpad
.Principle 13.8.4. PROGRAMMING TIP: Default Cases.
Although the order of the clauses in an if-else structure is usually not important, the default clause can sometimes be used to handle cases that can’t be referenced by name.
Subsubsection 13.8.3.1 Handling Previously Cut Text
The most difficult function in our program is the cut operation. Not only must the selected text be removed from the document and stored in the
scratchpad
, but it must also be inserted into the vector that is storing all the previous cuts. The addRecentCut()
method takes care of this last task. The basic idea here is to take the cut string and insert it at the beginning of the vector, so that cuts will be maintained in a last-in–first-out order. Then the cutsMenu
must be completely rebuilt by reading its entries out of the vector, from first to last. That way the most recent cut will appear first in the menu:private void addRecentCut(String cut) {
recentCuts.add(0,cut);
cutsMenu.removeAll();
for (int k = 0; k < recentCuts.size(); k++) {
JMenuItem item =
new JMenuItem((String)recentCuts.get(k));
cutsMenu.add( item );
item.addActionListener(this);
}
} // addRecentCut()
The
recentCuts ArrayList
stores the cut strings. Note the use of the insertElementAt()
method to insert strings into the vector and the elementAt()
method to get strings from the vector. (You may find it helpful to review the section on ArrayLists in Chapter 9.)Note also how menu items are removed and inserted in menus. The
cutsMenu
is reinitialized, using the removeAll()
method. Then the for loop iterates through the strings stored in the vector, making new menu items from them, which are then inserted into the cutsMenu
. In this way, the cutsMenu
is changed dynamically each time the user cuts a piece of text from the document.Subsection 13.8.4 The SimpleTextEditor Implementation
Activity 13.8.3.
Try the SimpleTextEditor Edit menu below.
The design of the
SimpleTextEditor
class is summarized in Figure 13.8.5 and its complete implementation is shown in Listing 13.8.6. It uses a BorderLayout
, with the JTextArea
placed at the center.Subsection 13.8.5 Adding Scrollbars to a Text Area
Note how simple it is to add scrollbars to the text area:
this.getContentPane().add(new JScrollPane(display));
This statement creates a
JScrollPane
and adds it to the application’s container. A JScrollPane
is one of Swing’s scrollbar classes. Its function is to manage the viewing and scrolling of a scrollable component, such as a JTextArea
. A JScrollPane
is actually a container, which is why it takes the display
as an argument. The display
is being added to the JScrollPane
.Just about any
Component
can be added to a JScrollPane
. Once a component is added, the scroll pane will manage the scrolling functions for the component. The default constructor used in this example takes a single Component
parameter. This refers to the scrollable component, in this case to the JTextArea
. Another constructor that you might use takes the following form:public JScrollPane(Component comp, int vsbPolicy,
int hsbPolicy);
The two integers refer to the vertical and horizontal scrolling policies. These cover properties such as whether the scrollbars are always present or just as needed. The default policy is to attach scrollbars to the component only when needed. Thus, to see the scrollbars in the
SimpleText
Editor
, you would have to shrink the window to the point where all of the text cannot be viewed (Figure 13.8.7). Because the text area in this example is wrapping the text, the horizontal scrollbar will never be needed.
Subsection 13.8.6 Self-Study Exercises
Exercise 13.8.8. Limit Cuts.
Fork the https://replit.com/@BerylHoffman/SimpleTextEditor. Modify the
addRecentCut()
method so it limits the cuts stored in the ArrayList to the last ten cuts.Exercise 13.8.9. No Duplicate Cuts.
In your fork above, modify the
addRecentCut()
method so that it doesn’t duplicate cuts already stored in the ArrayList.Hint.
Use the
indexOf(String)
method.Exercise 13.8.10. FileMenu Actions.
In your fork above, implement the actions for the File menu class to save the text into a file.
You have attempted of activities on this page.