java - SWT:如何在 OS X 上正确处理应用程序事件

标签 java macos swt

如何使用 SWT 正确处理 OS X 的应用程序事件(退出、关于、首选项)?对于 AWT/Swing,com.apple.eawt.Application.ApplicationAdapter 工作正常。

最佳答案

您可以按照以下方式响应退出事件:

final Display display = Display.getDefault();
display.addListener(SWT.Close, new Listener() {
    public void handleEvent(Event e) {
        // e.g., prevent quitting:
        e.doit = false;
    }
});

对于更复杂的情况,您可以使用CocoaUIEnhancer (EPL):

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.eclipse.jface.action.IAction;
import org.eclipse.swt.SWT;
import org.eclipse.swt.internal.C;
import org.eclipse.swt.internal.Callback;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Listener;

/**
 * Provide a hook to connecting the Preferences, About and Quit menu items of the Mac OS X
 * Application menu when using the SWT Cocoa bindings.
 * <p>
 * This code does not require the Cocoa SWT JAR in order to be compiled as it uses reflection to
 * access the Cocoa specific API methods. It does, however, depend on JFace (for IAction), but you
 * could easily modify the code to use SWT Listeners instead in order to use this class in SWT only
 * applications.
 * </p>
 * <p>
 * This code was influenced by the <a
 * href="http://www.simidude.com/blog/2008/macify-a-swt-application-in-a-cross-platform-way/"
 * >CarbonUIEnhancer from Agynami</a> with the implementation being modified from the <a href="http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.ui.cocoa/src/org/eclipse/ui/internal/cocoa/CocoaUIEnhancer.java"
 * >org.eclipse.ui.internal.cocoa.CocoaUIEnhancer</a>.
 * </p>
 * <p>
 * This class works with both the 32-bit and 64-bit versions of the SWT Cocoa bindings.
 * <p>
 * <p>
 * This class is released under the Eclipse Public License (<a href="http://www.eclipse.org/legal/epl-v10.html">EPL</a>).
 */
@SuppressWarnings("restriction")
public class CocoaUIEnhancer {

    private static final long kAboutMenuItem = 0;
    private static final long kPreferencesMenuItem = 2;
    // private static final long kServicesMenuItem = 4;
    // private static final long kHideApplicationMenuItem = 6;
    private static final long kQuitMenuItem = 10;

    static long sel_toolbarButtonClicked_;
    static long sel_preferencesMenuItemSelected_;
    static long sel_aboutMenuItemSelected_;
    static Callback proc3Args;

    final private String appName;

    /**
     * Class invoked via the Callback object to run the about and preferences actions.
     * <p>
     * If you don't use JFace in your application (SWT only), change the
     * {@link org.eclipse.jface.action.IAction}s to {@link org.eclipse.swt.widgets.Listener}s.
     * </p>
     */
    private static class MenuHookObject {
        final IAction about;
        final IAction pref;

        public MenuHookObject( IAction about, IAction pref ) {
            this.about = about;
            this.pref = pref;
        }

        /**
         * Will be called on 32bit SWT.
         */
        @SuppressWarnings( "unused" )
        public int actionProc( int id, int sel, int arg0 ) {
            return (int) actionProc( (long) id, (long) sel, (long) arg0 );
        }

        /**
         * Will be called on 64bit SWT.
         */
        public long actionProc( long id, long sel, long arg0 ) {
            if ( sel == sel_aboutMenuItemSelected_ ) {
                about.run();
            } else if ( sel == sel_preferencesMenuItemSelected_ ) {
                pref.run();
            } else {
                // Unknown selection!
            }
            // Return value is not used.
            return 99;
        }
    }

    /**
     * Construct a new CocoaUIEnhancer.
     * 
     * @param appName
     *            The name of the application. It will be used to customize the About and Quit menu
     *            items. If you do not wish to customize the About and Quit menu items, just pass
     *            <tt>null</tt> here.
     */
    public CocoaUIEnhancer( String appName ) {
        this.appName = appName;
    }

    /**
     * Hook the given Listener to the Mac OS X application Quit menu and the IActions to the About
     * and Preferences menus.
     * 
     * @param display
     *            The Display to use.
     * @param quitListener
     *            The listener to invoke when the Quit menu is invoked.
     * @param aboutAction
     *            The action to run when the About menu is invoked.
     * @param preferencesAction
     *            The action to run when the Preferences menu is invoked.
     */
    public void hookApplicationMenu( Display display, Listener quitListener, IAction aboutAction,
                                     IAction preferencesAction ) {
        // This is our callbackObject whose 'actionProc' method will be called when the About or
        // Preferences menuItem is invoked.
        MenuHookObject target = new MenuHookObject( aboutAction, preferencesAction );

        try {
            // Initialize the menuItems.
            initialize( target );
        } catch ( Exception e ) {
            throw new IllegalStateException( e );
        }

        // Connect the quit/exit menu.
        if ( !display.isDisposed() ) {
            display.addListener( SWT.Close, quitListener );
        }

        // Schedule disposal of callback object
        display.disposeExec( new Runnable() {
            public void run() {
                invoke( proc3Args, "dispose" );
            }
        } );
    }

