/**************************************************************************
OmegaT - Computer Assisted Translation (CAT) tool
with fuzzy matching, translation memory, keyword search,
glossaries, and translation leveraging into updated projects.
Copyright (C) 2014, 2015 Aaron Madlon-Kay
Home page: http://www.omegat.org/
Support center: http://groups.yahoo.com/group/OmegaT/
This file is part of OmegaT.
OmegaT is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OmegaT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/
package org.omegat.util.gui;
import java.awt.Image;
import java.awt.Window;
import java.awt.event.ActionListener;
import java.io.File;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import org.omegat.core.Core;
import org.omegat.core.CoreEvents;
import org.omegat.core.events.IApplicationEventListener;
import org.omegat.core.events.IProjectEventListener;
import org.omegat.gui.main.ProjectUICommands;
import org.omegat.util.Log;
import org.omegat.util.OConsts;
import org.omegat.util.Preferences;
import org.omegat.util.StaticUtils;
/**
* This class uses reflection to set Mac OS X-specific integration hooks.
*
* @author Aaron Madlon-Kay
*/
public final class OSXIntegration {
private OSXIntegration() {
}
public static final Image APP_ICON_MAC = ResourcesUtil.getBundledImage("OmegaT_mac.png");
private static volatile Class<?> appClass;
private static volatile Object app;
private static boolean guiLoaded = false;
private static final List<Runnable> DO_AFTER_LOAD = new ArrayList<>();
public static void init() {
try {
System.setProperty("apple.laf.useScreenMenuBar", "true");
System.setProperty("com.apple.mrj.application.apple.menu.about.name", "OmegaT");
// Set dock icon
Method setDockIconImage = getAppClass().getDeclaredMethod("setDockIconImage", Image.class);
setDockIconImage.invoke(getApp(), APP_ICON_MAC);
// Set quit strategy:
// app.setQuitStrategy(com.apple.eawt.QuitStrategy.CLOSE_ALL_WINDOWS);
Class<?> strategyClass = Class.forName("com.apple.eawt.QuitStrategy");
Method setQuitStrategy = getAppClass().getDeclaredMethod("setQuitStrategy", strategyClass);
setQuitStrategy.invoke(getApp(), strategyClass.getField("CLOSE_ALL_WINDOWS").get(null));
// Prevent sudden termination:
// app.disableSuddenTermination();
Method disableTerm = getAppClass().getDeclaredMethod("disableSuddenTermination");
disableTerm.invoke(getApp());
// Register to find out when app finishes loading so we can
// 1. Set up full-screen support, and...
CoreEvents.registerApplicationEventListener(APP_LISTENER);
// 2. The open file handler can defer opening a project until the GUI is ready.
setOpenFilesHandler(OPEN_FILES_HANDLER);
// Register listener to update the main window's proxy icon and modified indicators.
CoreEvents.registerProjectChangeListener(PROJECT_LISTENER);
} catch (Exception ex) {
Log.log(ex);
}
}
private static final IApplicationEventListener APP_LISTENER = new IApplicationEventListener() {
@Override
public void onApplicationStartup() {
guiLoaded = true;
synchronized (DO_AFTER_LOAD) {
for (Runnable r : DO_AFTER_LOAD) {
r.run();
}
DO_AFTER_LOAD.clear();
}
Window window = Core.getMainWindow().getApplicationFrame();
enableFullScreen(window);
}
@Override
public void onApplicationShutdown() {
guiLoaded = false;
}
};
private static final IOpenFilesHandler OPEN_FILES_HANDLER = new IOpenFilesHandler() {
@Override
public void openFiles(List<?> files) {
if (files.isEmpty()) {
return;
}
File firstFile = (File) files.get(0); // Ignore others
if (firstFile.getName().equals(OConsts.FILE_PROJECT)) {
firstFile = firstFile.getParentFile();
}
if (!StaticUtils.isProjectDir(firstFile)) {
return;
}
final File projDir = firstFile;
Runnable openProject = new Runnable() {
@Override
public void run() {
ProjectUICommands.projectOpen(projDir, true);
}
};
if (guiLoaded) {
SwingUtilities.invokeLater(openProject);
} else {
synchronized (DO_AFTER_LOAD) {
DO_AFTER_LOAD.add(openProject);
}
}
}
};
private static final IProjectEventListener PROJECT_LISTENER = eventType -> {
JRootPane rootPane = Core.getMainWindow().getApplicationFrame().getRootPane();
switch (eventType) {
case CREATE:
case LOAD:
String projDir = Core.getProject().getProjectProperties().getProjectRoot();
setProxyIcon(rootPane, new File(projDir));
break;
case CLOSE:
setProxyIcon(rootPane, null);
break;
case MODIFIED:
setModifiedIndicator(rootPane, true);
break;
case SAVE:
setModifiedIndicator(rootPane, false);
break;
default:
// Nothing
}
};
public static void setAboutHandler(final ActionListener al) {
try {
// Handler must implement com.apple.eawt.AboutHandler interface.
Class<?> aboutHandlerClass = Class.forName("com.apple.eawt.AboutHandler");
InvocationHandler ih = (proxy, method, args) -> {
if (method.getName().equals("handleAbout")) {
// Respond to handleAbout(com.apple.eawt.AppEvent.AboutEvent)
al.actionPerformed(null);
}
return null;
};
Object handler = Proxy.newProxyInstance(OSXIntegration.class.getClassLoader(),
new Class<?>[] { aboutHandlerClass }, ih);
// Set handler:
// app.setAboutHandler(handler);
Method setAboutHandler = getAppClass().getDeclaredMethod("setAboutHandler", aboutHandlerClass);
setAboutHandler.invoke(getApp(), handler);
} catch (Exception ex) {
Log.log(ex);
}
}
public static void setQuitHandler(final ActionListener al) {
try {
// Handler must implement com.apple.eawt.QuitHandler interface.
Class<?> quitHandlerClass = Class.forName("com.apple.eawt.QuitHandler");
InvocationHandler ih = (proxy, method, args) -> {
if (method.getName().equals("handleQuitRequestWith")) {
Class<?> quitResponseClass = Class.forName("com.apple.eawt.QuitResponse");
if (args != null && args.length > 1 && quitResponseClass.isInstance(args[1])
&& Preferences.isPreference(Preferences.ALWAYS_CONFIRM_QUIT)) {
// Respond to handleQuitRequestWith(com.apple.eawt.AppEvent.QuitEvent,
// com.apple.eawt.QuitResponse)
// Cancel the quit because OmegaT will prompt:
// arg1.cancelQuit();
Method cancelQuit = quitResponseClass.getDeclaredMethod("cancelQuit");
cancelQuit.invoke(args[1]);
}
al.actionPerformed(null);
}
return null;
};
Object handler = Proxy.newProxyInstance(OSXIntegration.class.getClassLoader(),
new Class<?>[] { quitHandlerClass }, ih);
// Set handler:
// app.setAboutHandler(handler);
Method setQuitHandler = getAppClass().getDeclaredMethod("setQuitHandler", quitHandlerClass);
setQuitHandler.invoke(getApp(), handler);
} catch (Exception ex) {
Log.log(ex);
}
}
public static void setOpenFilesHandler(final IOpenFilesHandler ofh) {
try {
// Handler must implement com.apple.eawt.OpenFilesHandler interface.
Class<?> openFilesHandlerClass = Class.forName("com.apple.eawt.OpenFilesHandler");
InvocationHandler ih = (proxy, method, args) -> {
try {
if (method.getName().equals("openFiles")) {
Class<?> filesEventClass = Class.forName("com.apple.eawt.AppEvent$FilesEvent");
if (args != null && args.length > 0 && filesEventClass.isInstance(args[0])) {
Object filesEvent = args[0];
// Respond to openFiles(com.apple.eawt.AppEvent.OpenFilesEvent)
// Get provided list of files:
// arg0.getFiles()
Method getFilesMethod = filesEventClass.getDeclaredMethod("getFiles");
Object filesList = getFilesMethod.invoke(filesEvent);
ofh.openFiles((List<?>) filesList);
}
}
} catch (Throwable t) {
Log.log(t);
}
return null;
};
Object handler = Proxy.newProxyInstance(OSXIntegration.class.getClassLoader(),
new Class<?>[] { openFilesHandlerClass }, ih);
// Set handler:
// app.setOpenFileHandler(handler);
Method setOpenFileHandler = getAppClass().getDeclaredMethod("setOpenFileHandler", openFilesHandlerClass);
setOpenFileHandler.invoke(getApp(), handler);
} catch (Exception ex) {
Log.log(ex);
}
}
public static void setPreferencesHandler(ActionListener listener) {
try {
// Handler must implement com.apple.eawt.PreferencesHandler interface.
Class<?> preferencesHandlerClass = Class.forName("com.apple.eawt.PreferencesHandler");
InvocationHandler ih = (proxy, method, args) -> {
if (method.getName().equals("handlePreferences")) {
// Respond to
// handlePreferences(com.apple.eawt.AppEvent.PreferencesHandler)
listener.actionPerformed(null);
}
return null;
};
Object handler = Proxy.newProxyInstance(OSXIntegration.class.getClassLoader(),
new Class<?>[] { preferencesHandlerClass }, ih);
// Set handler:
// app.setPreferencesHandler(handler);
Method setAboutHandler = getAppClass().getDeclaredMethod("setPreferencesHandler", preferencesHandlerClass);
setAboutHandler.invoke(getApp(), handler);
} catch (Exception ex) {
Log.log(ex);
}
}
public static void enableFullScreen(Window window) {
try {
// Enable full-screen mode:
// FullScreenUtilities.setWindowCanFullScreen(java.awt.Window, boolean)
Class<?> utilClass = Class.forName("com.apple.eawt.FullScreenUtilities");
Method setWindowCanFullScreen = utilClass.getMethod("setWindowCanFullScreen",
new Class<?>[] { java.awt.Window.class, Boolean.TYPE });
setWindowCanFullScreen.invoke(utilClass, window, true);
} catch (Exception ex) {
Log.log(ex);
}
}
public static void setProxyIcon(JRootPane rootPane, File file) {
rootPane.putClientProperty("Window.documentFile", file);
}
public static void setModifiedIndicator(JRootPane rootPane, boolean isModified) {
rootPane.putClientProperty("Window.documentModified", isModified);
}
public interface IOpenFilesHandler {
void openFiles(List<?> files);
}
private static Class<?> getAppClass() throws Exception {
if (appClass == null) {
appClass = Class.forName("com.apple.eawt.Application");
}
return appClass;
}
private static Object getApp() throws Exception {
if (app == null) {
Method getApp = getAppClass().getDeclaredMethod("getApplication");
app = getApp.invoke(null);
}
return app;
}
}