macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Write Twice, Run Anywhere
Pages: 1, 2, 3

Creating the Menu Items

The Open menu item is exactly the same in both the Windows and the Mac versions.



Here's the code for OpenMenuItem.

package commonGUI;

import javax.swing.JMenuItem;
import javax.swing.KeyStroke;
import java.awt.event.KeyEvent;
import java.awt.Toolkit;

public class OpenMenuItem extends JMenuItem {
  public OpenMenuItem(){
    super("Open...");
    setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, 
             Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
  }
}

The highlighted line above is where all the magic happens. The first parameter being passed into the getKeyStroke() method is the key event that corresponds to typing the letter "O". On a Mac you type Command - O to select the Open menu item and on Windows you type Control - O. The second parameter retrieves the mask for the menu shortcut key on the platform your application is running. Rather than hardcode in the Control key or the Command key, you retrieve the information at runtime. First the Toolkit's static method getDefaultToolkit() gives you a handle to the current AWT toolkit. Then that toolkit is queried for the mask for the menu shortcut key. On a Windows box it returns the mask for the Control key and on a Mac it returns the mask for the Command key. This allows the OpenMenuItem to look as it should in both environments.

The Page Setup menu item is common to both the Mac and Windows File menus but it has an accelerator assigned on the Mac but not on Windows. We can build a common parent class and place it in the commonGUI package and then extend it on the Mac and add the accelerator. When you add behavior to Page Setup you can then do it in the common parent class and reduce the amount of duplicated code. Here's the root class, PageSetupMenuItem.

package commonGUI;

import javax.swing.JMenuItem;

public class PageSetupMenuItem extends JMenuItem{
  public PageSetupMenuItem(){
    super("Page Setup...");
  }
}

Now extend PageSetupMenuItem and add the keyboard accelerator in the constructor. Remember that the no argument constructor in the parent class will be the first thing called by the constructor in MacPageSetupMenuItem.

package MacGUI;

import commonGUI.PageSetupMenuItem;

import javax.swing.KeyStroke;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;

public class MacPageSetupMenuItem extends PageSetupMenuItem{
  public MacPageSetupMenuItem(){
    setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, 
             Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()
             + java.awt.Event.SHIFT_MASK));
  }
}

There are two things to note about the highlighted code above. First, because it is Mac specific code we could have hard coded the choice of the menu shortcut mask. This version makes it easier to pull up to the parent class if we need to refactor later. The second issue is that there are actually two masks being specified here. If you look back at the image of the TextEdit File menu you can see that the shortcut for Page Setup requires that you press both the Command and Shift keys while typing the letter P. The values of the masks are powers of two and so you pass in more than one by adding them together. The component pieces of the sum can easily be determined.

Cleaning Up the Loose Ends

You can use these three menu item examples from the last section to create the code for the remaining menu items. Once that is done you'll need to create the FileMenu classes for the MacGUI package and for the WindowsGUI package. Here's the MacGUI version.

package MacGUI;

import javax.swing.JMenu;
import commonGUI.*;

public class FileMenu extends JMenu{
  public FileMenu(){
    super("File");
    add(new MacNewMenuItem());
    add(new OpenMenuItem());
    add(new JMenu("Open Recent"));
    addSeparator();
    add(new CloseMenuItem());
    add(new SaveMenuItem());
    add(new MacSaveAsMenuItem());
    add(new SaveAllMenuItem());
    add(new RevertToSavedMenuItem());
    addSeparator();
    add(new MacPageSetupMenuItem());
    add(new PrintMenuItem());
  }
}

Again, make the necessary adjustments for the WindowsGUI version. The final task is to go back and fix the MacJMenuBar and WindowsJMenuBar. We had them create an ordinary JMenu with the label "File". We need to go back and change the code so that we are actually creating a FileMenu object. Here's the fixed version of MacJMenuBar.

package MacGUI;

import javax.swing.JMenuBar;
import javax.swing.JMenu;

public class MacJMenuBar extends JMenuBar{
  public MacJMenuBar(){
    add(new FileMenu()); // instead of add(new JMenu("File"));
    add(new JMenu("Edit"));
    add(new JMenu("Format"));
    add(new JMenu("Window"));
    add(new JMenu("Help"));
  }
}

Summary

In this article we tuned a Java application so that it could meet the expectations of two disparate audiences on their home platforms. The application still looks much like a Windows application when run on a Windows machine and now it looks much more like a Mac application when run on a Mac. A combination of using System properties and a minimal amount of parallel code improves the user experience. The next step is to add Mac-specific functionality such as an About Handler--but that's another article.

Daniel H. Steinberg is the editor for the new series of Mac Developer titles for the Pragmatic Programmers. He writes feature articles for Apple's ADC web site and is a regular contributor to Mac Devcenter. He has presented at Apple's Worldwide Developer Conference, MacWorld, MacHack and other Mac developer conferences.


Read more Java Programming on the Mac columns.

Return to the Mac DevCenter.