Using menus and menu bars in applets

How to use the AWT's built-in menu classes in applets

Menus and applets in the AWT

Java's Abstract Windowing Toolkit (AWT) includes four concrete menu classes: menu bars representing a group of menus that appears across the top of the window or the screen (class MenuBar); pull-down menus that pull from menu bars or from other menus (class Menu); menu items that represent menu selections (class MenuItem); and menu items that the user can turn on and off (class CheckBoxMenuItem).

These classes are all subclasses of MenuComponent, not subclasses of Component. Because they aren't components, they can't be placed in any container -- the way you would place buttons and lists in a container. In a graphical user interface (GUI) the only way to use these menu classes is to place a menu bar (which can contain additional menus) in a frame using the frame's setMenuBar method. Since the applet class is not a subclass of Frame, it does not inherit the setMenuBar method. This means you cannot simply place a menu bar in an applet.

Still, there are several ways to create applets with menus: (1) An applet can open a new frame that contains an AWT menu bar with pull-down menus -- perhaps in response to the user clicking on a button; (2) an applet can use a custom pop-up-menu class, either one that you implement yourself or one that comes from a third-party widget library. (The AWT itself does not include pop-up menus.); and (3) an applet can also use an AWT menu bar with pull-down menus embedded in its enclosing rectangle in a Web page. You can accomplish this last action using the simple technique described in this article.

While each of the three approaches has its uses, the last one has several advantages over the other two. The main advantage of this approach over a design in which the applet opens a new frame with a menu bar are:

  • An applet that does not open a new frame integrates better with Web pages and is less distracting to users than an applet that requires the user to click a button to open a frame. This is especially true in windowing environments that require the user to manually place the new frame -- like many X window managers.

  • Using AWT menus within an applet rather than opening a new frame allows you to embed an exact working copy of the GUI of a standalone application within a Web page -- or you can even embed the entire application. This capability can be valuable for user guides and tutorials, as well as for distributing applications to users who do not have a standalone Java interpreter installed on their machines. These users can use the application with a Java-enabled Web browser instead of launching the standalone application.

The main advantages of using AWT menus in applets rather than using a custom pop-up-menu class are:

  • Using a custom pop-up-menu class requires you to either develop the class, purchase it, or at least find a suitable free one, whereas the AWT menus are built into every Java run-time environment.

  • The AWT menus always have the look and feel of the native windowing environment, whereas a custom pop-up-menu widget might not.

  • An applet that uses the AWT menus loads faster because it does not need to load the menu class from across the network.

Placing a menu bar and menus in an applet

Although an applet is not a subclass of Frame and therefore cannot contain a menu bar, it is always contained within some frame. You can find the frame that contains your applet using the following code, which was suggested for a different purpose in the Recently Asked Questions document on Sun's JavaSoft site:

  Object f = getParent ();
  while (! (f instanceof Frame))
    f = ((Component) f).getParent ();
  Frame frame = (Frame) f;

This code finds the applet's parent, the parent's parent, and so on, until it finds a container that is an instance of class Frame.

The following applet shows how to place menus in an applet. It creates, in its init method, a menu bar that contains two menus -- one with three menu items and the other with two. It then finds its enclosing frame and adds the menu bar to that frame. It attempts to handle, in its action method, the events that are generated when a menu item is selected, but as you will see, if you try to make a menu selection, it fails. The next section of this article analyzes the problem and demonstrates how to solve it.

You will need a Java-capable browser to view this applet.

Handling menu events in an applet

Let's examine why the previous applet fails to handle menu selection events. The code that creates the menus and places them in the frame is the same code you would use in a subclass of Frame that uses menus. Here is an excerpt that creates a menu bar, a menu, and a menu item, and then places the menu bar in the containing frame:

    mb = new MenuBar();
    mb.add(fm = new Menu("File"));
    fm.add(ol = new MenuItem("Open Location"));
    frame.setMenuBar(mb);

The event-handling code is also typical:

  public boolean action(Event e, Object arg) {
    if (e.target == ol) {
      /* Handle the event */
      return true;    
    }
    return super.action(e,arg);
  }

The problem is that the menu-item selection events are never passed to the applet's action method. A selection event is first passed to the menu item itself. The menu item does not handle the event, so it is passed from one object to its parent. Eventually,the event reaches the frame, which also does not handle it. The applet is a descendent of the frame, not its parent or ancestor, so the event is never passed to applet.

