3
如何正確使用SWT正確處理OS X的應用程序事件(退出,關於首選項)?對於AWT/Swing,com.apple.eawt.Application
和.ApplicationAdapter
工作正常。從我boomarksSWT:如何在OS X上正確處理應用程序事件
如何正確使用SWT正確處理OS X的應用程序事件(退出,關於首選項)?對於AWT/Swing,com.apple.eawt.Application
和.ApplicationAdapter
工作正常。從我boomarksSWT:如何在OS X上正確處理應用程序事件
可以與S中的退出事件作出響應沿着這些線路omething:
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);
}
}
}