    private void initialize( Object callbackObject )
            throws Exception {

        Class<?> osCls = classForName( "org.eclipse.swt.internal.cocoa.OS" );

        // Register names in objective-c.
        if ( sel_toolbarButtonClicked_ == 0 ) {
            // sel_toolbarButtonClicked_ = registerName( osCls, "toolbarButtonClicked:" ); //$NON-NLS-1$
            sel_preferencesMenuItemSelected_ = registerName( osCls, "preferencesMenuItemSelected:" ); //$NON-NLS-1$
            sel_aboutMenuItemSelected_ = registerName( osCls, "aboutMenuItemSelected:" ); //$NON-NLS-1$
        }

        // Create an SWT Callback object that will invoke the actionProc method of our internal
        // callbackObject.
        proc3Args = new Callback( callbackObject, "actionProc", 3 ); //$NON-NLS-1$
        Method getAddress = Callback.class.getMethod( "getAddress", new Class[0] );
        Object object = getAddress.invoke( proc3Args, (Object[]) null );
        long proc3 = convertToLong( object );
        if ( proc3 == 0 ) {
            SWT.error( SWT.ERROR_NO_MORE_CALLBACKS );
        }

        Class<?> nsmenuCls = classForName( "org.eclipse.swt.internal.cocoa.NSMenu" );
        Class<?> nsmenuitemCls = classForName( "org.eclipse.swt.internal.cocoa.NSMenuItem" );
        Class<?> nsstringCls = classForName( "org.eclipse.swt.internal.cocoa.NSString" );
        Class<?> nsapplicationCls = classForName( "org.eclipse.swt.internal.cocoa.NSApplication" );

        // Instead of creating a new delegate class in objective-c,
        // just use the current SWTApplicationDelegate. An instance of this
        // is a field of the Cocoa Display object and is already the target
        // for the menuItems. So just get this class and add the new methods
        // to it.
        object = invoke( osCls, "objc_lookUpClass", new Object[] { "SWTApplicationDelegate" } );
        long cls = convertToLong( object );

        // Add the action callbacks for Preferences and About menu items.
        invoke( osCls, "class_addMethod", new Object[] {
                                                        wrapPointer( cls ),
                                                        wrapPointer( sel_preferencesMenuItemSelected_ ),
                                                        wrapPointer( proc3 ),
                                                        "@:@" } ); //$NON-NLS-1$
        invoke( osCls, "class_addMethod", new Object[] {
                                                        wrapPointer( cls ),
                                                        wrapPointer( sel_aboutMenuItemSelected_ ),
                                                        wrapPointer( proc3 ),
                                                        "@:@" } ); //$NON-NLS-1$

        // Get the Mac OS X Application menu.
        Object sharedApplication = invoke( nsapplicationCls, "sharedApplication" );
        Object mainMenu = invoke( sharedApplication, "mainMenu" );
        Object mainMenuItem = invoke( nsmenuCls, mainMenu, "itemAtIndex", new Object[] { wrapPointer( 0 ) } );
        Object appMenu = invoke( mainMenuItem, "submenu" );

        // Create the About <application-name> menu command
        Object aboutMenuItem =
            invoke( nsmenuCls, appMenu, "itemAtIndex", new Object[] { wrapPointer( kAboutMenuItem ) } );
        if ( appName != null ) {
            Object nsStr = invoke( nsstringCls, "stringWith", new Object[] { "About " + appName } );
            invoke( nsmenuitemCls, aboutMenuItem, "setTitle", new Object[] { nsStr } );
        }
        // Rename the quit action.
        if ( appName != null ) {
            Object quitMenuItem =
                invoke( nsmenuCls, appMenu, "itemAtIndex", new Object[] { wrapPointer( kQuitMenuItem ) } );
            Object nsStr = invoke( nsstringCls, "stringWith", new Object[] { "Quit " + appName } );
            invoke( nsmenuitemCls, quitMenuItem, "setTitle", new Object[] { nsStr } );
        }

        // Enable the Preferences menuItem.
        Object prefMenuItem =
            invoke( nsmenuCls, appMenu, "itemAtIndex", new Object[] { wrapPointer( kPreferencesMenuItem ) } );
        invoke( nsmenuitemCls, prefMenuItem, "setEnabled", new Object[] { true } );

        // Set the action to execute when the About or Preferences menuItem is invoked.
        //
        // We don't need to set the target here as the current target is the SWTApplicationDelegate
        // and we have registerd the new selectors on it. So just set the new action to invoke the
        // selector.
        invoke( nsmenuitemCls, prefMenuItem, "setAction",
                new Object[] { wrapPointer( sel_preferencesMenuItemSelected_ ) } );
        invoke( nsmenuitemCls, aboutMenuItem, "setAction",
                new Object[] { wrapPointer( sel_aboutMenuItemSelected_ ) } );
    }

