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