/******************************************************************************* * Copyright (c) 2012 Google, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Google, Inc. - initial API and implementation *******************************************************************************/ package com.windowtester.swt.macosx.cocoa; import java.util.ArrayList; import java.util.List; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.internal.cocoa.OS; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Widget; import com.windowtester.swt.runtime.internal.macosx.ReflectionUtil; import com.windowtester.swt.runtime.internal.macosx.cocoa.MacCocoa; /** * Native methods for Cocoa, 64-bit. * TODO Addresses are still represented in 32 bits in many native functions. * The native code needs to be changed to support 64 bit addresses via macros. * Need to verify that the native library is being compiled for both 32 and 64 bit architectures. */ public class MacCocoa64 extends MacCocoa { static { System.loadLibrary("wt-cocoa"); fixMenuBarFontSize(); } /** * Return the menu bar height. */ public int getMenuBarHeight() { return _getMenuBarHeight(); } /** * @return the Cocoa id field. */ static long getID(Widget control, String string) { Object fieldObject = ReflectionUtil.getFieldObject(control, string); return ReflectionUtil.getFieldLong(fieldObject, "id"); } /** * JNI function that returns the menu bar height. */ private static native int _getMenuBarHeight(); long getCarbonMenuHandle(Menu menu) { long nsMenuId = getID(menu, "nsMenu"); return getCarbonMenuHandle(nsMenuId); } /** * Return true if the accessibility API is enabled. * * To enable it: open System Preferences, select Universal Access, then * select "Enable access for assistive devices". * @return true if the accessibility API is enabled */ public boolean isAXAPIEnabled() { return AXAPIEnabled(); } static final native long getCarbonMenuHandle(long menuId); /** * Returns the value of an accessibility object's attribute. * * @param inUIElement The AXUIElementRef representing the accessibility object. * @param attribute The attribute name. * @param value On return, the value associated with the specified attribute. * @return 0 if no error */ static final native int AXUIElementCopyAttributeValue(long inUIElement, long attribute, long [] value); /** * Convert an accessibility attribute value to a CGPoint. * * @param valueRef the AXValueRef * @param point the CGPoint to hold the value * @return true for success, false if the valueRef is not of the proper type */ /* $codepro.preprocessor.if version > 3.0 $ */ static final native boolean AXValueGetValueCGPoint(long valueRef, CGPoint point); /* $codepro.preprocessor.endif $ */ /** * Convert an accessibility attribute value to a CGSize. * * @param valueRef the AXValueRef * @param point the CGSize to hold the value * @return true for success, false if the valueRef is not of the proper type */ static final native boolean AXValueGetValueCGSize(long valueRef, CGSize size); /** * Return true if the accessibility API is enabled. * * To enable it: open System Preferences, select Universal Access, then * select "Enable access for assistive devices". * @return true if the accessibility API is enabled */ static final native boolean AXAPIEnabled(); static final native long AXUIElementCreateWithHIObjectAndIdentifier(long inHIObject, long inIdentifier); static final native long CFStringCreateWithCharacters(int alloc, char[] chars, long numChars); static final native long CFArrayGetValueAtIndex(long theArray, long idx); /** * Utility method to convert a java String to a CFString. * NOTE: Caller is responsible for releasing the returned value. * * @param string the string to convert * @return a CFString reference */ static long stringToCFStringRef(String string) { /* $codepro.preprocessor.if version > 3.0 $ */ char [] buffer = new char [string.length ()]; string.getChars (0, buffer.length, buffer, 0); return CFStringCreateWithCharacters (kCFAllocatorDefault, buffer, buffer.length); /* $codepro.preprocessor.elseif version <= 3.0 $ throw new RuntimeException("Internal error: Eclipse 3.0 on Mac not supported"); $codepro.preprocessor.endif $ */ } /** * Given a MenuItem, return its bounding box. * * @param item the menu item * @return Rectangle of item (in global coordinates), or null if something didn't work */ public Rectangle getMenuItemBounds(MenuItem item) { /* $codepro.preprocessor.if version > 3.1 $ */ if (item == null) return null; Menu parent = item.getParent(); int index = parent.indexOf(item); if (isMenuBarItem(item)) { List bounds = new ArrayList(); getMenuBarVisualData(parent, bounds); return (Rectangle) bounds.get(index); } int axError; boolean axReturnCode; // Find the OS handle of the menu. It's private in Menu, so we need this workaround. long menuRef = getCarbonMenuHandle(parent); // Get the AX element for the menu long[] children = new long[1]; long axMenuRef = AXUIElementCreateWithHIObjectAndIdentifier(menuRef, (long) 0); long cfChildren = stringToCFStringRef(kAXChildrenAttribute); axError = AXUIElementCopyAttributeValue(axMenuRef, cfChildren, children); OS.CFRelease(cfChildren); OS.CFRelease(axMenuRef); if (axError != 0 || children[0] == 0) { System.out.println("Do you have 'System Preferences/Universal Access/Enable access for assistive devices' turned on?"); return null; } // The Mac menu bar includes the Apple menu and the application menu, which are // not part of the SWT menu bar. If we're looking at the menu bar, increment // the index to skip those two menus. (Does menu bar visibility matter?) if ((parent.getStyle() & SWT.BAR) != 0) index += 2; long menuItem = CFArrayGetValueAtIndex(children[0], index); CGPoint position = new CGPoint(); CGSize size = new CGSize(); // Get the position long cfPosition = stringToCFStringRef(kAXPositionAttribute); long[] positionRef = new long[1]; axError = AXUIElementCopyAttributeValue(menuItem, cfPosition, positionRef); axReturnCode = AXValueGetValueCGPoint(positionRef[0], position); OS.CFRelease(positionRef[0]); OS.CFRelease(cfPosition); if (!axReturnCode) { System.out.println("Internal error: type mismatch in native code"); return null; } // Get the size long cfSize = stringToCFStringRef(kAXSizeAttribute); long[] sizeRef = new long[1]; axError = AXUIElementCopyAttributeValue(menuItem, cfSize, sizeRef); axReturnCode = AXValueGetValueCGSize(sizeRef[0], size); OS.CFRelease(sizeRef[0]); OS.CFRelease(cfSize); if (!axReturnCode) { System.out.println("Internal error: type mismatch in native code"); return null; } Rectangle result = new Rectangle((int)position.x, (int)position.y, (int)size.width, (int)size.height); //System.out.println(result); return result; /* $codepro.preprocessor.elseif version <= 3.1 $ throw new RuntimeException("Internal error: Eclipse 3.0 on Mac not supported"); $codepro.preprocessor.endif $ */ } private static class CGSize { public double width; public double height; } private static class CGPoint { public double x; public double y; } }