1 /*
2  * Copyright (C) Azureus Software, Inc, All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details ( see the LICENSE file ).
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18 
19 package org.gudy.azureus2.ui.swt.osx;
20 
21 import java.lang.reflect.*;
22 
23 import org.eclipse.swt.SWT;
24 import org.eclipse.swt.events.SelectionAdapter;
25 import org.eclipse.swt.events.SelectionEvent;
26 import org.eclipse.swt.graphics.Device;
27 import org.eclipse.swt.graphics.Image;
28 import org.eclipse.swt.internal.C;
29 import org.eclipse.swt.widgets.*;
30 import org.gudy.azureus2.core3.config.COConfigurationManager;
31 import org.gudy.azureus2.core3.util.AERunnable;
32 import org.gudy.azureus2.core3.util.Debug;
33 import org.gudy.azureus2.ui.swt.Messages;
34 import org.gudy.azureus2.ui.swt.Utils;
35 import org.gudy.azureus2.ui.swt.config.wizard.ConfigureWizard;
36 import org.gudy.azureus2.ui.swt.help.AboutWindow;
37 import org.gudy.azureus2.ui.swt.mainwindow.PluginsMenuHelper;
38 import org.gudy.azureus2.ui.swt.nat.NatTestWindow;
39 import org.gudy.azureus2.ui.swt.speedtest.SpeedTestWizard;
40 
41 import com.aelitis.azureus.ui.UIFunctions;
42 import com.aelitis.azureus.ui.UIFunctionsManager;
43 import com.aelitis.azureus.ui.mdi.MultipleDocumentInterface;
44 import com.aelitis.azureus.ui.swt.UIFunctionsManagerSWT;
45 import com.aelitis.azureus.ui.swt.UIFunctionsSWT;
46 
47 /**
48  * You can exclude this file (or this whole path) for non OSX builds
49  *
50  * Hook some Cocoa specific abilities:
51  * - App->About        <BR>
52  * - App->Preferences  <BR>
53  * - App->Exit         <BR>
54  * <BR>
55  * - OpenDocument  (possible limited to only files?) <BR>
56  *
57  * This code was influenced by the
58  * <a href="http://www.transparentech.com/opensource/cocoauienhancer">
59  * CocoaUIEnhancer</a>, which was influenced by the
60  * <a href="http://www.simidude.com/blog/2008/macify-a-swt-application-in-a-cross-platform-way/">
61  * CarbonUIEnhancer from Agynami</a>.
62  *
63  * Both cocoa implementations are modified from
64  * <a href="http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.ui.cocoa/src/org/eclipse/ui/internal/cocoa/CocoaUIEnhancer.java">
65  * org.eclipse.ui.internal.cocoa.CocoaUIEnhancer</a>
66  */
67 public class CocoaUIEnhancer
68 {
69 	private static final boolean DEBUG = false;
70 
71 	private static Object /*Callback*/ callBack3;
72 
73 	private static long callBack3Addr;
74 
75 	private static Object /*Callback*/ callBack4;
76 
77 	private static long callBack4Addr;
78 
79 	private static CocoaUIEnhancer instance;
80 
81 
82 	private static final int kServicesMenuItem = 4;
83 
84 	// private static final int kHideApplicationMenuItem = 6;
85 
86 	// private static final int kQuitMenuItem = 10;
87 
88 	//private static int NSWindowCloseButton = 0;
89 
90 	//private static int NSWindowDocumentIconButton = 4;
91 
92 	//private static int NSWindowMiniaturizeButton = 1;
93 
94 	private static int NSWindowToolbarButton = 3;
95 
96 	//private static int NSWindowZoomButton = 2;
97 
98 	private static long sel_application_openFile_;
99 
100 	private static long sel_application_openFiles_;
101 
102 	/** SWT v4331b supports this already */
103 	private static long sel_applicationShouldHandleReopen_;
104 
105 	private static long sel_toolbarButtonClicked_;
106 
107 	private static boolean alreadyHaveOpenDoc;
108 
109 	static final byte[] SWT_OBJECT = {
110 		'S',
111 		'W',
112 		'T',
113 		'_',
114 		'O',
115 		'B',
116 		'J',
117 		'E',
118 		'C',
119 		'T',
120 		'\0'
121 	};
122 
123 	private long delegateIdSWTApplication;
124 
125 	private long delegateJniRef;
126 
127 	private Object delegate;
128 
129 	private static boolean initialized = false;
130 
131 	private static Class<?> osCls = classForName("org.eclipse.swt.internal.cocoa.OS");
132 	private static Class<?> nsmenuCls = classForName("org.eclipse.swt.internal.cocoa.NSMenu");
133 	private static Class<?> nsmenuitemCls = classForName("org.eclipse.swt.internal.cocoa.NSMenuItem");
134 	private static Class<?> nsapplicationCls = classForName("org.eclipse.swt.internal.cocoa.NSApplication");
135 	private static Class<?> nsarrayCls = classForName("org.eclipse.swt.internal.cocoa.NSArray");
136 	private static Class<?> nsstringCls = classForName("org.eclipse.swt.internal.cocoa.NSString");
137 	private static Class<?> nsidCls = classForName("org.eclipse.swt.internal.cocoa.id");
138 	private static Class<?> nsautoreleasepoolCls = classForName("org.eclipse.swt.internal.cocoa.NSAutoreleasePool");
139 	private static Class<?> nsworkspaceCls = classForName("org.eclipse.swt.internal.cocoa.NSWorkspace");
140 	private static Class<?> nsimageCls = classForName("org.eclipse.swt.internal.cocoa.NSImage");
141 	private static Class<?> nssizeCls = classForName("org.eclipse.swt.internal.cocoa.NSSize");
142 	private static Class<?> nsscreenCls = classForName("org.eclipse.swt.internal.cocoa.NSScreen");
143 
144 	static {
145 
146 		Class<CocoaUIEnhancer> clazz = CocoaUIEnhancer.class;
147 		Class<?> callbackCls = classForName("org.eclipse.swt.internal.Callback");
148 
149 		try {
150 			SWT.class.getDeclaredField("OpenDocument");
151 			alreadyHaveOpenDoc = true;
152 		} catch (Throwable t) {
153 			alreadyHaveOpenDoc = false;
154 		}
155 
156 		try {
157 			Method mGetAddress = callbackCls.getMethod("getAddress", new Class[0]);
158 			Constructor<?> consCallback = callbackCls.getConstructor(new Class<?>[] {
159 				Object.class,
160 				String.class,
161 				int.class
162 			});
163 			//callBack3 = new Callback(clazz, "actionProc", 3);
164 			callBack3 = consCallback.newInstance(new Object[] {
165 				clazz,
166 				"actionProc",
167 				3
168 			});
169 			Object object = mGetAddress.invoke(callBack3, (Object[]) null);
170 			callBack3Addr = convertToLong(object);
171 			if (callBack3Addr == 0) {
172 				SWT.error(SWT.ERROR_NO_MORE_CALLBACKS);
173 			}
174 
175 			//callBack4 = new Callback(clazz, "actionProc", 4);
176 			callBack4 = consCallback.newInstance(new Object[] {
177 				clazz,
178 				"actionProc",
179 				4
180 			});
181 			object = mGetAddress.invoke(callBack4, (Object[]) null);
182 			callBack4Addr = convertToLong(object);
183 			if (callBack4Addr == 0) {
184 				SWT.error(SWT.ERROR_NO_MORE_CALLBACKS);
185 			}
186 		} catch (Throwable e) {
187 			Debug.out(e);
188 		}
189 	}
190 
actionProc(int id, int sel, int arg0)191 	static int /*long*/actionProc(int /*long*/id, int /*long*/sel,
192 			int /*long*/arg0) {
193 		return (int)actionProc((long)id, (long)sel, (long)arg0);
194 	}
195 
actionProc(long id, long sel, long arg0)196 	static long actionProc(long id, long sel,
197 			long arg0) {
198 		if (DEBUG) {
199 			System.err.println("id=" + id + ";sel=" + sel);
200 		}
201 
202 		if (sel == sel_toolbarButtonClicked_) {
203 			try {
204 				Field fldsel_window = osCls.getField("sel_window");
205 				Object windowId = invoke(osCls, "objc_msgSend", new Object[] {
206 					wrapPointer(arg0),
207 					fldsel_window.get(null)
208 				});
209 				final Shell shellAffected = (Shell) invoke(Display.class,
210 						Display.getCurrent(), "findWidget", new Object[] {
211 							windowId
212 						});
213 
214 				Utils.execSWTThread(new AERunnable() {
215 					public void runSupport() {
216 						int type;
217 						Long l = (Long) shellAffected.getData("OSX.ToolBarToggle");
218 						if (l == null || l.longValue() == 0) {
219 							type = SWT.Collapse;
220 						} else {
221 							type = SWT.Expand;
222 						}
223 
224 						Event event = new Event();
225 						event.type = type;
226 						event.display = shellAffected.getDisplay();
227 						event.widget = shellAffected;
228 						shellAffected.notifyListeners(type, event);
229 
230 						shellAffected.setData("OSX.ToolBarToggle", new Long(
231 								type == SWT.Collapse ? 1 : 0));
232 					}
233 				});
234 			} catch (Throwable t) {
235 				Debug.out(t);
236 			}
237 
238 		}
239 		return 0;
240 	}
241 
actionProc(int id, int sel, int arg0, int arg1)242 	static int /*long*/actionProc(int /*long*/id, int /*long*/sel,
243 			int /*long*/arg0, int /*long*/arg1)
244 			throws Throwable {
245 		return (int)actionProc((long)id, (long)sel, (long)arg0, (long)arg1);
246 	}
247 
248 
actionProc(long id, long sel, long arg0, long arg1)249 	static long actionProc(long id, long sel,
250 			long arg0, long arg1)
251 			throws Throwable {
252 		if (DEBUG) {
253 			System.err.println("actionProc 4 " + id + "/" + sel);
254 		}
255 		Display display = Display.getCurrent();
256 		if (display == null)
257 			return 0;
258 
259 		if (!alreadyHaveOpenDoc && sel == sel_application_openFile_) {
260 			Constructor<?> conNSString = nsstringCls.getConstructor(new Class[] {
261 				int.class
262 			});
263 			Object file = conNSString.newInstance(arg1);
264 			String fileString = (String) invoke(file, "getString");
265 			if (DEBUG) {
266 				System.err.println("OMG GOT OpenFile " + fileString);
267 			}
268 			OSXFileOpen.fileOpen(fileString);
269 		} else if (!alreadyHaveOpenDoc && sel == sel_application_openFiles_) {
270 			Constructor<?> conNSArray = nsarrayCls.getConstructor(new Class[] {
271 				int.class
272 			});
273 			Constructor<?> conNSString = nsstringCls.getConstructor(new Class[] {
274 				nsidCls
275 			});
276 
277 			Object arrayOfFiles = conNSArray.newInstance(arg1);
278 			int count = ((Number) invoke(arrayOfFiles, "count")).intValue();
279 
280 			String[] files = new String[count];
281 			for (int i = 0; i < count; i++) {
282 				Object fieldId = invoke(nsarrayCls, arrayOfFiles, "objectAtIndex",
283 						new Object[] {
284 							i
285 						});
286 				Object nsstring = conNSString.newInstance(fieldId);
287 				files[i] = (String) invoke(nsstring, "getString");
288 
289 				if (DEBUG) {
290 					System.err.println("OMG GOT OpenFiles " + files[i]);
291 				}
292 			}
293 			OSXFileOpen.fileOpen(files);
294 		} else if (sel == sel_applicationShouldHandleReopen_) {
295 			Event event = new Event ();
296 			event.detail = 1;
297 			if (display != null) {
298 				invoke(Display.class, display, "sendEvent", new Class[] {
299 					int.class,
300 					Event.class
301 				}, new Object[] {
302 					SWT.Activate,
303 					event
304 				});
305 			}
306 		}
307 		return 0;
308 	}
309 
classForName(String classname)310 	private static Class<?> classForName(String classname) {
311 		try {
312 			Class<?> cls = Class.forName(classname);
313 			return cls;
314 		} catch (ClassNotFoundException e) {
315 			throw new IllegalStateException(e);
316 		}
317 	}
318 
convertToLong(Object object)319 	private static long convertToLong(Object object) {
320 		if (object instanceof Integer) {
321 			Integer i = (Integer) object;
322 			return i.longValue();
323 		}
324 		if (object instanceof Long) {
325 			Long l = (Long) object;
326 			return l.longValue();
327 		}
328 		return 0;
329 	}
330 
getInstance()331 	public static CocoaUIEnhancer getInstance() {
332 		if (instance == null) {
333 			try {
334 				instance = new CocoaUIEnhancer();
335 			} catch (Throwable e) {
336 				Debug.out(e);
337 			}
338 		}
339 		return instance;
340 	}
341 
invoke(Class<?> clazz, Object target, String methodName, Object[] args)342 	private static Object invoke(Class<?> clazz, Object target,
343 			String methodName, Object[] args) {
344 		try {
345 			Class<?>[] signature = new Class<?>[args.length];
346 			for (int i = 0; i < args.length; i++) {
347 				Class<?> thisClass = args[i].getClass();
348 				if (thisClass == Integer.class)
349 					signature[i] = int.class;
350 				else if (thisClass == Long.class)
351 					signature[i] = long.class;
352 				else if (thisClass == Byte.class)
353 					signature[i] = byte.class;
354 				else if (thisClass == Boolean.class)
355 					signature[i] = boolean.class;
356 				else
357 					signature[i] = thisClass;
358 			}
359 			Method method = clazz.getMethod(methodName, signature);
360 			return method.invoke(target, args);
361 		} catch (Exception e) {
362 			throw new IllegalStateException(e);
363 		}
364 	}
365 
invoke(Class<?> clazz, Object target, String methodName, Class[] signature, Object[] args)366 	private static Object invoke(Class<?> clazz, Object target,
367 			String methodName, Class[] signature, Object[] args) {
368 		try {
369 			Method method = clazz.getDeclaredMethod(methodName, signature);
370 			method.setAccessible(true);
371 			return method.invoke(target, args);
372 		} catch (Exception e) {
373 			throw new IllegalStateException(e);
374 		}
375 	}
376 
invoke(Class<?> clazz, String methodName, Object[] args)377 	private static Object invoke(Class<?> clazz, String methodName, Object[] args) {
378 		return invoke(clazz, null, methodName, args);
379 	}
380 
invoke(Object obj, String methodName)381 	private static Object invoke(Object obj, String methodName) {
382 		return invoke(obj, methodName, (Class<?>[]) null, (Object[]) null);
383 	}
384 
invoke(Object obj, String methodName, Class<?>[] paramTypes, Object... arguments)385 	private static Object invoke(Object obj, String methodName,
386 			Class<?>[] paramTypes, Object... arguments) {
387 		try {
388 			Method m = obj.getClass().getMethod(methodName, paramTypes);
389 			return m.invoke(obj, arguments);
390 		} catch (Exception e) {
391 			throw new IllegalStateException(e);
392 		}
393 	}
394 
registerName(Class<?> osCls, String name)395 	private static long registerName(Class<?> osCls, String name)
396 			throws IllegalArgumentException, SecurityException,
397 			IllegalAccessException, InvocationTargetException, NoSuchMethodException {
398 		Object object = invoke(osCls, "sel_registerName", new Object[] {
399 			name
400 		});
401 		return convertToLong(object);
402 	}
403 
404 	////////////////////////////////////////////////////////////
405 
wrapPointer(long value)406 	private static Object wrapPointer(long value) {
407 		Class<?> PTR_CLASS = C.PTR_SIZEOF == 8 ? long.class : int.class;
408 		if (PTR_CLASS == long.class)
409 			return new Long(value);
410 		else
411 			return new Integer((int) value);
412 	}
413 
CocoaUIEnhancer()414 	private CocoaUIEnhancer()
415 			throws Throwable {
416 
417 		// Instead of creating a new delegate class in objective-c,
418 		// just use the current SWTApplicationDelegate. An instance of this
419 		// is a field of the Cocoa Display object and is already the target
420 		// for the menuItems. So just get this class and add the new methods
421 		// to it.
422 		Object delegateObjSWTApplication = invoke(osCls, "objc_lookUpClass",
423 				new Object[] {
424 					"SWTApplicationDelegate"
425 				});
426 		delegateIdSWTApplication = convertToLong(delegateObjSWTApplication);
427 
428 		// This doesn't feel right, but it works
429 		Class<?> swtapplicationdelegateCls = classForName("org.eclipse.swt.internal.cocoa.SWTApplicationDelegate");
430 		delegate = swtapplicationdelegateCls.newInstance();
431 		Object delegateAlloc = invoke(delegate, "alloc");
432 		invoke(delegateAlloc, "init");
433 		Object delegateIdObj = nsidCls.getField("id").get(delegate);
434 		delegateJniRef = ((Number) invoke(osCls, "NewGlobalRef", new Class<?>[] {
435 			Object.class
436 		}, new Object[] {
437 			CocoaUIEnhancer.this
438 		})).longValue();
439 		if (delegateJniRef == 0)
440 			SWT.error(SWT.ERROR_NO_HANDLES);
441 		//OS.object_setInstanceVariable(delegate.id, SWT_OBJECT, delegateJniRef);
442 		invoke(osCls, "object_setInstanceVariable", new Object[] {
443 			delegateIdObj,
444 			SWT_OBJECT,
445 			wrapPointer(delegateJniRef)
446 		});
447 	}
448 
449 	/**
450 	 * Hook the given Listener to the Mac OS X application Quit menu and the IActions to the About
451 	 * and Preferences menus.
452 	 *
453 	 */
hookApplicationMenu()454 	public void hookApplicationMenu() {
455 		Display display = Display.getCurrent();
456 		try {
457 			// Initialize the menuItems.
458 			initialize();
459 		} catch (Exception e) {
460 			throw new IllegalStateException(e);
461 		}
462 
463 		// Schedule disposal of callback object
464 		display.disposeExec(new Runnable() {
465 			public void run() {
466 				invoke(callBack3, "dispose");
467 				callBack3 = null;
468 				invoke(callBack4, "dispose");
469 				callBack4 = null;
470 
471 				if (delegateJniRef != 0) {
472 					//OS.DeleteGlobalRef(delegateJniRef);
473 					invoke(osCls, "DeleteGlobalRef", new Object[] {
474 						wrapPointer(delegateJniRef)
475 					});
476 					delegateJniRef = 0;
477 				}
478 
479 				if (delegate != null) {
480 					invoke(delegate, "release");
481 					delegate = null;
482 				}
483 			}
484 		});
485 	}
486 
hookDocumentOpen()487 	public void hookDocumentOpen()
488 			throws Throwable {
489 
490 		if (alreadyHaveOpenDoc) {
491 			return;
492 		}
493 
494 		if (sel_application_openFile_ == 0) {
495 			sel_application_openFile_ = registerName(osCls, "application:openFile:");
496 		}
497 		invoke(osCls, "class_addMethod", new Object[] {
498 			wrapPointer(delegateIdSWTApplication),
499 			wrapPointer(sel_application_openFile_),
500 			wrapPointer(callBack4Addr),
501 			"@:@:@"
502 		});
503 
504 		if (sel_application_openFiles_ == 0) {
505 			sel_application_openFiles_ = registerName(osCls, "application:openFiles:");
506 		}
507 		invoke(osCls, "class_addMethod", new Object[] {
508 			wrapPointer(delegateIdSWTApplication),
509 			wrapPointer(sel_application_openFiles_),
510 			wrapPointer(callBack4Addr),
511 			"@:@:@"
512 		});
513 	}
514 
getItem(Menu menu, int id)515 	static MenuItem getItem(Menu menu, int id) {
516 		MenuItem[] items = menu.getItems();
517 		for (int i = 0; i < items.length; i++) {
518 			if (items[i].getID() == id) return items[i];
519 		}
520 		return null;
521 	}
522 
initialize()523 	private void initialize()
524 			throws Exception {
525 
526 		// Get the Mac OS X Application menu.
527 		Object sharedApplication = invoke(nsapplicationCls, "sharedApplication");
528 		Object mainMenu = invoke(sharedApplication, "mainMenu");
529 		Object mainMenuItem = invoke(nsmenuCls, mainMenu, "itemAtIndex",
530 				new Object[] {
531 					wrapPointer(0)
532 				});
533 		Object appMenu = invoke(mainMenuItem, "submenu");
534 
535 
536 		// disable services menu
537 		Object servicesMenuItem = invoke(nsmenuCls, appMenu, "itemAtIndex",
538 				new Object[] {
539 					wrapPointer(kServicesMenuItem)
540 				});
541 		invoke(nsmenuitemCls, servicesMenuItem, "setEnabled", new Object[] {
542 			false
543 		});
544 
545 
546 		Menu systemMenu = Display.getCurrent().getSystemMenu();
547 		if (systemMenu != null) {
548 
549 			MenuItem sysItem = getItem(systemMenu, SWT.ID_ABOUT);
550 			if (sysItem != null) {
551 				sysItem.addSelectionListener(new SelectionAdapter() {
552 					public void widgetSelected(SelectionEvent e) {
553 						AboutWindow.show();
554 					};
555 				});
556 			}
557 
558 			sysItem = getItem(systemMenu, SWT.ID_PREFERENCES);
559 			if (sysItem != null) {
560 				sysItem.addSelectionListener(new SelectionAdapter() {
561 					public void widgetSelected(SelectionEvent e) {
562 						UIFunctions uiFunctions = UIFunctionsManager.getUIFunctions();
563 						if (uiFunctions != null) {
564 							uiFunctions.getMDI().showEntryByID(
565 									MultipleDocumentInterface.SIDEBAR_SECTION_CONFIG);
566 						}
567 					};
568 				});
569 			}
570 
571 			int quitIndex = systemMenu.indexOf(getItem(systemMenu, SWT.ID_QUIT));
572 			MenuItem restartItem = new MenuItem(systemMenu, SWT.CASCADE, quitIndex);
573 			Messages.setLanguageText(restartItem, "MainWindow.menu.file.restart");
574 			restartItem.addSelectionListener(new SelectionAdapter() {
575 				public void widgetSelected(SelectionEvent e) {
576 					UIFunctions uiFunctions = UIFunctionsManager.getUIFunctions();
577 					if (uiFunctions != null) {
578 						uiFunctions.dispose(true, false);
579 					}
580 				}
581 			});
582 
583 			// Add other menus
584 			boolean isAZ3 = "az3".equalsIgnoreCase(COConfigurationManager.getStringParameter("ui"));
585 
586 			if (!isAZ3) {
587 				// add Wizard, NAT Test, Speed Test
588 
589 				int prefIndex = systemMenu.indexOf(getItem(systemMenu,
590 						SWT.ID_PREFERENCES)) + 1;
591 				MenuItem wizItem = new MenuItem(systemMenu, SWT.CASCADE, prefIndex);
592 				Messages.setLanguageText(wizItem, "MainWindow.menu.file.configure");
593 				wizItem.addSelectionListener(new SelectionAdapter() {
594 					public void widgetSelected(SelectionEvent e) {
595 						new ConfigureWizard(false, ConfigureWizard.WIZARD_MODE_FULL);
596 					}
597 				});
598 
599 				MenuItem natMenu = new MenuItem(systemMenu, SWT.CASCADE, prefIndex);
600 				Messages.setLanguageText(natMenu, "MainWindow.menu.tools.nattest");
601 				natMenu.addSelectionListener(new SelectionAdapter() {
602 					public void widgetSelected(SelectionEvent e) {
603 						new NatTestWindow();
604 					}
605 				});
606 
607 				MenuItem netstatMenu = new MenuItem(systemMenu, SWT.CASCADE, prefIndex);
608 				Messages.setLanguageText(netstatMenu, "MainWindow.menu.tools.netstat");
609 				netstatMenu.addSelectionListener(new SelectionAdapter() {
610 					public void widgetSelected(SelectionEvent e) {
611 						UIFunctionsSWT uiFunctions = UIFunctionsManagerSWT.getUIFunctionsSWT();
612 						if (uiFunctions != null) {
613 
614 							PluginsMenuHelper.IViewInfo[] views = PluginsMenuHelper.getInstance().getPluginViewsInfo();
615 
616 							for ( PluginsMenuHelper.IViewInfo view: views ){
617 
618 								String viewID = view.viewID;
619 
620 								if ( viewID != null && viewID.equals( "aznetstatus" )){
621 
622 									view.openView( uiFunctions );
623 								}
624 							}
625 						}
626 					}
627 				});
628 
629 				MenuItem speedMenu = new MenuItem(systemMenu, SWT.CASCADE, prefIndex);
630 				Messages.setLanguageText(speedMenu, "MainWindow.menu.tools.speedtest");
631 				speedMenu.addSelectionListener(new SelectionAdapter() {
632 					public void widgetSelected(SelectionEvent e) {
633 						new SpeedTestWizard();
634 					}
635 				});
636 
637 			}
638 		}
639 
640 		// Register names in objective-c.
641 		if (sel_applicationShouldHandleReopen_ == 0) {
642 			sel_applicationShouldHandleReopen_ = registerName(osCls, "applicationShouldHandleReopen:hasVisibleWindows:");
643 		}
644 
645 		// Add the action callbacks for menu items.
646 		invoke(osCls, "class_addMethod", new Object[] {
647 			wrapPointer(delegateIdSWTApplication),
648 			wrapPointer(sel_applicationShouldHandleReopen_),
649 			wrapPointer(callBack4Addr),
650 			"@:@c"
651 		});
652 
653 		initialized = true;
654 	}
655 
656 
invoke(Class<?> cls, String methodName)657 	private Object invoke(Class<?> cls, String methodName) {
658 		return invoke(cls, methodName, (Class<?>[]) null, (Object[]) null);
659 	}
660 
invoke(Class<?> cls, String methodName, Class<?>[] paramTypes, Object... arguments)661 	private Object invoke(Class<?> cls, String methodName, Class<?>[] paramTypes,
662 			Object... arguments) {
663 		try {
664 			Method m = cls.getMethod(methodName, paramTypes);
665 			return m.invoke(null, arguments);
666 		} catch (Exception e) {
667 			throw new IllegalStateException(e);
668 		}
669 	}
670 
registerToolbarToggle(Shell shell)671 	public void registerToolbarToggle(Shell shell)
672 			throws Throwable {
673 
674 		if (sel_toolbarButtonClicked_ == 0) {
675 			sel_toolbarButtonClicked_ = registerName(osCls, "toolbarButtonClicked:");
676 		}
677 
678 		invoke(osCls, "class_addMethod", new Object[] {
679 			wrapPointer(delegateIdSWTApplication),
680 			wrapPointer(sel_toolbarButtonClicked_),
681 			wrapPointer(callBack3Addr),
682 			"@:@"
683 		});
684 
685 		Class<?> nstoolbarCls = classForName("org.eclipse.swt.internal.cocoa.NSToolbar");
686 		Class<?> nsbuttonCls = classForName("org.eclipse.swt.internal.cocoa.NSButton");
687 
688 		//NSToolbar dummyBar = new NSToolbar();
689 		Object dummyBar = nstoolbarCls.newInstance();
690 		//dummyBar.alloc();
691 		invoke(dummyBar, "alloc");
692 		//dummyBar.initWithIdentifier(NSString.stringWith("SWTToolbar"));
693 		Object nsStrDummyToolbar = invoke(nsstringCls, "stringWith", new Object[] {
694 			"SWTToolbar"
695 		});
696 		invoke(dummyBar, "initWithIdentifier", new Class<?>[] {
697 			nsstringCls
698 		}, new Object[] {
699 			nsStrDummyToolbar
700 		});
701 		//dummyBar.setVisible(false);
702 		invoke(dummyBar, "setVisible", new Class<?>[] {
703 			boolean.class
704 		}, new Object[] {
705 			Boolean.FALSE
706 		});
707 
708 		// reflect me
709 		//NSWindow nsWindow = shell.view.window();
710 		Object view = shell.getClass().getField("view").get(shell);
711 		Object nsWindow = invoke(view, "window");
712 		//nsWindow.setToolbar(dummyBar);
713 		invoke(nsWindow, "setToolbar", new Class<?>[] {
714 			nstoolbarCls
715 		}, new Object[] {
716 			dummyBar
717 		});
718 		//nsWindow.setShowsToolbarButton(true);
719 		invoke(nsWindow, "setShowsToolbarButton", new Class<?>[] {
720 			boolean.class
721 		}, new Object[] {
722 			Boolean.TRUE
723 		});
724 
725 		//NSButton toolbarButton = nsWindow.standardWindowButton(NSWindowToolbarButton);
726 		Object toolbarButton = invoke(nsWindow, "standardWindowButton",
727 				new Class<?>[] {
728 					int.class
729 				}, new Object[] {
730 					new Integer(NSWindowToolbarButton)
731 				});
732 
733 		//toolbarButton.setTarget(delegate);
734 		invoke(toolbarButton, "setTarget", new Class[] {
735 			nsidCls
736 		}, new Object[] {
737 			delegate
738 		});
739 
740 		//OS.objc_msgSend(this.id, OS.sel_setTarget_, anObject != null ? anObject.id : 0);
741 		//invoke(osCls, "objc_msgSend", new Object[] {
742 		//	toolbarButton.getClass().getField("id").get(toolbarButton),
743 		//	osCls.getField("sel_setTarget_").get(null),
744 		//	wrapPointer(delegateIdSWTApplication)
745 		//});
746 
747 		//toolbarButton.setAction((int) sel_toolbarButtonClicked_);
748 		invoke(nsbuttonCls, toolbarButton, "setAction", new Object[] {
749 			wrapPointer(sel_toolbarButtonClicked_)
750 		});
751 	}
752 
753 	// from Program.getImageData, except returns bigger images
getFileIcon(String path, int imageWidthHeight)754 	public static Image getFileIcon (String path, int imageWidthHeight) {
755 		Object pool = null;
756 		try {
757 			//NSAutoreleasePool pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
758 			pool = nsautoreleasepoolCls.newInstance();
759 			Object delegateAlloc = invoke(pool, "alloc");
760 			invoke(delegateAlloc, "init");
761 
762 			//NSWorkspace workspace = NSWorkspace.sharedWorkspace();
763 			Object workspace = invoke(nsworkspaceCls, "sharedWorkspace", new Object[] {});
764 			//NSString fullPath = NSString.stringWith(path);
765 			Object fullPath = invoke(nsstringCls, "stringWith", new Object[] {
766 				path
767 			});
768 			if (fullPath != null) {
769 				// SWT also had a :
770 				// fullPath = workspace.fullPathForApplication(NSString.stringWith(name));
771 				// which might be handy someday, but for now, full path works
772 
773 				//NSImage nsImage = workspace.iconForFile(fullPath);
774 				Object nsImage = invoke(workspace, "iconForFile", new Class[] {
775 					nsstringCls
776 				}, new Object[] {
777 					fullPath
778 				});
779 				if (nsImage != null) {
780 					//NSSize size = new NSSize();
781 					Object size = nssizeCls.newInstance();
782 					//size.width = size.height = imageWidthHeight;
783 					nssizeCls.getField("width").set(size, imageWidthHeight);
784 					nssizeCls.getField("height").set(size, imageWidthHeight);
785 					//nsImage.setSize(size);
786 					invoke(nsImage, "setSize", new Class[] {
787 						nssizeCls
788 					}, new Object[] {
789 						size
790 					});
791 					//nsImage.retain();
792 					invoke(nsImage, "retain");
793 					//Image image = Image.cocoa_new(Display.getCurrent(), SWT.BITMAP, nsImage);
794 					Image image = (Image) invoke(Image.class, null, "cocoa_new",
795 							new Class[] {
796 								Device.class,
797 								int.class,
798 								nsimageCls
799 							}, new Object[] {
800 								Display.getCurrent(),
801 								SWT.BITMAP,
802 								nsImage
803 							});
804 				return image;
805 				}
806 			}
807 		} catch (Throwable t) {
808 			Debug.printStackTrace(t);
809 		} finally {
810 			if (pool != null) {
811 				invoke(pool, "release");
812 			}
813 		}
814 		return null;
815 	}
816 
817 
isInitialized()818 	public static boolean isInitialized() {
819 		return initialized;
820 	}
821 
isRetinaDisplay()822 	public boolean isRetinaDisplay() {
823 		try {
824 			//NSArray screens = NSScreen.screens();
825 			Object screens = invoke(nsscreenCls, "screens");
826 			//int count = (int) /*64*/screens.count();
827 			Object oCount = invoke(screens, "count");
828 			if (!(oCount instanceof Number)) {
829 				System.err.println("Can't determine Retina: count is " + oCount);
830 			}
831 			int count = ((Number) oCount).intValue();
832 			for (int i = 0; i < count; i++) {
833 				//NSScreen screen = new NSScreen(screens.objectAtIndex(i));
834 				Object screenID = invoke(screens, "objectAtIndex", new Class[] {
835 					C.PTR_SIZEOF == 8 ? long.class : int.class
836 				}, i);
837 
838 				Object screen = nsscreenCls.getConstructor(screenID.getClass()).newInstance(screenID);
839 				//			if (screen.backingScaleFactor() == 2) {
840 				Object oBackingScaleFactor = invoke(screen, "backingScaleFactor");
841 				//System.err.println("Retina #" + i + " = " + oBackingScaleFactor);
842 				if ((oBackingScaleFactor instanceof Number)
843 						&& ((Number) oBackingScaleFactor).intValue() == 2) {
844 					return true;
845 				}
846 			}
847 		} catch (Throwable t) {
848 			Debug.out("Can't determine Retina", t);
849 		}
850 		return false;
851 	}
852 
853 }
854