Capture about, preferences and quit menu items
Asked Answered
H

4

7

I'm using the current version of SWT to build my applications and I want to run it under Mac OS X (Yosemite).
My problem is now that I'm not be able to capture clicks on the "About", "Preferences" and "Quit" menu items which were automatically added to my application.
I already searched a lot and found the following class which seems very helpful to me http://www.transparentech.com/files/CocoaUIEnhancer.java.

And that's my code to initialize it:

import org.eclipse.swt.*;
import org.eclipse.swt.widgets.*;

public class Test {
  private Display display;
  private Shell shell;

  public Test(Display display) {
    this.display = display;
    initUI();
  }

  public void open() {
    shell.open();
    while (!shell.isDisposed()) {
      if (!display.readAndDispatch()) {
        display.sleep();
      }
    }
  }

  private void initUI() {
    shell = new Shell(display);
    shell.setSize(808, 599);
    shell.setText("Test");

    AboutHandler aboutHandler = new AboutHandler();
    PreferencesHandler preferencesHandler = new PreferencesHandler();
    QuitHandler quitHandler = new QuitHandler();

    CocoaUIEnhancer uienhancer = new CocoaUIEnhancer("Test");
    uienhancer.hookApplicationMenu(display, quitHandler, aboutHandler, preferencesHandler);
  }

  private class AboutHandler implements Listener {
    public void handleEvent(Event e) {
    }
  }

  private class PreferencesHandler implements Listener {
    public void handleEvent(Event e) {
    }
  }

  private class QuitHandler implements Listener {
    public void handleEvent(Event e) {
    }
  }
}

I can compile it without any errors but if I start the program then I will get the following exception:

Exception in thread "main" java.lang.NoSuchMethodError: actionProc
  at org.eclipse.swt.internal.Callback.bind(Native Method)
  at org.eclipse.swt.internal.Callback.<init>(Unknown Source)
  at org.eclipse.swt.internal.Callback.<init>(Unknown Source)
  at org.eclipse.swt.internal.Callback.<init>(Unknown Source)
  at CocoaUIEnhancer.initialize(CocoaUIEnhancer.java:124)
  at CocoaUIEnhancer.hookApplicationMenu(CocoaUIEnhancer.java:92)
  at Test.initUI(Test.java:50)
  at Test.<init>(Test.java:18)

It's probably an error in the native libraries but I can't figure it out!

Hungary answered 5/9, 2015 at 5:4 Comment(0)
E
10

I didn't use CocoaUIEnhancer at all, as it was causing issues as well.

So here's what I ended up doing in my applications:

/**
 * Convenience method that takes care of special menu items (About, Preferences, Quit)
 *
 * @param name     The name of the menu item
 * @param parent   The parent {@link Menu}
 * @param listener The {@link Listener} to add to the item
 * @param id       The <code>SWT.ID_*</code> id
 */
private void addMenuItem(String name, Menu parent, Listener listener, int id)
{
    if (OSUtils.isMac())
    {
        Menu systemMenu = Display.getDefault().getSystemMenu();

        for (MenuItem systemItem : systemMenu.getItems())
        {
            if (systemItem.getID() == id)
            {
                systemItem.addListener(SWT.Selection, listener);
                return;
            }
        }
    }

    /* We get here if we're not running on a Mac, or if we're running on a Mac, but the menu item with the given id hasn't been found */
    MenuItem item = new MenuItem(parent, SWT.NONE);
    item.setText(name);
    item.addListener(SWT.Selection, listener);
}

Just call it with SWT.ID_PREFERENCES, SWT.ID_ABOUT and SWT.ID_QUIT respectively. Hand in a fallback menu item name, a fallback Menu and the actual Listener you want to add to the menu item.

So for example:

addMenuItem("Quit", myMenu, new Listener()
{
    @Override
    public void handleEvent(Event event)
    {
        // Close database connection for example
    }
}, SWT.ID_QUIT);
Ernesternesta answered 24/9, 2015 at 19:6 Comment(3)
A perfect solution - simple and straightforward! The menu items can be renamed, as well. But what about the "SWT" item? How to change its text? Display.setAppName does not work for me due to some unknown reason. Are there any other ways to do it?Scute
My mistake. Display.setAppName() works fine. I just should not have done anything with the display before I call the setAppName(). It should have been the very first line in my code.Scute
@m.vokhm Yeah, I was just going to post that. Glad it's working for you now.Ernesternesta
A
2

It looks like this the actionProc

int actionProc( int id, int sel, int arg0 )

in CocoaUIEnhancer probably needs to use long rather than int for the arguments to work with 64 bit SWT.

Ambiversion answered 5/9, 2015 at 7:3 Comment(1)
It was a good idea but unfortunately it doesn't help. I changed the parameters id, sel, arg0 from int to long but the exception is still the same and the stacktrace is also the same.Hungary
J
2

You need to modify CocoaUIEnhancer.java, to make it work with pure SWT application as described in this tutorial:

  • Modify the getProductName() method to return a String when no product is found (instead of null)
  • Wrap the code in hookWorkbenchListener() in a try-catch (IllegalStateException e) block
  • Wrap the code in modifyShells() in a try-catch (IllegalStateException e) block
  • Add some code to the actionProc(...) method, to bring up an About-Dialog and Preferences-Dialog (since we aren’t using commands):
    static long actionProc(long id, long sel, long arg0) throws Exception {
        // ...
        else if (sel == sel_preferencesMenuItemSelected_) {
            showPreferences();
        } else if (sel == sel_aboutMenuItemSelected_) {
            showAbout();
        }
        return 0;
    }

    private static void showAbout() {
        MessageDialog.openInformation(null, "About...",
                "Replace with a proper about text  / dialog");
    }

    private static void showPreferences() {
        System.out.println("Preferences...");
        PreferenceManager manager = new PreferenceManager();
        PreferenceDialog dialog = new PreferenceDialog(null, manager);
        dialog.open();
    }


    // ...

Finally, we add the following lines to our main() method:

public static final String APP_NAME = "MyApp";

public static void main(String[] args) {
    //in your case change the Test constructor 
    Display.setAppName(APP_NAME);
    Display display = Display.getDefault();

    //insert in initUI method call the earlysetup
    if (SWT.getPlatform().equals("cocoa")) {
        new CocoaUIEnhancer().earlyStartup();
    }

    Shell shell = new Shell(display);
    shell.setText(APP_NAME);
    ...
}

Quoted code.

Jemima answered 24/9, 2015 at 12:24 Comment(0)
W
0

Baz's solution works great! If you'd rather not import OSUtils just to test if you are on a Mac, use instead:

System.getProperty("os.name").contentEquals("Mac OS X")
Wordy answered 10/3, 2021 at 4:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.