Porting MagicTree to Mac OS X

Submitted by alla on Wed, 09/22/2010 - 16:55

Well, MagicTree is written in Java, so in theory we shouldn't need to port anywhere. But, as they say, "In theory, theory and practice are the same. In practice, they are not." Actually most things worked without any porting, which is a good thing. Still, a few things had to be done.

To get more Mac OS-like look and feel we had to make MagicTree show the menu bar on top of the screen, rather than in the main frame, respond to application menu items, such as "About" and "Quit" (and remove those menu items from File and Help respectively) and to get the application icon look pretty.

Detecting Mac OS X is rather simple:

    public static boolean isMacOSX() {
        String osName = System.getProperty("os.name");
        return osName.startsWith("Mac OS X");
    }

Having that, we can now make the menu go to the top of the screen whenever we run on Mac OS X:

    System.setProperty("apple.laf.useScreenMenuBar", "true");

Another necessity is to set the application name to something friendly, such as "MagicTree" instead of com.gremwell.mt.MtApplication:

    System.setProperty("com.apple.mrj.application.apple.menu.about.name", "MagicTree");

Handling Mac OS X application menu requires using Apple Java Extensions (com.apple.eawt). This works great when you are building on Mac OS. But if you happened to compile the application anywhere else, the build will break. This package is only available on Mac OS. I suppose Apple works from the assumption that somebody who has tried working on Mac OS once will never want to use any other operating system again.

There are six and a half billion people in the world right now. Good thing about it is that whatever problem you have somebody has already had it. And, thanks to the Internet, we can use the solution. The solution to Apple Java Extensions not available on other platforms is Macify - a wrapper around Apple classes that can be used on other platforms as well.

Macify also allows setting the application icon on Mac OS X. This way we can use a high resolution icon while still distributing the application as JAR, and not having to create a separate package for MacOS:

    Application application = new DefaultApplication();
    try {
         URL imageURL = MtApplication.class.getResource("/com/gremwell/mt/application/images/macos-icon-512x512.png");
         BufferedImage macIcon = ImageIO.read(imageURL);
         application.setApplicationIconImage(macIcon);
    } catch (IOException ex) {
         logger.info("Failed loading Mac OS X high resolution icon");
    }

MagicTree uses Microsoft Word and/or OpenOffice Write to view and edit report templates and reports. We had some quite gnarly code that tried to determine whether we are running on Unix or Windows and what is the path for the required office application. Of course, on Mac OS X we had to do that too. Luckily, there is a much better solution to the problem of opening office files. Enter java.awt.Desktop! Opening an .ODT or .DOCX file with the available office application is as simple as this:

    private void openDocument(File document) throws IOException {
        Desktop desktop = java.awt.Desktop.getDesktop();
        desktop.open(document);
    }

One more thing that did not work straight away on Mac OS X was command console. On Linux we open xterm, on Windows we use Cygwin rxwt. On Mac OS X we should be using Terminal. But, it turns out, there is absolutely no way to pass the command to execute to Terminal via the command line arguments. Googling saved us again. The Mac OS X way of starting Terminal and passing it the command "echo hello" to execute is

osascript -e "tell application \"Terminal\" to do script \"echo hello\""

I am not kidding you.

And, if you are reading this from a Mac try pasting this into Terminal:

osascript -e "say \"MagicTree is mighty cool\" using \"Zarvox\""

Contacts

+32 (0) 2 215 53 58

Gremwell BVBA
Sint-Katherinastraat 24
1742 Ternat
Belgium
VAT: BE 0821.897.133.