123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 |
- /*
- * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
- package com.dmdirc.addons.ui_swing;
-
- import com.dmdirc.ServerManager;
- import com.dmdirc.addons.ui_swing.components.MenuBar;
- import com.dmdirc.actions.ActionManager;
- import com.dmdirc.actions.CoreActionType;
- import com.dmdirc.actions.interfaces.ActionType;
- import com.dmdirc.commandparser.commands.global.NewServer;
- import com.dmdirc.config.IdentityManager;
- import com.dmdirc.interfaces.ActionListener;
- import com.dmdirc.logger.ErrorLevel;
- import com.dmdirc.logger.Logger;
-
- import java.awt.event.ActionEvent;
-
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Field;
- import java.lang.reflect.Proxy;
- import java.lang.reflect.InvocationTargetException;
-
- import java.net.URI;
- import java.net.URISyntaxException;
- import javax.swing.UIManager;
- import javax.swing.JMenu;
- import javax.swing.JMenuItem;
-
- import java.util.ArrayList;
-
- /**
- * Integrate DMDirc with OS X better.
- */
- public final class Apple implements InvocationHandler, ActionListener {
-
- /**
- * Dummy interface for ApplicationEvent from the Apple UI on non-Apple platforms.
- * http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/ApplicationEvent.html
- */
- public interface ApplicationEvent {
-
- /**
- * Provides the filename associated with a particular AppleEvent.
- *
- * @return The filename associated with a particular AppleEvent.
- */
- String getFilename();
-
- /**
- * Whether or not this event is handled.
- *
- * @return True if the event is handled, false otherwise
- */
- boolean isHandled();
-
- /**
- * Sets the handled state of this event.
- *
- * @param handled The new 'handled' state for this event.
- */
- void setHandled(boolean handled);
-
- /**
- * Retrieves the source of this event.
- *
- * @return This event's source
- */
- Object getSource();
-
- /**
- * Get a string representation of this object.
- *
- * @return A string representation of this object.
- */
- @Override
- String toString();
- }
-
- /** The singleton instance of Apple. */
- private static Apple me;
- /** The "Application" object used to do stuff on OS X. */
- private static Object application;
- /** The "NSApplication" object used to do cocoa stuff on OS X. */
- private static Object nsApplication;
- /** Are we listening? */
- private boolean isListener = false;
- /** The MenuBar for the application. */
- private MenuBar menuBar = null;
- /** Has the CLIENT_OPENED action been called? */
- private volatile boolean clientOpened = false;
- /** Store any addresses that are opened before CLIENT_OPENED. */
- private final ArrayList<URI> addresses = new ArrayList<URI>();
-
- /**
- * Get the "Apple" instance.
- *
- * @return Apple instance.
- */
- public static Apple getApple() {
- if (me == null) {
- me = new Apple();
- }
- return me;
- }
-
- /**
- * Create the Apple class.
- * <p>This attempts to:</p>
- *
- * <ul>
- * <li>load the JNI library</li>
- * <li>register the callback</li>
- * <li>register a CLIENT_OPENED listener</li>
- * </ul>
- */
- private Apple() {
- if (isApple()) {
- try {
- System.loadLibrary("DMDirc-Apple");
- registerOpenURLCallback();
- ActionManager.addListener(this, CoreActionType.CLIENT_OPENED);
- } catch (UnsatisfiedLinkError ule) {
- Logger.userError(ErrorLevel.MEDIUM,
- "Unable to load JNI library.", ule);
- }
- }
- }
-
- /**
- * Get the "Application" object
- *
- * @return Object that on OSX will be an "Application"
- */
- public static Object getApplication() {
- if (application == null && isApple()) {
- try {
- final Class<?> app = Class.forName("com.apple.eawt.Application");
- final Method method = app.getMethod("getApplication",
- new Class[0]);
- application = method.invoke(null, new Object[0]);
- } catch (ClassNotFoundException ex) { // Probably not on OS X, do nothing.
- } catch (NoSuchMethodException ex) { // Probably not on OS X, do nothing.
- } catch (SecurityException ex) { // Probably not on OS X, do nothing.
- } catch (IllegalAccessException ex) { // Probably not on OS X, do nothing.
- } catch (IllegalArgumentException ex) { // Probably not on OS X, do nothing.
- } catch (InvocationTargetException ex) { // Probably not on OS X, do nothing.
- }
- }
- return application;
- }
-
- /**
- * Get the "NSApplication" object
- *
- * @return Object that on OSX will be an "NSApplication"
- */
- public static Object getNSApplication() {
- if (nsApplication == null && isApple()) {
- try {
- final Class<?> app = Class.forName(
- "com.apple.cocoa.application.NSApplication");
- final Method method = app.getMethod("sharedApplication",
- new Class[0]);
- nsApplication = method.invoke(null, new Object[0]);
- } catch (ClassNotFoundException ex) { // Probably not on OS X, do nothing.
- } catch (NoSuchMethodException ex) { // Probably not on OS X, do nothing.
- } catch (IllegalAccessException ex) { // Probably not on OS X, do nothing.
- } catch (InvocationTargetException ex) { // Probably not on OS X, do nothing.
- }
- }
- return nsApplication;
- }
-
- /**
- * Are we on OS X?
- *
- * @return true if we are running on OS X
- */
- public static boolean isApple() {
- return (System.getProperty("mrj.version") != null);
- }
-
- /**
- * Are we using the OS X look and feel?
- *
- * @return true if we are using the OS X look and feel
- */
- public static boolean isAppleUI() {
- return isApple() && UIManager.getLookAndFeel().getClass().getName().
- equals("apple.laf.AquaLookAndFeel");
- }
-
- /**
- * Set some OS X only UI settings.
- */
- public void setUISettings() {
- if (!isApple()) {
- return;
- }
-
- // Set some Apple OS X related stuff from http://tinyurl.com/6xwuld
- final String aaText = IdentityManager.getGlobalConfig().getOptionBool(
- "ui", "antialias") ? "on" : "off";
-
- System.setProperty("apple.awt.antialiasing", aaText);
- System.setProperty("apple.awt.textantialiasing", aaText);
- System.setProperty("apple.awt.showGrowBox", "true");
- System.setProperty("com.apple.mrj.application.apple.menu.about.name",
- "DMDirc");
- System.setProperty("apple.laf.useScreenMenuBar", "true");
- System.setProperty("com.apple.mrj.application.growbox.intrudes", "false");
- System.setProperty("com.apple.mrj.application.live-resize", "true");
- }
-
- /**
- * Request user attention (Bounce the dock).
- *
- * @param isCritical If this is false, the dock icon only bounces once,
- * otherwise it will bounce until clicked on.
- */
- public void requestUserAttention(final boolean isCritical) {
- if (!isApple()) {
- return;
- }
-
- try {
- final Field type = isCritical ? getNSApplication().getClass().
- getField("UserAttentionRequestCritical") : getNSApplication().
- getClass().getField("Informational");
- final Method method = getNSApplication().getClass().getMethod(
- "requestUserAttention", new Class[]{Integer.TYPE});
- method.invoke(getNSApplication(), new Object[]{type.get(null)});
- } catch (NoSuchFieldException ex) { // Probably not on OS X, do nothing.
- } catch (NoSuchMethodException ex) { // Probably not on OS X, do nothing.
- } catch (IllegalAccessException ex) { // Probably not on OS X, do nothing.
- } catch (InvocationTargetException ex) { // Probably not on OS X, do nothing.
- }
- }
-
- /**
- * Set this up as a listener for the Apple Events
- *
- * @return True if the listener was added, else false.
- */
- public boolean setListener() {
- if (!isApple() || isListener) {
- return false;
- }
-
- try {
- final Class listenerClass = Class.forName(
- "com.apple.eawt.ApplicationListener");
- final Object listener = Proxy.newProxyInstance(getClass().
- getClassLoader(), new Class[]{listenerClass}, this);
-
- Method method = getApplication().getClass().getMethod(
- "addApplicationListener", new Class[]{listenerClass});
- method.invoke(getApplication(), listener);
-
- isListener = true;
-
- method = getApplication().getClass().getMethod(
- "setEnabledPreferencesMenu", new Class[]{Boolean.TYPE});
- method.invoke(getApplication(), new Object[]{Boolean.TRUE});
-
- method =
- getApplication().getClass().getMethod("setEnabledAboutMenu",
- new Class[]{Boolean.TYPE});
- method.invoke(getApplication(), new Object[]{Boolean.TRUE});
- return true;
- } catch (ClassNotFoundException ex) { // Probably not on OS X, do nothing.
- } catch (NoSuchMethodException ex) { // Probably not on OS X, do nothing.
- } catch (IllegalAccessException ex) { // Probably not on OS X, do nothing.
- } catch (InvocationTargetException ex) { // Probably not on OS X, do nothing.
- }
- return false;
- }
-
- /**
- * {@inheritDoc}
- *
- * @throws Throwable Throws stuff on errors
- */
- @Override
- public Object invoke(final Object proxy, final Method method,
- final Object[] args) throws Throwable {
- if (!isApple()) {
- return null;
- }
-
- try {
- final ApplicationEvent event = (ApplicationEvent) Proxy.
- newProxyInstance(getClass().getClassLoader(), new Class[]{
- ApplicationEvent.class}, new InvocationHandler() {
-
- /** {@inheritDoc} */
- @Override
- public Object invoke(final Object p, final Method m,
- final Object[] a) throws Throwable {
- return args[0].getClass().getMethod(m.getName(), m.
- getParameterTypes()).invoke(args[0], a);
- }
- });
- Method thisMethod = this.getClass().getMethod(method.getName(),
- new Class[]{ApplicationEvent.class});
- return thisMethod.invoke(this, event);
- } catch (NoSuchMethodException e) {
- if (method.getName().equals("equals") && args.length == 1) {
- return Boolean.valueOf(proxy == args[0]);
- }
- }
-
- return null;
- }
-
- /**
- * Set the MenuBar.
- * This will unset all menu mnemonics aswell if on the OSX ui.
- *
- * @param menuBar MenuBar to use to send events to,
- */
- public void setMenuBar(final MenuBar menuBar) {
- this.menuBar = menuBar;
- if (isAppleUI()) {
- for (int i = 0; i < menuBar.getMenuCount(); i++) {
- final JMenu menu = menuBar.getMenu(i);
- if (menu != null) {
- menu.setMnemonic(0);
- for (int j = 0; j < menu.getItemCount(); j++) {
- final JMenuItem menuItem = menu.getItem(j);
- if (menuItem != null) {
- menuItem.setMnemonic(0);
- }
- }
- }
- }
- }
- }
-
- /**
- * Handle an event using the menuBar
- *
- * @param name The name of the event according to the menubar
- * @param event The ApplicationEvent we are handingle
- */
- public void handleMenuBarEvent(final String name,
- final ApplicationEvent event) {
- if (!isApple() || menuBar == null) {
- return;
- }
- final ActionEvent actionEvent = new ActionEvent(this,
- ActionEvent.ACTION_PERFORMED, name);
-
- menuBar.actionPerformed(actionEvent);
- event.setHandled(true);
- }
-
- /**
- * This is called when Quit is selected from the Application menu
- *
- * @param event an ApplicationEvent object
- */
- public void handleQuit(final ApplicationEvent event) {
- handleMenuBarEvent("Exit", event);
- }
-
- /**
- * This is called when About is selected from the Application menu
- *
- * @param event an ApplicationEvent object
- */
- public void handleAbout(final ApplicationEvent event) {
- handleMenuBarEvent("About", event);
- }
-
- /**
- * This is called when Preferences is selected from the Application menu
- *
- * @param event an ApplicationEvent object
- */
- public void handlePreferences(final ApplicationEvent event) {
- handleMenuBarEvent("Preferences", event);
- }
-
- /**
- * This is called when the Application is opened
- *
- * @param event an ApplicationEvent object
- */
- public void handleOpenApplication(final ApplicationEvent event) {
- }
-
- /**
- * This is called when the application is asked to open a file
- *
- * @param event an ApplicationEvent object
- */
- public void handleOpenFile(final ApplicationEvent event) {
- }
-
- /**
- * This is called when asked to print
- *
- * @param event an ApplicationEvent object
- */
- public void handlePrintFile(final ApplicationEvent event) {
- }
-
- /**
- * This is called when the application is reopened
- *
- * @param event an ApplicationEvent object
- */
- public void handleReopenApplication(final ApplicationEvent event) {
- }
-
- /** {@inheritDoc} */
- @Override
- public void processEvent(final ActionType type, final StringBuffer format,
- final Object... arguments) {
- if (type == CoreActionType.CLIENT_OPENED) {
- synchronized (addresses) {
- clientOpened = true;
- for (URI addr : addresses) {
- ServerManager.getServerManager().connectToAddress(addr);
- }
- addresses.clear();
- }
- }
- }
-
- /**
- * Callback from JNI library.
- * If called before the client has finished opening, the URL will be added to
- * a list that will be connected to once the CLIENT_OPENED action is called.
- * Otherwise we connect right away.
- *
- * @param url The irc url to connect to.
- */
- public void handleOpenURL(final String url) {
- if (isApple()) {
- try {
- synchronized (addresses) {
- final URI addr = NewServer.getURI(url);
- if (!clientOpened) {
- addresses.add(addr);
- } else {
- // When the JNI callback is called there is no
- // ContextClassLoader set, which causes an NPE in
- // IconManager if no servers have been connected to yet.
- if (Thread.currentThread().getContextClassLoader() ==
- null) {
- Thread.currentThread().setContextClassLoader(ClassLoader.
- getSystemClassLoader());
- }
- ServerManager.getServerManager().connectToAddress(addr);
- }
- }
- } catch (URISyntaxException iae) {
- }
- }
- }
-
- /**
- * Register the getURL Callback.
- *
- * @return 0 on success, 1 on failure.
- */
- private synchronized final native int registerOpenURLCallback();
- }
|