/* * Copyright 2014 by A-SIT, Secure Information Technology Center Austria * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://joinup.ec.europa.eu/software/page/eupl * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. */ package at.asit.pdfover.gui.osx; import java.lang.reflect.Method; 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; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import at.asit.pdfover.commons.Constants; import at.asit.pdfover.commons.Messages; /** * 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. *
* 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. Use SWT Listeners * instead in order to use this class in SWT only applications. * *
** This code was influenced by the CocoaUIEnhancer - Connect the About, Preferences and Quit menus in Mac OS X * Cocoa SWT and JFace applications. *
** This class works with both the 32-bit and 64-bit versions of the SWT Cocoa * bindings. *
*
* This class is released under the Eclipse Public License (EPL).
*/
public class CocoaUIEnhancer {
static final Logger log = LoggerFactory.getLogger(CocoaUIEnhancer.class);
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 long sel_hideApplicationMenuItemSelected_;
static Callback proc3Args;
/**
* Class invoked via the Callback object to run the about and preferences
* actions.
*/
private static class MenuHookObject {
final Listener about;
final Listener pref;
public MenuHookObject(final Listener aboutListener,
final Listener preferencesListener) {
this.about = aboutListener;
this.pref = preferencesListener;
}
/**
* Will be called on 32bit SWT.
* @param id
* @param sel
* @param arg0
* @return x
*/
@SuppressWarnings("unused")
public int actionProc(final int id, final int sel, final int arg0) {
return (int) this.actionProc((long) id, (long) sel, (long) arg0);
}
/**
* Will be called on 64bit SWT.
* @param id
* @param sel
* @param arg0
* @return x
*/
public long actionProc(final long id, final long sel, final long arg0) {
if (sel == sel_aboutMenuItemSelected_) {
if (log.isDebugEnabled()) {
log.debug("[MenuHookObject - actionProc] : About");
this.about.handleEvent(null);
}
} else if (sel == sel_preferencesMenuItemSelected_) {
if (log.isDebugEnabled()) {
log.debug("[MenuHookObject - actionProc] : Preferences");
}
this.pref.handleEvent(null);
} else {
if (log.isDebugEnabled()) {
log.debug("[MenuHookObject - actionProc] : Unknow selection!");
}
}
// Return value is not used.
return 99;
}
// Getters and setters
@SuppressWarnings("unused")
public Listener getAbout() {
return this.about;
}
@SuppressWarnings("unused")
public Listener getPref() {
return this.pref;
}
}
/**
* 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 aboutListener
* The listener to invoke when the About menu is invoked.
* @param preferencesListener
* The listener to invoke when the Preferences menu is invoked.
*/
public static void hookApplicationMenu(final Display display,
final Listener quitListener, final Listener aboutListener,
final Listener preferencesListener) {
// This is our callbackObject whose 'actionProc' method will be called
// when the About or
// Preferences menuItem is invoked.
final MenuHookObject target = new MenuHookObject(aboutListener,
preferencesListener);
try {
// Initialize the menuItems.
initialize(target);
} catch (final 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(() -> {
CocoaUIEnhancer.invoke(proc3Args, "dispose");
});
}
private static void initialize(final Object callbackObject) throws Exception {
final Class> osCls = classForName("org.eclipse.swt.internal.cocoa.OS");
// Register names in objective-c.
if (sel_toolbarButtonClicked_ == 0) {
// sel_toolbarButtonClicked_ = registerName( osCls, "toolbarButtonClicked:" );
sel_preferencesMenuItemSelected_ = registerName(osCls,
"preferencesMenuItemSelected:");
sel_aboutMenuItemSelected_ = registerName(osCls,
"aboutMenuItemSelected:");
}
// Create an SWT Callback object that will invoke the actionProc method
// of our internal
// callbackObject.
proc3Args = new Callback(callbackObject, "actionProc", 3);
final Method getAddress = Callback.class.getMethod("getAddress",
new Class[0]);
Object object = getAddress.invoke(proc3Args, (Object[]) null);
final long proc3 = convertToLong(object);
if (proc3 == 0) {
SWT.error(SWT.ERROR_NO_MORE_CALLBACKS);
}
final Class> nsmenuCls = classForName("org.eclipse.swt.internal.cocoa.NSMenu");
final Class> nsmenuitemCls = classForName("org.eclipse.swt.internal.cocoa.NSMenuItem");
final Class> nsstringCls = classForName("org.eclipse.swt.internal.cocoa.NSString");
final 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" });
final 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), "@:@" });
invoke(osCls, "class_addMethod", new Object[] { wrapPointer(cls),
wrapPointer(sel_aboutMenuItemSelected_), wrapPointer(proc3),
"@:@" });
// Get the Mac OS X Application menu.
final Object sharedApplication = invoke(nsapplicationCls,
"sharedApplication");
final Object mainMenu = invoke(sharedApplication, "mainMenu");
final Object mainMenuItem = invoke(nsmenuCls, mainMenu, "itemAtIndex",
new Object[] { wrapPointer(0) });
final Object appMenu = invoke(mainMenuItem, "submenu");
// Create the About