diff options
| author | tkellner <tkellner@174cde9d-5d70-4d2a-aa98-46368bc2aaf7> | 2014-01-27 18:16:14 +0000 | 
|---|---|---|
| committer | tkellner <tkellner@174cde9d-5d70-4d2a-aa98-46368bc2aaf7> | 2014-01-27 18:16:14 +0000 | 
| commit | ef724ae59cc18b1d934db431af81396341a3f8d0 (patch) | |
| tree | ff9d7f7f2219d59f1af88add36eb81fb56b61d79 /pdf-over-gui/src | |
| parent | 3219581f933a24fb2466da2eaca3c325b437ce44 (diff) | |
| download | pdf-over-ef724ae59cc18b1d934db431af81396341a3f8d0.tar.gz pdf-over-ef724ae59cc18b1d934db431af81396341a3f8d0.tar.bz2 pdf-over-ef724ae59cc18b1d934db431af81396341a3f8d0.zip | |
Add missing CocoaUIEnhancer class
git-svn-id: https://joinup.ec.europa.eu/svn/pdf-over/trunk@481 174cde9d-5d70-4d2a-aa98-46368bc2aaf7
Diffstat (limited to 'pdf-over-gui/src')
| -rw-r--r-- | pdf-over-gui/src/main/java/at/asit/pdfover/gui/osx/CocoaUIEnhancer.java | 372 | 
1 files changed, 372 insertions, 0 deletions
| diff --git a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/osx/CocoaUIEnhancer.java b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/osx/CocoaUIEnhancer.java new file mode 100644 index 00000000..d6076272 --- /dev/null +++ b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/osx/CocoaUIEnhancer.java @@ -0,0 +1,372 @@ +/* + * 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.apache.log4j.Logger; +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 at.asit.pdfover.gui.Constants; +import at.asit.pdfover.gui.utils.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. + * <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. 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.transparentech.com/opensource/cocoauienhancer" + * >CocoaUIEnhancer - Connect the About, Preferences and Quit menus in Mac OS X + * Cocoa SWT and JFace applications</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>). + */ +public class CocoaUIEnhancer { +	static final Logger log = Logger.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"); //$NON-NLS-1$ +					this.about.handleEvent(null); +				} +			} else if (sel == sel_preferencesMenuItemSelected_) { +				if (log.isDebugEnabled()) { +					log.debug("[MenuHookObject - actionProc] : Preferences"); //$NON-NLS-1$ +				} +				this.pref.handleEvent(null); + +			} else { +				if (log.isDebugEnabled()) { +					log.debug("[MenuHookObject - actionProc] : Unknow selection!"); //$NON-NLS-1$ +				} +			} +			// 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(new Runnable() { +			@Override +			public void run() { +				CocoaUIEnhancer.invoke(proc3Args, "dispose"); //$NON-NLS-1$ +			} +		}); +	} + +	private static void initialize(final Object callbackObject) throws Exception { + +		final Class<?> osCls = classForName("org.eclipse.swt.internal.cocoa.OS"); //$NON-NLS-1$ + +		// 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$ +		final Method getAddress = Callback.class.getMethod("getAddress", //$NON-NLS-1$ +				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"); //$NON-NLS-1$ +		final Class<?> nsmenuitemCls = classForName("org.eclipse.swt.internal.cocoa.NSMenuItem"); //$NON-NLS-1$ +		final Class<?> nsstringCls = classForName("org.eclipse.swt.internal.cocoa.NSString"); //$NON-NLS-1$ +		final Class<?> nsapplicationCls = classForName("org.eclipse.swt.internal.cocoa.NSApplication"); //$NON-NLS-1$ + +		// 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", //$NON-NLS-1$ +				new Object[] { "SWTApplicationDelegate" }); //$NON-NLS-1$ +		final long cls = convertToLong(object); + +		// Add the action callbacks for Preferences and About menu items. +		invoke(osCls, "class_addMethod", new Object[] { wrapPointer(cls), //$NON-NLS-1$ +				wrapPointer(sel_preferencesMenuItemSelected_), +				wrapPointer(proc3), "@:@" }); //$NON-NLS-1$ +		invoke(osCls, "class_addMethod", new Object[] { wrapPointer(cls), //$NON-NLS-1$ +				wrapPointer(sel_aboutMenuItemSelected_), wrapPointer(proc3), +				"@:@" }); //$NON-NLS-1$ + +		// Get the Mac OS X Application menu. +		final Object sharedApplication = invoke(nsapplicationCls, +				"sharedApplication"); //$NON-NLS-1$ +		final Object mainMenu = invoke(sharedApplication, "mainMenu"); //$NON-NLS-1$ +		final Object mainMenuItem = invoke(nsmenuCls, mainMenu, "itemAtIndex", //$NON-NLS-1$ +				new Object[] { wrapPointer(0) }); +		final Object appMenu = invoke(mainMenuItem, "submenu"); //$NON-NLS-1$ + +		// Create the About <application-name> menu command +		final Object aboutMenuItem = invoke(nsmenuCls, appMenu, "itemAtIndex", //$NON-NLS-1$ +				new Object[] { wrapPointer(kAboutMenuItem) }); +		final Object nsStrAbout = invoke(nsstringCls, "stringWith", //$NON-NLS-1$ +				new Object[] { String.format(Messages.getString("main.about"), Constants.APP_NAME) }); //$NON-NLS-1$ +		invoke(nsmenuitemCls, aboutMenuItem, "setTitle", //$NON-NLS-1$ +				new Object[] { nsStrAbout }); +		// Rename the quit action. +		final Object quitMenuItem = invoke(nsmenuCls, appMenu, +				"itemAtIndex", new Object[] { wrapPointer(kQuitMenuItem) }); //$NON-NLS-1$ +		final Object nsStrQuit = invoke(nsstringCls, "stringWith", //$NON-NLS-1$ +				new Object[] { String.format(Messages.getString("main.quit"), Constants.APP_NAME) }); //$NON-NLS-1$ +		invoke(nsmenuitemCls, quitMenuItem, "setTitle", //$NON-NLS-1$ +				new Object[] { nsStrQuit }); + +		// Rename the hide action. +		final Object hideMenuItem = invoke(nsmenuCls, appMenu, +				"itemAtIndex", //$NON-NLS-1$ +				new Object[] { wrapPointer(kHideApplicationMenuItem) }); +		final Object nsStrHide = invoke(nsstringCls, "stringWith", //$NON-NLS-1$ +				new Object[] { String.format(Messages.getString("main.hide"), Constants.APP_NAME) }); //$NON-NLS-1$ +		invoke(nsmenuitemCls, hideMenuItem, "setTitle", //$NON-NLS-1$ +				new Object[] { nsStrHide }); + +		// Enable the Preferences menuItem. +		final Object prefMenuItem = invoke(nsmenuCls, appMenu, "itemAtIndex", //$NON-NLS-1$ +				new Object[] { wrapPointer(kPreferencesMenuItem) }); +		invoke(nsmenuitemCls, prefMenuItem, "setEnabled", new Object[] { true }); //$NON-NLS-1$ + +		// 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", //$NON-NLS-1$ +				new Object[] { wrapPointer(sel_preferencesMenuItemSelected_) }); +		invoke(nsmenuitemCls, aboutMenuItem, "setAction", //$NON-NLS-1$ +				new Object[] { wrapPointer(sel_aboutMenuItemSelected_) }); +	} + +	private static long registerName(final Class<?> osCls, final String name) +			throws IllegalArgumentException, SecurityException { +		final Object object = invoke(osCls, "sel_registerName", //$NON-NLS-1$ +				new Object[] { name }); +		return convertToLong(object); +	} + +	private static long convertToLong(final Object object) { +		if (object instanceof Integer) { +			final Integer i = (Integer) object; +			return i.longValue(); +		} +		if (object instanceof Long) { +			final Long l = (Long) object; +			return l.longValue(); +		} +		return 0; +	} + +	private static Object wrapPointer(final long value) { +		final Class<?> PTR_CLASS = C.PTR_SIZEOF == 8 ? long.class : int.class; +		if (PTR_CLASS == long.class) { +			return new Long(value); +		} +		return new Integer((int) value); +	} + +	private static Object invoke(final Class<?> clazz, final String methodName, +			final Object[] args) { +		return invoke(clazz, null, methodName, args); +	} + +	private static Object invoke(final Class<?> clazz, final Object target, +			final String methodName, final Object[] args) { +		try { +			final Class<?>[] signature = new Class<?>[args.length]; +			for (int i = 0; i < args.length; i++) { +				final 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; +				} +			} +			final Method method = clazz.getMethod(methodName, signature); +			return method.invoke(target, args); +		} catch (final Exception e) { +			throw new IllegalStateException(e); +		} +	} + +	private static Class<?> classForName(final String classname) { +		try { +			final Class<?> cls = Class.forName(classname); +			return cls; +		} catch (final ClassNotFoundException e) { +			throw new IllegalStateException(e); +		} +	} + +	private static Object invoke(final Class<?> cls, final String methodName) { +		return invoke(cls, methodName, (Class<?>[]) null, (Object[]) null); +	} + +	private static Object invoke(final Class<?> cls, final String methodName, +			final Class<?>[] paramTypes, final Object... arguments) { +		try { +			final Method m = cls.getDeclaredMethod(methodName, paramTypes); +			return m.invoke(null, arguments); +		} catch (final Exception e) { +			throw new IllegalStateException(e); +		} +	} + +	static Object invoke(final Object obj, final String methodName) { +		return invoke(obj, methodName, (Class<?>[]) null, (Object[]) null); +	} + +	private static Object invoke(final Object obj, final String methodName, +			final Class<?>[] paramTypes, final Object... arguments) { +		try { +			final Method m = obj.getClass().getDeclaredMethod(methodName, +					paramTypes); +			return m.invoke(obj, arguments); +		} catch (final Exception e) { +			throw new IllegalStateException(e); +		} +	} +} | 
