package org.basex.gui;
import java.awt.Image;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.basex.core.Prop;
import org.basex.gui.layout.BaseXLayout;
/**
* Sets some Mac OS X specific interface options.
*
* @author BaseX Team 2005-12, BSD License
* @author Bastian Lemke
*/
public final class GUIMacOSX {
/** Native class name. */
private static final String C_APPLICATION =
"com.apple.eawt.Application";
/** Native class name. */
private static final String C_APPLICATION_LISTENER =
"com.apple.eawt.ApplicationListener";
/** Native class name. */
private static final String C_APPLICATION_EVENT =
"com.apple.eawt.ApplicationEvent";
/** System property identifier. */
private static final String P_ABOUT_NAME =
"com.apple.mrj.application.apple.menu.about.name";
/** System property identifier. */
private static final String P_SCREEN_MENU_BAR =
"apple.laf.useScreenMenuBar";
/** Empty class array. */
private static final Class<?>[] EC = {};
/** Empty object array. */
private static final Object[] EO = {};
/** Reference to the main gui. */
GUI main;
/** Instance of the 'Application' class. */
private final Object appObj;
/**
* Constructor.
* @throws Exception if any error occurs.
*/
public GUIMacOSX() throws Exception {
// name for the dock icon and the application menu
System.setProperty(P_ABOUT_NAME, Prop.NAME);
// show menu in the screen menu instead of inside the application window
System.setProperty(P_SCREEN_MENU_BAR, "true");
// load native java classes...
/* Reference to the loaded 'Application' class. */
final Class<?> appClass = Class.forName(C_APPLICATION);
appObj = invoke(appClass, null, "getApplication", EC, EO);
Class.forName(C_APPLICATION_EVENT);
if(appObj != null) {
invoke(appObj, "addAboutMenuItem");
invoke(appObj, "setEnabledAboutMenu", true);
invoke(appObj, "addPreferencesMenuItem");
invoke(appObj, "setEnabledPreferencesMenu", true);
addDockIcon();
final Class<?> alc = Class.forName(C_APPLICATION_LISTENER);
final Object listener = Proxy.newProxyInstance(
getClass().getClassLoader(), new Class[] { alc},
new AppInvocationHandler());
invoke(appObj, "addApplicationListener", alc, listener);
}
}
/**
* Initializes this mac gui with the main gui. Has to be called
* immediately after creating the gui.
* @param gui main gui reference
*/
public void init(final GUI gui) {
main = gui;
}
/**
* Adds the project icon to the dock.
* @throws Exception if any error occurs.
*/
private void addDockIcon() throws Exception {
invoke(appObj, "setDockIconImage", Image.class, BaseXLayout.image("logo"));
}
/**
* Sets a value for the badge in the dock.
* @param value string value
* @throws Exception if any error occurs
*/
public void setBadge(final String value) throws Exception {
invoke(appObj, "setDockIconBadge", String.class, value);
}
/**
* Handler for the native application events.
* @author Bastian Lemke
*/
class AppInvocationHandler implements InvocationHandler {
@Override
public Object invoke(final Object proxy, final Method method,
final Object[] args) throws Throwable {
final Object obj = args[0];
/*
* Get the name of the method and call the method of this object that has
* the same name with only the first argument.
* This emulates the an implementation of the native 'ApplicationListener'
* interface (@see com.apple.eawt.ApplicationListener on Mac OS X).
* The argument is an instance of the 'ApplicationEvent' class
* (@see com.apple.eawt.ApplicationEvent)
*/
try {
GUIMacOSX.invoke(this, method.getName(), Object.class, obj);
} catch(final NoSuchMethodException ex) {
GUIMacOSX.invoke(this, method.getName());
}
// mark the current event as 'handled'
GUIMacOSX.invoke(obj, "setHandled", true);
return null;
}
/** Called when the user selects the About item in the application menu. */
public void handleAbout() {
// explicit cast to circumvent Java compiler bug
GUICommands.C_ABOUT.execute(main);
}
/**
* Called when the application receives an Open Application event from the
* Finder or another application.
*/
public void handleOpenApplication() { /* NOT IMPLEMENTED */ }
/**
* Called when the application receives an Open Document event from the
* Finder or another application.
* @param obj application event
*/
public void handleOpenFile(@SuppressWarnings("unused") final Object obj) {
// get the associated filename:
// final String name = (String) GUIMacOSX.invoke(obj, "getFilename");
}
/** Called when the Preference item in the application menu is selected. */
public void handlePreferences() {
// explicit cast to circumvent Java compiler bug
((GUICommand) GUICommands.C_PREFS).execute(main);
}
/**
* Called when the application is sent a request to print a particular file
* or files.
*/
public void handlePrintFile() { /* NOT IMPLEMENTED */ }
/** Called when the application is sent the Quit event. */
public void handleQuit() {
// explicit cast to circumvent Java compiler bug
((GUICommand) GUICommands.C_EXIT).execute(main);
}
/**
* Called when the application receives a Reopen Application event from the
* Finder or another application.
*/
public void handleReOpenApplication() {
if(main != null) main.setVisible(true);
}
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
/**
* Invokes a method without arguments on the given object.
* @param obj object on which the method should be invoked
* @param method name of the method to invoke
* @return return value of the method
* @throws Exception if any error occurs.
*/
static Object invoke(final Object obj, final String method)
throws Exception {
return invoke(obj.getClass(), obj, method, EC, EO);
}
/**
* Invokes a method on the given object that expects a single boolean value as
* argument.
* @param obj object on which the method should be invoked
* @param method name of the method to invoke
* @param arg boolean value that is passed as argument
* @return return value of the method
* @throws Exception if any error occurs.
*/
static Object invoke(final Object obj, final String method,
final boolean arg) throws Exception {
return invoke(obj, method, Boolean.TYPE, arg);
}
/**
* Invokes a method on the given object that expects a single argument.
* @param obj object on which the method should be invoked
* @param method name of the method to invoke
* @param argClass "type" of the argument
* @param argObject argument value
* @return return value of the method
* @throws Exception if any error occurs
*/
static Object invoke(final Object obj, final String method,
final Class<?> argClass, final Object argObject) throws Exception {
final Class<?>[] argClasses = { argClass };
final Object[] argObjects = { argObject };
return invoke(obj.getClass(), obj, method, argClasses, argObjects);
}
/**
* Invokes a method on the given object that expects multiple arguments.
* @param clazz class object to get the method from
* @param obj object on which the method should be invoked. Can be
* {@code null} for static methods
* @param method name of the method to invoke
* @param argClasses "types" of the arguments
* @param argObjects argument values
* @return return value of the method
* @throws Exception if any error occurs
*/
private static Object invoke(final Class<?> clazz, final Object obj,
final String method, final Class<?>[] argClasses,
final Object[] argObjects) throws Exception {
return clazz.getMethod(method, argClasses).invoke(obj, argObjects);
}
}