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}