001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.system;
020
021import icy.gui.dialog.LoaderDialog;
022import icy.gui.frame.AboutFrame;
023import icy.gui.preferences.GeneralPreferencePanel;
024import icy.gui.preferences.PreferenceFrame;
025import icy.main.Icy;
026import icy.resource.ResourceUtil;
027import icy.system.thread.ThreadUtil;
028
029import java.awt.Toolkit;
030import java.beans.PropertyChangeListener;
031import java.lang.reflect.InvocationHandler;
032import java.lang.reflect.Method;
033import java.lang.reflect.Proxy;
034
035/**
036 * OSX application compatibility class
037 * 
038 * @author stephane
039 */
040public class AppleUtil
041{
042    static final Thread fixThread = new Thread(new Runnable()
043    {
044        @Override
045        public void run()
046        {
047            appleFixLiveRun();
048        }
049    }, "AppleFix");
050
051    @SuppressWarnings({"unchecked", "rawtypes"})
052    public static void init()
053    {
054        // only when we have the GUI
055        if (!Icy.getMainInterface().isHeadLess())
056        {
057            try
058            {
059                final ClassLoader classLoader = SystemUtil.getSystemClassLoader();
060                final Class appClass = classLoader.loadClass("com.apple.eawt.Application");
061                final Object app = appClass.newInstance();
062
063                final Class listenerClass = classLoader.loadClass("com.apple.eawt.ApplicationListener");
064                final Object listener = Proxy.newProxyInstance(classLoader, new Class[] {listenerClass},
065                        new InvocationHandler()
066                        {
067                            @Override
068                            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
069                            {
070                                final Object applicationEvent = args[0];
071                                final Class appEventClass = applicationEvent.getClass();
072                                final Method m = appEventClass.getMethod("setHandled", boolean.class);
073
074                                if (method.getName().equals("handleQuit"))
075                                {
076                                    m.invoke(applicationEvent, Boolean.valueOf(Icy.exit(false)));
077                                }
078                                if (method.getName().equals("handleAbout"))
079                                {
080                                    new AboutFrame();
081                                    m.invoke(applicationEvent, Boolean.valueOf(true));
082                                }
083                                if (method.getName().equals("handleOpenFile"))
084                                {
085                                    new LoaderDialog();
086                                    m.invoke(applicationEvent, Boolean.valueOf(true));
087                                }
088                                if (method.getName().equals("handlePreferences"))
089                                {
090                                    new PreferenceFrame(GeneralPreferencePanel.NODE_NAME);
091                                    m.invoke(applicationEvent, Boolean.valueOf(true));
092                                }
093
094                                return null;
095                            }
096                        });
097
098                Method m;
099
100                m = appClass.getMethod("addApplicationListener", listenerClass);
101                m.invoke(app, listener);
102                m = appClass.getMethod("setDockIconImage", java.awt.Image.class);
103                m.invoke(app, ResourceUtil.IMAGE_ICY_256);
104                m = appClass.getMethod("addPreferencesMenuItem");
105                m.invoke(app);
106
107                // set menu bar name
108                SystemUtil.setProperty("com.apple.mrj.application.apple.menu.about.name", "Icy");
109
110            }
111            catch (Exception e)
112            {
113                System.err.println("Can't install OSX application wrapper...");
114            }
115        }
116
117        // start the fix thread
118        fixThread.start();
119    }
120
121    /**
122     * Apple fix live run (fixes specific OS X JVM stuff)
123     */
124    static void appleFixLiveRun()
125    {
126        while (true)
127        {
128            final Toolkit toolkit = Toolkit.getDefaultToolkit();
129
130            // fix memory leak introduced in java 1.6.0_29 in Mac OS X JVM
131            // TODO : remove this when issue will be resolved in JVM
132            final PropertyChangeListener[] leak = toolkit.getPropertyChangeListeners("apple.awt.contentScaleFactor");
133
134            // remove listener
135            for (int i = 0; i < leak.length; i++)
136                toolkit.removePropertyChangeListener("apple.awt.contentScaleFactor", leak[i]);
137
138            // no need more...
139            ThreadUtil.sleep(500);
140        }
141    }
142}