The solution is to replace the standard menu items with custom menu items that intercept the selection event and pass it explicitly to the applet rather than rely on the AWT's default event-handling strategy. Our custom menu items are an almost trivial derivation from MenuItem:

public class RedirectingMenuItem extends MenuItem { private Component event_handler;

public RedirectingMenuItem(Component event_handler, String label) { super(label); this.event_handler = event_handler; }

public boolean postEvent(Event e) { if (event_handler.isValid()) return event_handler.postEvent(e); else return false; } }

The RedirectingMenuItem constructor expects an event-handling component, which it simply saves, and a label that it passes to the MenuItem constructor. The trick in this class is to intercept events by overriding the postEvent method and posting the event to the event-handling component. The event is only posted to the event-handling component if it is already valid (that is, its peer has been constructed by the AWT).

Using the RedirectingMenuItem class is easy. We just replace the standard menu items by the redirecting menu items, and pass the this pointer as the reference to the event-handling component:

    fm.add(ol = new RedirectingMenuItem(this,"Open Location"));

The applet below uses this technique to correctly handle menu events.

You will need a Java-capable browser to view this applet.

Ensuring correct applet layout

When we add a menu bar to an applet, it uses some of the space that is allocated to the applet in the Web page by the width and height parameters of the HTML applet tag. Typically, the applet itself is pushed down, and it should be resized by the AWT to lay itself out in a smaller space. The applet will not be resized automatically, however. You must call the frame's pack method after you add the menu bar with setMenuBar. The frame's pack method calculates the new, smaller size of the applet and resizes it. If the applet uses a layout manager, it is automatically called to lay out the components within the applet. The example below calls pack and works correctly. To demonstrate that it indeed works, we have added two buttons above and below the selection text using a BorderLayout layout manager.

You will need a Java-capable browser to view this applet.

If we omit the call to pack, the applet continues to believe that it occupies the entire frame and therefore lays out its components incorrectly. The applet and its layout manager assume that they have more vertical space than they actually have and place the bottom button in an invisible part of the applet. Do not forget to call pack!

You will need a Java-capable browser to view this applet.

Conclusion

We have shown in this article how to use a menu bar and menus in an applet. We have demonstrated how to place the menu bar in an applet, how to ensure that the applet's layout manager lays it out correctly, and perhaps most importantly, how to handle menu events. The RedirectingMenuItem class allows you to write event-handling code that is identical to the menu-related event-handling code that you would normally use in a standalone application.

[]

Addendum: Incompatibilities

Feedback from readers indicates the applets described in this article do not work correctly on all platforms. In particular, the applets do not work under Microsoft Internet Explorer 3.0 and Netscape Navigator 3.0/2.02 for Windows 95 and for Windows NT. They do work under Netscape Navigator for Unix machines, except that menu panes do not always appear in the correct location on the screen. In particular, the panes appear in a correct location when first pulled down, but they continue to appear in the same location even if the page that contains the applet is scrolled up or down.

While I regret that these problems exist, I believe these problems reflect bugs in the browsers. I do not believe that they reflect an inherent nonportability of the techniques the applets use, because all of these techniques are documented by Sun. I have filed a bug report with Netscape concerning these bugs, and I am hopeful that these bugs will be corrected so that these techniques can be widely deployed.

Theses bugs most likely exist because the use of menus in applets is not straightforward, and this aspect of the AWT therefore was not tested by the browsers' developers.

Although it may seem plausible to expect applet developers to test the applet on any possible platform that might be used to run the applet over a network, the task is nearly impossible given the number of platforms. The need to test applets on multiple platforms implies that there are variations in the functionality of various Java platforms. Such variations defeat one of the most important benefits that Java promises to developers: write-once, run-everywhere" (The Java Platform, A White Paper, by Douglas Kramer with Bill Joy and David Spenhoff, Sun Microsystems, May 1996). Hence, it is reasonable to expect that Sun and browser vendors will work to eliminate any such variations.

Sivan Toledo is a postdoctoral associate at the Xerox Palo Alto Research Center. Before joining Xerox, he was a postdoctoral fellow in the Mathematical Sciences Department at the IBM T.J. Watson Research Center. He works on high-performance computing. He used Java to develop a graphical performance-data analysis tool.
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more