【问题标题】:SWT: how to handle application events correctly on OS XSWT:如何在 OS X 上正确处理应用程序事件
【发布时间】:2011-10-03 16:51:06
【问题描述】:

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

【问题讨论】:

    标签: java macos swt


    【解决方案1】:

    您可以通过以下方式响应退出事件:

    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 );
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      我的热潮中至少有两个可能的答案:

      • 我遇到了一个Azureus 解决方案,它应该非常便携...
      • SWT 3.7 还对 OSX 有一些额外的支持,如 EclipseCon '11 中所述

      但是...我还没有尝试过这些...

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-10-03
        • 2011-10-05
        • 2010-09-22
        • 1970-01-01
        • 2011-06-18
        • 1970-01-01
        • 2012-04-20
        • 1970-01-01
        相关资源
        最近更新 更多