    private long registerName( Class<?> osCls, String name )
            throws IllegalArgumentException, SecurityException, IllegalAccessException,
            InvocationTargetException, NoSuchMethodException {
        Object object = invoke( osCls, "sel_registerName", new Object[] { name } );
        return convertToLong( object );
    }

    private long convertToLong( Object object ) {
        if ( object instanceof Integer ) {
            Integer i = (Integer) object;
            return i.longValue();
        }
        if ( object instanceof Long ) {
            Long l = (Long) object;
            return l.longValue();
        }
        return 0;
    }

    private static Object wrapPointer( long value ) {
        Class<?> PTR_CLASS = C.PTR_SIZEOF == 8 ? long.class : int.class;
        if ( PTR_CLASS == long.class ) {
            return new Long( value );
        } else {
            return new Integer( (int) value );
        }
    }

    private static Object invoke( Class<?> clazz, String methodName, Object[] args ) {
        return invoke( clazz, null, methodName, args );
    }

    private static Object invoke( Class<?> clazz, Object target, String methodName, Object[] args ) {
        try {
            Class<?>[] signature = new Class<?>[args.length];
            for ( int i = 0; i < args.length; i++ ) {
                Class<?> thisClass = args[i].getClass();
                if ( thisClass == Integer.class )
                    signature[i] = int.class;
                else if ( thisClass == Long.class )
                    signature[i] = long.class;
                else if ( thisClass == Byte.class )
                    signature[i] = byte.class;
                else if ( thisClass == Boolean.class )
                    signature[i] = boolean.class;
                else
                    signature[i] = thisClass;
            }
            Method method = clazz.getMethod( methodName, signature );
            return method.invoke( target, args );
        } catch ( Exception e ) {
            throw new IllegalStateException( e );
        }
    }

    private Class<?> classForName( String classname ) {
        try {
            Class<?> cls = Class.forName( classname );
            return cls;
        } catch ( ClassNotFoundException e ) {
            throw new IllegalStateException( e );
        }
    }

    private Object invoke( Class<?> cls, String methodName ) {
        return invoke( cls, methodName, (Class<?>[]) null, (Object[]) null );
    }

    private Object invoke( Class<?> cls, String methodName, Class<?>[] paramTypes, Object... arguments ) {
        try {
            Method m = cls.getDeclaredMethod( methodName, paramTypes );
            return m.invoke( null, arguments );
        } catch ( Exception e ) {
            throw new IllegalStateException( e );
        }
    }

    private Object invoke( Object obj, String methodName ) {
        return invoke( obj, methodName, (Class<?>[]) null, (Object[]) null );
    }

    private Object invoke( Object obj, String methodName, Class<?>[] paramTypes, Object... arguments ) {
        try {
            Method m = obj.getClass().getDeclaredMethod( methodName, paramTypes );
            return m.invoke( obj, arguments );
        } catch ( Exception e ) {
            throw new IllegalStateException( e );
        }
    }
}

关于java - SWT:如何在 OS X 上正确处理应用程序事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6665933/

相关文章:

java - OpenTypeSelectionDialog 无法解析为类型

java - 什么是shell脚本、makefile和ant文件

java - 如何从主方法(或任何方法)中的args[]初始化java中的常量,例如 "public static final Integer"

macos - 删除/更改列表突出显示颜色(或调整项目之间的空间)

objective-c - 向 MainWindow 添加 View 或窗口

git - 发现可疑 https ://github. com/Homebrew/brew git origin remote

javax.net.ssl.SSLHandshakeException : java. security.cert.CertPathValidatorException:找不到证书路径的信任 anchor

java - 从java应用程序调用智能合约函数,无需监听事件

SWT 浏览器 XULRUNNER 10 : how to enable javascript to access to system clipboard

java - Swing 应用程序中的 SWT 托盘图标