/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
* * Contributors:
* IBM Corporation - initial API and implementation
* Aelitis - Adaptation for Azureus
*******************************************************************************/
package org.gudy.azureus2.ui.swt.osx;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.*;
import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.util.AERunnable;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.platform.macosx.access.jnilib.OSXAccess;
import org.gudy.azureus2.ui.swt.UIExitUtilsSWT;
import org.gudy.azureus2.ui.swt.Utils;
import org.gudy.azureus2.ui.swt.config.wizard.ConfigureWizard;
import org.gudy.azureus2.ui.swt.help.AboutWindow;
import org.gudy.azureus2.ui.swt.mainwindow.TorrentOpener;
import org.gudy.azureus2.ui.swt.nat.NatTestWindow;
import org.gudy.azureus2.ui.swt.speedtest.SpeedTestWizard;
import com.aelitis.azureus.core.AzureusCore;
import com.aelitis.azureus.core.AzureusCoreFactory;
import com.aelitis.azureus.core.AzureusCoreRunningListener;
import com.aelitis.azureus.ui.UIFunctions;
import com.aelitis.azureus.ui.UIFunctionsManager;
public class CarbonUIEnhancer
{
// Most of these constants come from SWT OS.java. Could have used reflection
// to get them, but I assume they are truly constant (will never change)
private static final int noErr = 0;
private static final int eventNotHandledErr = -9874;
private static final int kEventWindowToolbarSwitchMode = 150;
private static final int kWindowToolbarButtonAttribute = (1 << 6);
private static final int kEventAppleEvent = 1;
private static final int kEventProcessCommand = 1;
private static final int kCFAllocatorDefault = 0;
private static final int kMenuItemAttrSeparator = 64;
private static final int kCFURLPOSIXPathStyle = 0;
private static final int kEventClassWindow = ('w' << 24) + ('i' << 16)
+ ('n' << 8) + 'd';
private static final int kAEQuitApplication = ('q' << 24) + ('u' << 16)
+ ('i' << 8) + 't';
private static final int kEventClassAppleEvent = ('e' << 24) + ('p' << 16)
+ ('p' << 8) + 'c';
private static final int kEventParamDirectObject = ('-' << 24) + ('-' << 16)
+ ('-' << 8) + '-';
private static final int kEventClassCommand = ('c' << 24) + ('m' << 16)
+ ('d' << 8) + 's';
private static final int kEventParamAEEventID = ('e' << 24) + ('v' << 16)
+ ('t' << 8) + 'i';
private static final int typeHICommand = ('h' << 24) + ('c' << 16)
+ ('m' << 8) + 'd';
private static final int typeFSRef = ('f' << 24) + ('s' << 16) + ('r' << 8)
+ 'f';
private static final int typeWindowRef = ('w' << 24) + ('i' << 16)
+ ('n' << 8) + 'd';
private static final int typeType = ('t' << 24) + ('y' << 16) + ('p' << 8)
+ 'e';
private static final int kHICommandPreferences = ('p' << 24) + ('r' << 16)
+ ('e' << 8) + 'f';
private static final int kHICommandAbout = ('a' << 24) + ('b' << 16)
+ ('o' << 8) + 'u';
private static final int kHICommandServices = ('s' << 24) + ('e' << 16)
+ ('r' << 8) + 'v';
private static final int kHICommandWizard = ('a' << 24) + ('z' << 16)
+ ('c' << 8) + 'n';
private static final int kHICommandNatTest = ('a' << 24) + ('z' << 16)
+ ('n' << 8) + 't';
private static final int kHICommandSpeedTest = ('a' << 24) + ('z' << 16)
+ ('s' << 8) + 't';
private static final int kHICommandRestart = ('a' << 24) + ('z' << 16)
+ ('r' << 8) + 's';
private static final int typeAEList = ('l' << 24) + ('i' << 16) + ('s' << 8)
+ 't';
private static final int kCoreEventClass = ('a' << 24) + ('e' << 16)
+ ('v' << 8) + 't';
private static final int kAEOpenDocuments = ('o' << 24) + ('d' << 16)
+ ('o' << 8) + 'c';
private static final int kAEReopenApplication = ('r' << 24) + ('a' << 16)
+ ('p' << 8) + 'p';
private static final int kAEOpenContents = ('o' << 24) + ('c' << 16)
+ ('o' << 8) + 'n';
private static final int kURLEventClass = ('G' << 24) + ('U' << 16)
+ ('R' << 8) + 'L';
private static final int typeText = ('T' << 24) + ('E' << 16) + ('X' << 8)
+ 'T';
private static String fgAboutActionName;
private static String fgWizardActionName;
private static String fgNatTestActionName;
private static String fgRestartActionName;
private static String fgSpeedTestActionName;
private static int memmove_type = 0;
/**
* KN: Some of the menu items have been removed for the Vuze and Vuze Advanced UI's;
* the classic UI still retains all its menu items as before. Follow this flag in the code
* to see which menu items are effected.
*/
private boolean isAZ3 = "az3".equalsIgnoreCase(COConfigurationManager.getStringParameter("ui"));
private static Class<?> claCallback;
private static Constructor<?> constCallback3;
private static Method mCallback_getAddress;
private static Method mCallback_dispose;
private static Class<?> claOS;
private static Class<?> claHICommand;
private static Class<?> claCFRange;
private static Class<?> claAEDesc;
private static Class<?> claEventRecord;
//public static final int BOUNCE_SINGLE = NSApplication.UserAttentionRequestInformational;
//public static final int BOUNCE_CONTINUOUS = NSApplication.UserAttentionRequestCritical;
static {
try {
claCallback = Class.forName("org.eclipse.swt.internal.Callback");
//public Callback (Object object, String method, int argCount) {
constCallback3 = claCallback.getConstructor(new Class[] {
Object.class,
String.class,
int.class,
});
// public int /*long*/ getAddress () {
mCallback_getAddress = claCallback.getMethod("getAddress", new Class[] {});
// public void dispose () {
mCallback_dispose = claCallback.getMethod("dispose", new Class[] {});
claOS = Class.forName("org.eclipse.swt.internal.carbon.OS");
claHICommand = Class.forName("org.eclipse.swt.internal.carbon.HICommand");
claCFRange = Class.forName("org.eclipse.swt.internal.carbon.CFRange");
claAEDesc = Class.forName("org.eclipse.swt.internal.carbon.AEDesc");
claEventRecord = Class.forName("org.eclipse.swt.internal.carbon.EventRecord");
} catch (Exception e) {
}
}
public CarbonUIEnhancer() {
if (fgAboutActionName == null) {
fgAboutActionName = MessageText.getString("MainWindow.menu.help.about").replaceAll(
"&", "");
}
if (false == isAZ3) {
if (fgWizardActionName == null) {
fgWizardActionName = MessageText.getString(
"MainWindow.menu.file.configure").replaceAll("&", "");
}
if (fgNatTestActionName == null) {
fgNatTestActionName = MessageText.getString(
"MainWindow.menu.tools.nattest").replaceAll("&", "");
}
if (fgSpeedTestActionName == null) {
fgSpeedTestActionName = MessageText.getString(
"MainWindow.menu.tools.speedtest").replaceAll("&", "");
}
}
if (fgRestartActionName == null) {
fgRestartActionName = MessageText.getString(
"MainWindow.menu.file.restart").replaceAll("&", "");
}
earlyStartup();
registerTorrentFile();
}
public static void registerToolbarToggle(Shell shell) {
try {
final Object toolbarToggleCB = constCallback3.newInstance(
CarbonUIEnhancer.class, "toolbarToggle", 3);
int toolbarToggle = ((Number) mCallback_getAddress.invoke(
toolbarToggleCB, new Object[] {})).intValue();
if (toolbarToggle == 0) {
Debug.out("OSX: Could not find callback 'toolbarToggle'");
mCallback_dispose.invoke(toolbarToggleCB, new Object[] {});
return;
}
shell.getDisplay().disposeExec(new Runnable() {
public void run() {
try {
mCallback_dispose.invoke(toolbarToggleCB, new Object[] {});
} catch (Exception e) {
}
}
});
// add the button to the window trim
Object oHandle = shell.getClass().getField("handle").get(shell);
int windowHandle = ((Number) invoke(claOS, null, "GetControlOwner",
new Object[] {
oHandle
})).intValue();
invoke(claOS, null, "ChangeWindowAttributes", new Object[] {
windowHandle,
kWindowToolbarButtonAttribute,
0
});
int[] mask = new int[] {
kEventClassWindow,
kEventWindowToolbarSwitchMode
};
// register the handler with the OS
int applicationEventTarget = ((Number) invoke(claOS, null,
"GetApplicationEventTarget", new Object[] {})).intValue();
// int InstallEventHandler(int inTarget, int inHandler, int inNumTypes, int[] inList, int inUserData, int[] outRef);
invoke(claOS, null, "InstallEventHandler", new Class[] {
int.class,
int.class,
int.class,
int[].class,
int.class,
int[].class
}, new Object[] {
applicationEventTarget,
toolbarToggle,
mask.length / 2,
mask,
0,
null
});
} catch (Throwable e) {
Debug.out("RegisterToolbarToggle failed", e);
}
}
private void registerTorrentFile() {
try {
int result;
Object clickDockIconCallback = constCallback3.newInstance(
CarbonUIEnhancer.class, "clickDockIcon", 3);
int clickDocIcon = ((Number) mCallback_getAddress.invoke(
clickDockIconCallback, new Object[] {})).intValue();
if (clickDocIcon == 0) {
mCallback_dispose.invoke(clickDockIconCallback, new Object[] {});
} else {
result = ((Number) invoke(claOS, null, "AEInstallEventHandler",
new Object[] {
kCoreEventClass,
kAEReopenApplication,
clickDocIcon,
(int) 0,
false
})).intValue();
if (result != noErr) {
Debug.out("OSX: Could Install ReopenApplication Event Handler. Error: "
+ result);
}
}
Object openContentsCallback = constCallback3.newInstance(
CarbonUIEnhancer.class, "openContents", 3);
int openContents = ((Number) mCallback_getAddress.invoke(
openContentsCallback, new Object[] {})).intValue();
if (openContents == 0) {
mCallback_dispose.invoke(openContentsCallback, new Object[] {});
} else {
result = ((Number) invoke(claOS, null, "AEInstallEventHandler",
new Object[] {
kCoreEventClass,
kAEOpenContents,
openContents,
(int) 0,
false
})).intValue();
if (result != noErr) {
Debug.out("OSX: Could Install OpenContents Event Handler. Error: "
+ result);
}
}
Object openDocCallback = constCallback3.newInstance(
CarbonUIEnhancer.class, "openDocProc", 3);
int openDocProc = ((Number) mCallback_getAddress.invoke(openDocCallback,
new Object[] {})).intValue();
if (openDocProc == 0) {
Debug.out("OSX: Could not find Callback 'openDocProc'");
mCallback_dispose.invoke(openDocCallback, new Object[] {});
return;
}
result = ((Number) invoke(claOS, null, "AEInstallEventHandler",
new Object[] {
kCoreEventClass,
kAEOpenDocuments,
openDocProc,
(int) 0,
false
})).intValue();
if (result != noErr) {
Debug.out("OSX: Could not Install OpenDocs Event Handler. Error: "
+ result);
return;
}
result = ((Number) invoke(claOS, null, "AEInstallEventHandler",
new Object[] {
kURLEventClass,
kURLEventClass,
openDocProc,
(int) 0,
false
})).intValue();
if (result != noErr) {
Debug.out("OSX: Could not Install URLEventClass Event Handler. Error: "
+ result);
return;
}
///
Object quitAppCallback = constCallback3.newInstance(
CarbonUIEnhancer.class, "quitAppProc", 3);
int quitAppProc = ((Number) mCallback_getAddress.invoke(quitAppCallback,
new Object[] {})).intValue();
if (quitAppProc == 0) {
Debug.out("OSX: Could not find Callback 'quitApp'");
mCallback_dispose.invoke(quitAppCallback, new Object[] {});
} else {
result = ((Number) invoke(claOS, null, "AEInstallEventHandler",
new Object[] {
kCoreEventClass,
kAEQuitApplication,
quitAppProc,
(int) 0,
false
})).intValue();
if (result != noErr) {
Debug.out("OSX: Could not install QuitApplication Event Handler. Error: "
+ result);
}
}
///
int appTarget = ((Number) invoke(claOS, null,
"GetApplicationEventTarget", new Object[] {})).intValue();
Object appleEventCallback = constCallback3.newInstance(this,
"appleEventProc", 3);
int appleEventProc = ((Number) mCallback_getAddress.invoke(
appleEventCallback, new Object[] {})).intValue();
int[] mask3 = new int[] {
kEventClassAppleEvent,
kEventAppleEvent,
kURLEventClass,
kAEReopenApplication,
kAEOpenContents,
};
result = ((Number) invoke(claOS, null, "InstallEventHandler",
new Class[] {
int.class,
int.class,
int.class,
int[].class,
int.class,
int[].class
}, new Object[] {
appTarget,
appleEventProc,
mask3.length / 2,
mask3,
0,
null
})).intValue();
if (result != noErr) {
Debug.out("OSX: Could Install Event Handler. Error: " + result);
return;
}
} catch (Throwable e) {
Debug.out("registerTorrentFile failed", e);
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.IStartup#earlyStartup()
*/
public void earlyStartup() {
final Display display = Display.getDefault();
display.syncExec(new AERunnable() {
public void runSupport() {
hookApplicationMenu(display);
}
});
}
/**
* See Apple Technical Q&A 1079 (http://developer.apple.com/qa/qa2001/qa1079.html)<br />
* Also http://developer.apple.com/documentation/Carbon/Reference/Menu_Manager/menu_mgr_ref/function_group_10.html
*/
public void hookApplicationMenu(final Display display) {
try {
final Object commandCallback = constCallback3.newInstance(
CarbonUIEnhancer.class, "commandProc", 3); //$NON-NLS-1$
int commandProc = ((Number) mCallback_getAddress.invoke(commandCallback,
new Object[] {})).intValue();
if (commandProc == 0) {
mCallback_dispose.invoke(commandCallback, new Object[] {});
return; // give up
}
// Install event handler for commands
int[] mask = new int[] {
kEventClassCommand,
kEventProcessCommand
};
int appTarget = ((Number) invoke(claOS, null,
"GetApplicationEventTarget", new Object[] {})).intValue();
invoke(claOS, null, "InstallEventHandler", new Class[] {
int.class,
int.class,
int.class,
int[].class,
int.class,
int[].class
}, new Object[] {
appTarget,
commandProc,
mask.length / 2,
mask,
0,
null
});
// create About menu command
int[] outMenu = new int[1];
short[] outIndex = new short[1];
// int GetIndMenuItemWithCommandID(int mHandle, int commandId, int index, int[] outMenu, short[] outIndex);
int ind = ((Number) invoke(claOS, null, "GetIndMenuItemWithCommandID",
new Class[] {
int.class,
int.class,
int.class,
int[].class,
short[].class
}, new Object[] {
0,
kHICommandPreferences,
1,
outMenu,
outIndex
})).intValue();
if (ind == noErr && outMenu[0] != 0) {
int menu = outMenu[0];
int l = fgAboutActionName.length();
char buffer[] = new char[l];
fgAboutActionName.getChars(0, l, buffer, 0);
int str = CFStringCreateWithCharacters(kCFAllocatorDefault, buffer, l);
InsertMenuItemTextWithCFString(menu, str, (short) 0, 0, kHICommandAbout);
invoke(claOS, null, "CFRelease", new Object[] {
str
});
// add separator between About & Preferences
InsertMenuItemTextWithCFString(menu, 0, (short) 1,
kMenuItemAttrSeparator, 0);
// enable pref menu
invoke(claOS, null, "EnableMenuCommand", new Object[] {
menu,
kHICommandPreferences
});
// disable services menu
invoke(claOS, null, "DisableMenuCommand", new Object[] {
menu,
kHICommandServices
});
if (!isAZ3) {
// wizard menu
l = fgWizardActionName.length();
buffer = new char[l];
fgWizardActionName.getChars(0, l, buffer, 0);
str = CFStringCreateWithCharacters(kCFAllocatorDefault, buffer, l);
InsertMenuItemTextWithCFString(menu, str, (short) 3, 0,
kHICommandWizard);
invoke(claOS, null, "CFRelease", new Object[] {
str
});
// NAT test menu
l = fgNatTestActionName.length();
buffer = new char[l];
fgNatTestActionName.getChars(0, l, buffer, 0);
str = CFStringCreateWithCharacters(kCFAllocatorDefault, buffer, l);
InsertMenuItemTextWithCFString(menu, str, (short) 4, 0,
kHICommandNatTest);
invoke(claOS, null, "CFRelease", new Object[] {
str
});
//SpeedTest
l = fgSpeedTestActionName.length();
buffer = new char[l];
fgSpeedTestActionName.getChars(0, l, buffer, 0);
str = CFStringCreateWithCharacters(kCFAllocatorDefault, buffer, l);
InsertMenuItemTextWithCFString(menu, str, (short) 5, 0,
kHICommandSpeedTest);
invoke(claOS, null, "CFRelease", new Object[] {
str
});
}
InsertMenuItemTextWithCFString(menu, 0, (short) 6,
kMenuItemAttrSeparator, 0);
// restart menu
l = fgRestartActionName.length();
buffer = new char[l];
fgRestartActionName.getChars(0, l, buffer, 0);
str = CFStringCreateWithCharacters(kCFAllocatorDefault, buffer, l);
InsertMenuItemTextWithCFString(menu, str, (short) 7, 0,
kHICommandRestart);
invoke(claOS, null, "CFRelease", new Object[] {
str
});
InsertMenuItemTextWithCFString(menu, 0, (short) 8,
kMenuItemAttrSeparator, 0);
}
// schedule disposal of callback object
display.disposeExec(new AERunnable() {
public void runSupport() {
try {
mCallback_dispose.invoke(commandCallback, new Object[] {});
} catch (Throwable e) {
}
// stopSidekick();
}
});
} catch (Throwable e) {
Debug.out("Failed hookApplicatioMenu", e);
}
}
private void InsertMenuItemTextWithCFString(int mHandle, int sHandle,
short index, int attributes, int commandID) {
//OS.InsertMenuItemTextWithCFString(mHandle, sHandle, index, attributes, commandID);
invoke(claOS, null, "InsertMenuItemTextWithCFString", new Class[] {
int.class,
int.class,
short.class,
int.class,
int.class
}, new Object[] {
mHandle,
sHandle,
index,
attributes,
commandID
});
}
private int CFStringCreateWithCharacters(int alloc, char[] buffer,
int numChars) {
//return OS.CFStringCreateWithCharacters(alloc, buffer, numChars);
return ((Number) invoke(claOS, null, "CFStringCreateWithCharacters",
new Object[] {
alloc,
buffer,
numChars
})).intValue();
}
int appleEventProc(int nextHandler, int theEvent, int userData) {
try {
int eventClass = ((Number) invoke(claOS, null, "GetEventClass",
new Object[] {
theEvent
})).intValue();
//int eventKind = OS.GetEventKind(theEvent);
//System.out.println("appleEventProc " + OSXtoString(eventClass) + ";"
// + OS.GetEventKind(theEvent) + ";" + OSXtoString(theEvent) + ";"
// + OSXtoString(userData));
// Process the odoc event
if (eventClass == kEventClassAppleEvent) {
int[] aeEventID = new int[1];
// int GetEventParameter(int inEvent, int inName, int inDesiredType,
// int[] outActualType, int inBufferSize, int[] outActualSize, int[] outData);
int ret = ((Number) invoke(claOS, null, "GetEventParameter",
new Class[] {
int.class,
int.class,
int.class,
int[].class,
int.class,
int[].class,
int[].class
}, new Object[] {
theEvent,
kEventParamAEEventID,
typeType,
null,
4,
null,
aeEventID
})).intValue();
if (ret != noErr) {
return eventNotHandledErr;
}
//System.out.println("EventID = " + OSXtoString(aeEventID[0]));
if (aeEventID[0] != kAEOpenDocuments && aeEventID[0] != kURLEventClass
&& aeEventID[0] != kAEReopenApplication
&& aeEventID[0] != kAEOpenContents
&& aeEventID[0] != kAEQuitApplication) {
return eventNotHandledErr;
}
// Handle Event
Object eventRecord = claEventRecord.newInstance();
invoke(claOS, null, "ConvertEventRefToEventRecord", new Class[] {
int.class,
claEventRecord
}, new Object[] {
theEvent,
eventRecord
});
invoke(claOS, null, "AEProcessAppleEvent", new Object[] {
eventRecord
});
// Tell Mac we are handling this event
return noErr;
}
} catch (Throwable e) {
Debug.out(e);
}
return eventNotHandledErr;
}
/*
private static String OSXtoString(int i) {
char[] c = new char[4];
c[0] = (char) ((i >> 24) & 0xff);
c[1] = (char) ((i >> 16) & 0xff);
c[2] = (char) ((i >> 8) & 0xff);
c[3] = (char) (i & 0xff);
return new String(c);
}
*/
private static void memmove(byte[] dest, int src, int size) {
switch (memmove_type) {
case 0:
try {
OSXAccess.memmove(dest, src, size);
memmove_type = 0;
return;
} catch (Throwable e) {
}
// FALL THROUGH
case 1:
try {
Class<?> cMemMove = Class.forName("org.eclipse.swt.internal.carbon.OS");
Method method = cMemMove.getMethod("memmove", new Class[] {
byte[].class,
Integer.TYPE,
Integer.TYPE
});
method.invoke(null, new Object[] {
dest,
new Integer(src),
new Integer(size)
});
memmove_type = 1;
return;
} catch (Throwable e) {
}
// FALL THROUGH
case 2:
try {
Class<?> cMemMove = Class.forName("org.eclipse.swt.internal.carbon.OS");
Method method = cMemMove.getMethod("memcpy", new Class[] {
byte[].class,
Integer.TYPE,
Integer.TYPE
});
method.invoke(null, new Object[] {
dest,
new Integer(src),
new Integer(size)
});
memmove_type = 2;
return;
} catch (Throwable e) {
}
// FALL THROUGH
default:
break;
}
memmove_type = 3;
}
final static int commandProc(int nextHandler, int theEvent, int userData) {
try {
int kind = ((Number) invoke(claOS, null, "GetEventKind", new Object[] {
theEvent
})).intValue();
if (kind == kEventProcessCommand) {
Object command = claHICommand.newInstance();
// int GetEventParameter(int inEvent, int inName, int inDesiredType,
// int[] outActualType, int inBufferSize, int[] outActualSize, HICommand outData);
invoke(claOS, null, "GetEventParameter", new Class[] {
int.class,
int.class,
int.class,
int[].class,
int.class,
int[].class,
claHICommand
}, new Object[] {
theEvent,
kEventParamDirectObject,
typeHICommand,
null,
claHICommand.getField("sizeof").getInt(command),
null,
command
});
int commandID = claHICommand.getField("commandID").getInt(command);
switch (commandID) {
case kHICommandPreferences: {
UIFunctions uiFunctions = UIFunctionsManager.getUIFunctions();
if (uiFunctions != null) {
uiFunctions.openView(UIFunctions.VIEW_CONFIG, null);
}
return noErr;
}
case kHICommandAbout:
AboutWindow.show();
return noErr;
case kHICommandRestart: {
UIFunctions uiFunctions = UIFunctionsManager.getUIFunctions();
if (uiFunctions != null) {
uiFunctions.dispose(true, false);
}
return noErr;
}
case kHICommandWizard:
new ConfigureWizard(false,ConfigureWizard.WIZARD_MODE_FULL);
return noErr;
case kHICommandNatTest:
new NatTestWindow();
return noErr;
case kHICommandSpeedTest:
new SpeedTestWizard();
return noErr;
case kAEQuitApplication:
UIFunctions uiFunctions = UIFunctionsManager.getUIFunctions();
if (uiFunctions != null) {
uiFunctions.dispose(false, false);
return noErr;
} else {
UIExitUtilsSWT.setSkipCloseCheck(true);
}
default:
break;
}
}
} catch (Throwable t) {
Debug.out(t);
}
return eventNotHandledErr;
}
final static int quitAppProc(int theAppleEvent, int reply, int handlerRefcon) {
UIFunctions uiFunctions = UIFunctionsManager.getUIFunctions();
if (uiFunctions != null) {
uiFunctions.dispose(false, false);
} else {
UIExitUtilsSWT.setSkipCloseCheck(true);
Display.getDefault().dispose();
}
return noErr;
}
final static int openDocProc(int theAppleEvent, int reply, int handlerRefcon) {
try {
Object aeDesc = claAEDesc.newInstance();
Object eventRecord = claEventRecord.newInstance();
invoke(claOS, null, "ConvertEventRefToEventRecord", new Class[] {
int.class,
claEventRecord
}, new Object[] {
theAppleEvent,
eventRecord
});
try {
int result = OSXAccess.AEGetParamDesc(theAppleEvent,
kEventParamDirectObject, typeAEList, aeDesc);
if (result != noErr) {
Debug.out("OSX: Could call AEGetParamDesc. Error: " + result);
return noErr;
}
} catch (java.lang.UnsatisfiedLinkError e) {
Debug.out("OSX: AEGetParamDesc not available. Can't open sent file");
return noErr;
}
int[] count = new int[1];
invoke(claOS, null, "AECountItems", new Class[] {
claAEDesc,
int[].class
}, new Object[] {
aeDesc,
count
});
//System.out.println("COUNT: " + count[0]);
if (count[0] > 0) {
final String[] fileNames = new String[count[0]];
int maximumSize = 80; // size of FSRef
int dataPtr = ((Number) invoke(claOS, null, "NewPtr", new Object[] {
maximumSize
})).intValue();
int[] aeKeyword = new int[1];
int[] typeCode = new int[1];
int[] actualSize = new int[1];
for (int i = 0; i < count[0]; i++) {
try {
// int AEGetNthPtr(AEDesc theAEDescList, int index, int desiredType,
// int[] theAEKeyword, int[] typeCode, int dataPtr, int maximumSize, int[] actualSize);
Class<?>[] sigAEGetNthPtr = new Class[] {
claAEDesc,
int.class,
int.class,
int[].class,
int[].class,
int.class,
int.class,
int[].class
};
int ret = ((Number) invoke(claOS, null, "AEGetNthPtr",
sigAEGetNthPtr, new Object[] {
aeDesc,
i + 1,
typeFSRef,
aeKeyword,
typeCode,
dataPtr,
maximumSize,
actualSize
})).intValue();
if (ret == noErr) {
byte[] fsRef = new byte[actualSize[0]];
memmove(fsRef, dataPtr, actualSize[0]);
int dirUrl = ((Number) invoke(claOS, null,
"CFURLCreateFromFSRef", new Object[] {
kCFAllocatorDefault,
fsRef
})).intValue();
int dirString = ((Number) invoke(claOS, null,
"CFURLCopyFileSystemPath", new Object[] {
dirUrl,
kCFURLPOSIXPathStyle
})).intValue();
int length = ((Number) invoke(claOS, null, "CFStringGetLength",
new Object[] {
dirString
})).intValue();
char[] buffer = new char[length];
Object range = claCFRange.newInstance();
claCFRange.getField("length").setInt(range, length);
invoke(claOS, null, "CFStringGetCharacters", new Class[] {
int.class,
claCFRange,
char[].class
}, new Object[] {
dirString,
range,
buffer
});
fileNames[i] = new String(buffer);
invoke(claOS, null, "CFRelease", new Object[] {
dirString
});
invoke(claOS, null, "CFRelease", new Object[] {
dirUrl
});
} else {
ret = ((Number) invoke(claOS, null, "AEGetNthPtr",
sigAEGetNthPtr, new Object[] {
aeDesc,
i + 1,
typeText,
aeKeyword,
typeCode,
dataPtr,
2048,
actualSize
})).intValue();
if (ret == noErr) {
byte[] urlRef = new byte[actualSize[0]];
memmove(urlRef, dataPtr, actualSize[0]);
fileNames[i] = new String(urlRef);
}
}
} catch (Throwable t) {
Debug.out(t);
}
//System.out.println(fileNames[i]);
}
AzureusCoreFactory.addCoreRunningListener(new AzureusCoreRunningListener() {
public void azureusCoreRunning(AzureusCore core) {
TorrentOpener.openTorrents(fileNames);
}
});
}
return noErr;
} catch (Throwable e) {
Debug.out(e);
}
return eventNotHandledErr;
}
final static int clickDockIcon(int nextHandler, int theEvent, int userData) {
UIFunctions uiFunctions = UIFunctionsManager.getUIFunctions();
if (uiFunctions != null) {
uiFunctions.bringToFront();
return noErr;
}
return eventNotHandledErr;
}
final static int openContents(int nextHandler, int theEvent, int userData) {
Debug.out("openDocContents");
return noErr;
}
final static int toolbarToggle(int nextHandler, int theEvent, int userData) {
int eventKind = ((Number) invoke(claOS, null, "GetEventKind", new Object[] {
theEvent
})).intValue();
if (eventKind != kEventWindowToolbarSwitchMode) {
return eventNotHandledErr;
}
int[] theWindow = new int[1];
//int GetEventParameter(int inEvent, int inName, int inDesiredType,
// int[] outActualType, int inBufferSize, int[] outActualSize, int[] outData);
invoke(claOS, null, "GetEventParameter", new Class[] {
int.class,
int.class,
int.class,
int[].class,
int.class,
int[].class,
int[].class
}, new Object[] {
theEvent,
kEventParamDirectObject,
typeWindowRef,
null,
4,
null,
theWindow
});
int[] theRoot = new int[1];
invoke(claOS, null, "GetRootControl", new Object[] {
theWindow[0],
theRoot
});
final Widget widget = Display.getCurrent().findWidget(theRoot[0]);
if (!(widget instanceof Shell)) {
return eventNotHandledErr;
}
final Shell shellAffected = (Shell) widget;
Utils.execSWTThread(new AERunnable() {
public void runSupport() {
int type;
Long l = (Long) shellAffected.getData("OSX.ToolBarToggle");
if (l == null || l.longValue() == 0) {
type = SWT.Collapse;
} else {
type = SWT.Expand;
}
Event event = new Event();
event.type = type;
event.display = widget.getDisplay();
event.widget = widget;
shellAffected.notifyListeners(type, event);
shellAffected.setData("OSX.ToolBarToggle", new Long(
type == SWT.Collapse ? 1 : 0));
}
});
return noErr;
}
private static Object invoke(Class<?> clazz, Object target,
String methodName, Object[] args) {
try {
Class<?>[] signature = new Class<?>[args.length];
for (int i = 0; i < args.length; i++) {
Class<?> thisClass = args[i].getClass();
if (thisClass == Integer.class)
signature[i] = int.class;
else if (thisClass == Long.class)
signature[i] = long.class;
else if (thisClass == Byte.class)
signature[i] = byte.class;
else if (thisClass == Boolean.class)
signature[i] = boolean.class;
else
signature[i] = thisClass;
}
Method method = clazz.getMethod(methodName, signature);
return method.invoke(target, args);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
private static Object invoke(Class<?> clazz, Object target,
String methodName, Class<?>[] signature, Object[] args) {
try {
Method method = clazz.getDeclaredMethod(methodName, signature);
method.setAccessible(true);
return method.invoke(target, args);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
}