package org.eclipse.swt.widgets;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import abbot.Platform;
import com.windowtester.swt.platform.ext.macosx.MacExtensions;
/**
* This class adapts SWT to Abbot, e.g. where SWT methods are not public.
*
* @author Gary Johnston
* @author Steve Northover
* @version $Id: SWTWorkarounds.java,v 1.15 2009-01-26 17:46:22 pq Exp $
*/
public class SWTWorkarounds {
/**
* The MacExtensions instance. It is initialized during plug-in activation
* when running Mac OSX. It will be null otherwise.
* @see com.windowtester.swt.RuntimePlugin#start(org.osgi.framework.BundleContext)
*/
public static MacExtensions MacExt;
/*************************** COMMON *****************************/
public static Rectangle getBounds (Object object) {
Rectangle result = new Rectangle (0, 0, 0, 0);
try {
Method method = object.getClass().getDeclaredMethod ("getBounds", null);
method.setAccessible(true);
result = (Rectangle) method.invoke (object, null);
} catch (Throwable th) {
// TODO - decide what should happen when the method is unavailable
}
return result;
}
public static Rectangle getBounds(MenuItem menuItem) {
if (Platform.isOSX()) {
Rectangle result = MacExt.getMenuItemBounds(menuItem);
if (menuItem.isDisposed()) {
throw new NullPointerException(); // expected to be caught in WidgetLocator.getLocation(Widget, boolean)
}
return result;
}
Rectangle itemRect = getBounds ((Object)menuItem);
Rectangle menuRect = getBounds (menuItem.getParent ());
if ((menuItem.getParent ().getStyle() & SWT.RIGHT_TO_LEFT) != 0) {
itemRect.x = menuRect.x + menuRect.width - itemRect.width - itemRect.x;
} else {
itemRect.x += menuRect.x;
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=38436#c143
itemRect.y += menuRect.y;
return itemRect;
}
public static Rectangle getBounds (Menu menu) {
return getBounds ((Object)menu);
}
public static Rectangle getBounds (ScrollBar scrollBar) {
Point size = scrollBar.getSize ();
Rectangle bounds = scrollBar.getParent().getBounds();
if ((scrollBar.getParent ().getStyle() & SWT.RIGHT_TO_LEFT) != 0) {
bounds.x = 0;
bounds.width = size.x;
} else {
bounds.x = bounds.width - size.x;
}
bounds.y = bounds.height - size.y;
// TODO - coordinate system may change when the API is added to SWT
return scrollBar.getDisplay().map (scrollBar.getParent (), null, bounds);
}
/*************************** WIN32 *****************************/
static int SendMessage (long hWnd, int Msg, int wParam, int [] lParam)
{
/*
* In the move to 3.5M4 we're seeing this error in the build:
* SendMessage(int,int,int,int[]) in SWTWorkarounds cannot be applied to (long,int,int,int[])
* This cast fixes the compilation. NOTE: this method should not get called in 3.5.
*/
return SendMessage((int)hWnd, Msg, wParam, lParam);
}
static int SendMessage (int hWnd, int Msg, int wParam, int [] lParam)
{
int result = 0;
try {
Class clazz = Class.forName ("org.eclipse.swt.internal.win32.OS");
Class [] params = new Class [] {
Integer.TYPE,
Integer.TYPE,
Integer.TYPE,
lParam.getClass (),
};
Method method = clazz.getMethod ("SendMessage", params);
Object [] args = new Object [] {
new Integer (hWnd),
new Integer (Msg),
new Integer (wParam),
lParam,
};
result = ((Integer) method.invoke (clazz, args)).intValue ();
} catch (Throwable e) {
// TODO - decide what should happen when the method is unavailable
}
return result;
}
static Rectangle win32_getBounds(TabItem tabItem) {
TabFolder parent = tabItem.getParent();
int index = parent.indexOf (tabItem);
if (index == -1) return new Rectangle (0, 0, 0, 0);
int [] rect = new int [4];
SendMessage (parent.handle, /*TCM_GETITEMRECT*/ 0x130a, index, rect);
int width = rect [2] - rect[0];
int height = rect [3] - rect [1];
Rectangle bounds = new Rectangle (rect [0], rect [1], width, height);
return tabItem.getDisplay().map (tabItem.getParent (), null, bounds);
}
static Rectangle win32_getBounds(TableColumn tableColumn) {
Table parent = tableColumn.getParent ();
int index = parent.indexOf (tableColumn);
if (index == -1) return new Rectangle (0, 0, 0, 0);
int hwndHeader = SendMessage (parent.handle, /*LVM_GETHEADER*/ 0x101f, 0, new int [0]);
int [] rect = new int [4];
SendMessage (hwndHeader, /*HDM_GETITEMRECT*/ 0x1200 + 7, index, rect);
int width = rect [2] - rect[0];
int height = rect [3] - rect [1];
Rectangle bounds = new Rectangle (rect [0], rect [1], width, height);
// TODO - oordinate system may change when the API is added to SWT
return tableColumn.getDisplay().map (parent, null, bounds);
}
/*************************** GTK *****************************/
static void gtk_getBounds (int handle, Rectangle bounds) {
try {
Class clazz = Class.forName ("org.eclipse.swt.internal.gtk.OS");
Class [] params = new Class [] {Integer.TYPE};
Object [] args = new Object [] {new Integer (handle)};
Method method = clazz.getMethod ("GTK_WIDGET_X", params);
bounds.x = ((Integer) method.invoke (clazz, args)).intValue ();
method = clazz.getMethod ("GTK_WIDGET_Y", params);
bounds.y = ((Integer) method.invoke (clazz, args)).intValue ();
method = clazz.getMethod ("GTK_WIDGET_WIDTH", params);
bounds.width = ((Integer) method.invoke (clazz, args)).intValue ();
method = clazz.getMethod ("GTK_WIDGET_HEIGHT", params);
bounds.height = ((Integer) method.invoke (clazz, args)).intValue ();
} catch (Throwable e) {
// TODO - decide what should happen when the method is unavailable
}
}
static Rectangle gtk_getBounds(TableColumn tabColumn) {
Rectangle bounds = new Rectangle (0, 0, 0, 0);
try {
Class c = tabColumn.getClass();
Field f = c.getDeclaredField("buttonHandle");
f.setAccessible(true);
int handle = f.getInt(tabColumn);
gtk_getBounds(handle, bounds);
} catch (Throwable e) {
// TODO - decide what should happen when the method is unavailable
}
return tabColumn.getDisplay().map (tabColumn.getParent (), null, bounds);
}
static Rectangle gtk_getBounds(TabItem tabItem) {
Rectangle bounds = new Rectangle (0, 0, 0, 0);
try {
Class c = Class.forName ("org.eclipse.swt.widgets.Widget");
Field f = c.getDeclaredField("handle");
f.setAccessible(true);
int handle = f.getInt(tabItem);
gtk_getBounds(handle, bounds);
} catch (Throwable e) {
// TODO - decide what should happen when the method is unavailable
}
return tabItem.getDisplay().map (tabItem.getParent (), null, bounds);
}
/*************************** MOTIF *****************************/
static Rectangle motif_getBounds(TabItem tabItem) {
Rectangle bounds = new Rectangle (0, 0, 0, 0);
try {
Class c = tabItem.getClass();
Method m = c.getDeclaredMethod("getBounds", null);
m.setAccessible(true);
bounds = (Rectangle)m.invoke(tabItem, null);
int margin = 2;
bounds.x +=margin;bounds.y+=margin;
bounds.width -= 2*margin; bounds.height-=margin;
} catch (Throwable e) {
// TODO - decide what should happen when the method is unavailable
}
return tabItem.getDisplay().map (tabItem.getParent (), null, bounds);
}
static Rectangle motif_getBounds(TableColumn tableColumn) {
Rectangle bounds = new Rectangle (0, 0, 0, 0);
try {
Class c = tableColumn.getClass();
Method m = c.getDeclaredMethod("getX", null);
m.setAccessible(true);
bounds.x = ((Integer)m.invoke(tableColumn, null)).intValue();
bounds.width = tableColumn.getWidth() - 2;
bounds.height = tableColumn.getParent().getHeaderHeight() - 2;
} catch (Throwable e) {
// TODO - decide what should happen when the method is unavailable
}
return tableColumn.getDisplay().map (tableColumn.getParent (), null, bounds);
}
/*************************** CARBON *****************************/
static Rectangle carbon_getBounds(TabItem tabItem) {
Rectangle bounds = MacExt.getTabItemBounds(tabItem);
// bounds = tabItem.getDisplay().map(tabItem.getParent(), null, bounds);
// System.out.println(bounds);
return bounds;
}
static Rectangle carbon_getBounds(TableColumn tableColumn) {
Table table = tableColumn.getParent();
if (!table.getHeaderVisible())
return new Rectangle(0, 0, 0, 0);
TableColumn[] columns = table.getColumns();
int x = 0;
int width = 0;
int height = table.getHeaderHeight();
for (int i = 0; i < columns.length; i++) {
TableColumn col = columns[i];
if (col == tableColumn) {
width = col.getWidth();
break;
} else
x += col.getWidth();
}
if (width == 0)
return new Rectangle(0, 0, 0, 0);
Rectangle bounds = new Rectangle(x, 0, width, height);
bounds = tableColumn.getDisplay().map(table, null, bounds);
return bounds;
}
public static Rectangle getBounds (TabItem tabItem){
if (SWT.getPlatform().equals("win32")) {
return win32_getBounds (tabItem);
}
if (SWT.getPlatform().equals("gtk")) {
return gtk_getBounds (tabItem);
}
if (SWT.getPlatform().equals("motif")) {
return motif_getBounds (tabItem);
}
if (SWT.getPlatform().equals("carbon")) {
return carbon_getBounds (tabItem);
}
return null;
}
public static Rectangle getBounds (TableColumn tableColumn) {
if (SWT.getPlatform().equals("win32")) {
return win32_getBounds (tableColumn);
}
if (SWT.getPlatform().equals("gtk")) {
return gtk_getBounds (tableColumn);
}
if (SWT.getPlatform().equals("motif")) {
return motif_getBounds (tableColumn);
}
if (SWT.getPlatform().equals("carbon")) {
return carbon_getBounds (tableColumn);
}
return null;
}
public static Rectangle getBounds (TableItem item) {
return item.getDisplay().map (item.getParent (), null, item.getBounds (0));
}
public static Rectangle getBounds (TreeItem item) {
return item.getDisplay().map (item.getParent (), null, item.getBounds ());
}
public static Rectangle getBounds (CTabItem item) {
return item.getDisplay().map (item.getParent (), null, item.getBounds ());
}
public static Rectangle getBounds (ToolItem item) {
return item.getDisplay().map (item.getParent (), null, item.getBounds ());
}
public static Rectangle getBounds (CoolItem item) {
return item.getDisplay().map (item.getParent (), null, item.getBounds ());
}
//!pq:
public static MenuItem[] getItems(Display d) {
MenuItem[] result = null;
try {
Field field = d.getClass().getDeclaredField("items");
field.setAccessible(true);
result = (MenuItem[]) field.get(d);
} catch (Throwable th) {
th.printStackTrace();
// TODO - decide what should happen when the method is unavailable
}
//prune out null entries
java.util.List pruned = new ArrayList();
if (result != null) {
for (int i = 0; i < result.length; i++) {
if (result[i] != null)
pruned.add(result[i]);
}
}
return (MenuItem[])pruned.toArray(new MenuItem[]{});
}
//!pq:
//TODO[pq]: move this to a fragment
public static Menu[] getMenus(Display d) {
MenuItem[] result = null;
try {
Field field = d.getClass().getDeclaredField("items");
field.setAccessible(true);
result = (MenuItem[]) field.get(d);
} catch (Throwable th) {
th.printStackTrace();
// TODO - decide what should happen when the method is unavailable
}
LinkedHashSet set = new LinkedHashSet();
if (result != null) {
for (int i = 0; i < result.length; i++) {
if (result[i] != null) {
Menu menu = (Menu) ((MenuItem) result[i]).getParent();
// TODO: clean this up!
if (menu != null && !isSubMenu(menu) && !parentIsControl(menu))
set.add(menu);
}
}
}
return (Menu[])set.toArray(new Menu[]{});
}
//TODO: fix me... producing duplicates...
private static boolean isSubMenu(Menu menu) {
MenuItem parent = menu.getParentItem();
return parent != null;
}
private static boolean parentIsControl(Menu menu) {
Decorations parent = menu.getParent();
return (parent != null && parent.getClass().equals(Control.class));
}
//!pq:
public static Menu[] getMenus(Decorations shell) {
Menu[] result = null;
try {
if (Platform.isOSX()) {
Menu bar = shell.getMenuBar();
if (bar == null)
return new Menu[0]; // TODO Mac testing
MenuItem[] items = bar.getItems();
result = new Menu[items.length];
for (int i = 0; i < items.length; i++)
result[i] = items[i].getMenu();
} else {
Field field = Decorations.class.getDeclaredField("menus");
field.setAccessible(true);
result = (Menu[]) field.get(shell);
}
} catch (Throwable th) {
th.printStackTrace();
// TODO - decide what should happen when the method is unavailable
}
return result;
}
//!pq:
public static Rectangle getBounds(Control w, Layout layout) {
Rectangle bounds = new Rectangle (0, 0, 0, 0);
Point result;
try {
Class c = Layout.class;
Class [] params = new Class [] {
Composite.class,
Integer.TYPE,
Integer.TYPE,
Boolean.TYPE,
};
Method m = c.getDeclaredMethod("computeSize", params);
m.setAccessible(true);
Object[] args = {w.getParent(), new Integer(0), new Integer(0), new Boolean(false)};
result = (Point)m.invoke(layout, args);
} catch (Throwable e) {
// TODO - decide what should happen when the method is unavailable
}
return bounds;
}
/**
* Return true if the accessibility API must be enabled. This is
* Mac-specific so return false if not on a Mac.
*
* @return true if running on Mac and accessibility needs to be enabled
*/
public static boolean isMacAccessibilityDisabled() {
if (Platform.isOSX())
return !MacExt.isAXAPIEnabled();
return false;
}
}