package abbot.swt;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.internal.win32.MENUBARINFO;
import org.eclipse.swt.internal.win32.OS;
import org.eclipse.swt.internal.win32.RECT;
import org.eclipse.swt.widgets.CoolItem;
import org.eclipse.swt.widgets.Decorations;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Scrollable;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.TreeItem;
import abbot.tester.swt.TableItemTester;
/**
* This class provides workarounds for non-public SWT methods.
* Note that current code only works on/for win32.
*
* @author gjohnsto
* @version $Id: SWTWorkarounds.java,v 1.17 2005/06/23 22:46:36 tlroche Exp $
*/
public class SWTWorkarounds {
/**
* Shadows the unfortunately un-public
* {@link org.eclipse.swt.widgets.Display#KeyTable}
*/
public static int keyTable[][] = null;
/**
* Workaround for package scope of MenuItem.getBounds().
* Returns a rectangle describing the receiver's size and location
* relative to its parent (or its display if its parent is null).
*
* @return the receiver's bounding rectangle
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.1
*/
public static Rectangle getBounds(MenuItem menuItem) {
// start code adapted from https://bugs.eclipse.org/bugs/show_bug.cgi?id=38436#c113
Menu parent = menuItem.getParent();
Method m1 = null;
try {
m1 = parent.getClass().getDeclaredMethod("getBounds", null);
} catch (Exception e) {
return getBoundsHack(menuItem, parent);
}
Rectangle menuRect = null;
// Avoid NPE for Eclipse 3.0
if (m1 != null) {
m1.setAccessible(true);
try {
menuRect = (Rectangle) m1.invoke(parent, null);
} catch (Exception e) {
return getBoundsHack(menuItem, parent);
}
} else {
return getBoundsHack(menuItem, parent);
}
Method m2 = null;
try {
m2 = menuItem.getClass().getDeclaredMethod("getBounds", null);
} catch (Exception e) {
return getBoundsHack(menuItem, parent);
}
m2.setAccessible(true);
Rectangle itemRect = null;
try {
itemRect = (Rectangle) m2.invoke(menuItem, null);
} catch (Exception e) {
return getBoundsHack(menuItem, parent);
}
return translateRectangle(parent, menuRect, itemRect);
// end code adapted from https://bugs.eclipse.org/bugs/show_bug.cgi?id=38436#c113
}
// start code adapted from o.e.swt.patched.MenuItem.getBounds()
// Menu parent = menuItem.getParent();
// Decorations parentControl = parent.getParent();
// if (OS.IsWinCE) return new Rectangle (0, 0, 0, 0);
// int index = parent.indexOf (menuItem);
// if (index == -1) return new Rectangle (0, 0, 0, 0);
// RECT rect = new RECT();
// boolean success = OS.GetMenuItemRect(((parent.getStyle()&SWT.BAR)==SWT.BAR)?parentControl.handle:0,
// parent.handle,
// index,
// rect);
// if (!success) {
// /* the OS API call couldn't get the MenuItem bounds, so return an empty rectangle */
// return new Rectangle(0, 0, 0, 0);
// }
// if ((parentControl.getStyle() & SWT.MIRRORED) == 0) {
// /* coordinates are not mirrored */
// Point p = parentControl.toControl(rect.left,rect.top);
// int width = rect.right - rect.left;
// int height = rect.bottom - rect.top;
// return new Rectangle(p.x,p.y,width,height);
// } else {
// /* coordinates are mirrored */
// /* TODO: this has not been tested */
// Point p = parentControl.toControl(rect.left,rect.top);
// int width = rect.left - rect.right;
// int height = rect.bottom - rect.top;
// return new Rectangle(p.x,p.y,width,height);
// }
//end code adapted from o.e.swt.patched.MenuItem.getBounds()
protected static Rectangle translateRectangle(Menu parent, Rectangle menuRect, Rectangle itemRect) {
if ((parent.getStyle() & SWT.RIGHT_TO_LEFT) != 0) {
itemRect.x = menuRect.x + menuRect.width - itemRect.width - itemRect.x;
} else {
itemRect.x += menuRect.x;
}
itemRect.y += menuRect.y;
return itemRect;
}
/**
* Kludge for versions of Eclipse lacking
* {@link org.eclipse.swt.widgets.MenuItem#getBounds()}.
* This is a copy/mod of code from 3.1-M7. The only
* mod is rectangle translation: see
* {@link #translateRectangleHack(Rectangle, int, int)}
*/
// TODO: need better handling of Eclipse versions!
protected static Rectangle getBoundsHack(MenuItem menuItem, Menu parent) {
// start code copy/mod from org.eclipse.swt.widgets.MenuItem.getBounds()
if (OS.IsWinCE) return new Rectangle (0, 0, 0, 0);
int index = parent.indexOf (menuItem);
if (index == -1) return new Rectangle (0, 0, 0, 0);
if ((parent.getStyle() & SWT.BAR) != 0) {
Decorations shell = parent.getParent();
if (shell.getMenuBar() != parent) {
return new Rectangle (0, 0, 0, 0);
}
int hwndShell = shell.handle;
MENUBARINFO info1 = new MENUBARINFO ();
info1.cbSize = MENUBARINFO.sizeof;
if (!OS.GetMenuBarInfo (hwndShell, OS.OBJID_MENU, 1, info1)) {
return new Rectangle (0, 0, 0, 0);
}
MENUBARINFO info2 = new MENUBARINFO ();
info2.cbSize = MENUBARINFO.sizeof;
if (!OS.GetMenuBarInfo (hwndShell, OS.OBJID_MENU, index + 1, info2)) {
return new Rectangle (0, 0, 0, 0);
}
int x = info2.left - info1.left;
int y = info2.top - info1.top;
int width = info2.right - info2.left;
int height = info2.bottom - info2.top;
// return new Rectangle (x, y, width, height);
return translateRectangleHack(
new Rectangle (x, y, width, height), info1.left, info1.top);
} else {
int hMenu = parent.handle;
RECT rect1 = new RECT ();
if (!OS.GetMenuItemRect (0, hMenu, 0, rect1)) {
return new Rectangle (0, 0, 0, 0);
}
RECT rect2 = new RECT ();
if (!OS.GetMenuItemRect (0, hMenu, index, rect2)) {
return new Rectangle (0, 0, 0, 0);
}
int x = rect2.left - (rect1.left + 2);
int y = rect2.top - (rect1.top + 2);
int width = rect2.right - rect2.left;
int height = rect2.bottom - rect2.top;
// return new Rectangle (x, y, width, height);
return translateRectangleHack(
new Rectangle (x, y, width, height), (rect1.left + 2), (rect1.top + 2));
}
// end code copy/mod from org.eclipse.swt.widgets.MenuItem.getBounds()
}
/**
* Does translation for {@link getBoundsHack(MenuItem, Menu)}.
* (May only be needed for 3.0 support.)
* Thanks Joerg Weingarten!
*/
protected static Rectangle translateRectangleHack(
Rectangle itemRect, int deltaX, int deltaY) {
itemRect.x += deltaX;
itemRect.y += deltaY;
return itemRect;
}
/**
* Workaround for lack of Menu.getBounds().
* Adapted from the non-public org.eclipse.swt.widgets.Menu.getBounds() in Eclipse 3.1M6.
*
* Returns a rectangle describing the receiver's size and location
* relative to its parent (or its display if its parent is null),
* unless the receiver is a menu or a shell. In this case, the
* location is relative to the display.
*
* Note that the bounds of a menu or menu item are undefined when
* the menu is not visible. This is because most platforms compute
* the bounds of a menu dynamically just before it is displayed.
*
*
* @return the receiver's bounding rectangle
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*
* @since 3.1
*/
public static Rectangle getBounds (Menu menu) {
// menu.getParent() calls checkWidget() on itself.
//checkWidget ();
Decorations parent = menu.getParent();
if (OS.IsWinCE) return new Rectangle (0, 0, 0, 0);
if ((menu.getStyle() & SWT.BAR) != 0) {
if (parent.getMenuBar() != menu) {
return new Rectangle (0, 0, 0, 0);
}
int hwndShell = parent.handle;
MENUBARINFO info = new MENUBARINFO ();
info.cbSize = MENUBARINFO.sizeof;
if (OS.GetMenuBarInfo (hwndShell, OS.OBJID_MENU, 0, info)) {
int width = info.right - info.left;
int height = info.bottom - info.top;
return new Rectangle (info.left, info.top, width, height);
}
} else {
int count = menu.getItemCount();
if (count != 0) {
RECT rect1 = new RECT ();
if (OS.GetMenuItemRect (0, menu.handle, 0, rect1)) {
RECT rect2 = new RECT ();
if (OS.GetMenuItemRect (0, menu.handle, count - 1, rect2)) {
int x = rect1.left - 2, y = rect1.top - 2;
int width = (rect2.right - rect2.left) + 4;
int height = (rect2.bottom - rect1.top) + 4;
return new Rectangle (x, y, width, height);
}
}
}
}
return new Rectangle (0, 0, 0, 0);
}
/**
* Workaround for lack of ScrollBar.getBounds().
* Adapted from the non-public org.eclipse.swt.widgets.ScrollBar.getBounds() in Eclipse 3.1M6.
*/
public static Rectangle getBounds (ScrollBar scrollBar) {
// scrollBar.getParent() calls checkWidget() on itself.
// checkWidget ();
Scrollable parent = scrollBar.getParent();
// Use getClientArea() to call forceResize(), which is private.
//parent.forceResize ();
parent.getClientArea();
RECT rect = new RECT ();
OS.GetClientRect (parent.handle, rect);
int x = 0, y = 0, width, height;
if ((scrollBar.getStyle() & SWT.HORIZONTAL) != 0) {
y = rect.bottom - rect.top;
width = rect.right - rect.left;
height = OS.GetSystemMetrics (OS.SM_CYHSCROLL);
} else {
x = rect.right - rect.left;
width = OS.GetSystemMetrics (OS.SM_CXVSCROLL);
height = rect.bottom - rect.top;
}
return new Rectangle (x, y, width, height);
}
// Below is the original adaptation of the patch version.
// public static Rectangle getBounds (ScrollBar scrollBar) {
//
//// Call to (private) checkWidget not needed because getParent() calls it first thing.
//// scrollBar.checkWidget ();
// Scrollable parent = scrollBar.getParent();
//
//// Can't call forceResize() because it's private.
//// So, instead call getClientArea(), which calls forceResize(), throwing away the result.
//// parent.forceResize ();
// parent.getClientArea();
//
// RECT rect = new RECT ();
// OS.GetClientRect (parent.handle, rect);
// int x = 0, y = 0, width, height;
// if ((scrollBar.getStyle() & SWT.HORIZONTAL) != 0) {
// y = rect.bottom - rect.top;
// width = rect.right - rect.left;
// height = OS.GetSystemMetrics (OS.SM_CYHSCROLL);
// } else {
// x = rect.right - rect.left;
// width = OS.GetSystemMetrics (OS.SM_CXVSCROLL);
// height = rect.bottom - rect.top;
// }
// return new Rectangle (x, y, width, height);
// }
/**
* Workaround for lack of TabItem.getBounds().
*
* Returns a rectangle describing a TabItem's size and location
* relative to its parent.
*
* @return the specified TabItem's bounding rectangle
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*/
public static Rectangle getBounds(TabItem tabItem){
// Call to tabItem.getParent(), next, first calls checkWidget().
// tabItem.checkWidget();
TabFolder parent = tabItem.getParent();
int index = parent.indexOf(tabItem);
if (index == -1) return new Rectangle (0, 0, 0, 0);
RECT rect = new RECT();
OS.SendMessage( parent.handle,
OS.TCM_GETITEMRECT,
index,
rect);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
// return new Rectangle(rect.left,rect.top,width,height);
// moved code from WidgetLocator so as to centralize in this class
Rectangle bounds = new Rectangle(rect.left,rect.top,width,height);
Point p = tabItem.getParent().toDisplay(bounds.x,bounds.y);
return new Rectangle(p.x,p.y,bounds.width,bounds.height);
}
/* Used by getBounds(TableColumn), below. */
private static final int OS_HDM_GETITEMRECT = OS.HDM_FIRST + 7;
/**
* Returns a rectangle describing the receiver's header relative
* to its parent table.
*
* @return the bounding rectangle of the receiver's header
*
* @exception SWTException
* - ERROR_WIDGET_DISPOSED - if the receiver has been disposed
* - ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver
*
*/
public static Rectangle getBounds(TableColumn tableColumn) {
// Call to tableColumn.getParent(), next, first calls checkWidget().
// tableColumn.checkWidget();
Table parent = tableColumn.getParent();
int index = parent.indexOf(tableColumn);
if (index == -1) return new Rectangle (0, 0, 0, 0);
int hwnd = parent.handle;
int hwndHeader = OS.SendMessage (hwnd, OS.LVM_GETHEADER, 0, 0);
RECT rect = new RECT();
OS.SendMessage(hwndHeader,OS_HDM_GETITEMRECT,index,rect);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
// return new Rectangle(rect.left,rect.top,width,height);
// moved code from WidgetLocator so as to centralize in this class
Rectangle bounds = new Rectangle(rect.left,rect.top,width,height);
Point p = tableColumn.getParent().toDisplay(bounds.x,bounds.y);
return new Rectangle(p.x,p.y,bounds.width,bounds.height);
}
/**
* {@link org.eclipse.swt.widgets.TableItem#getBounds()} is not public?
* Hence this workaround. Note request for public-ity in
*
* the getBounds() bugzilla
*/
public static Rectangle getBounds(TableItem item) {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=38436#c86
// return TableItemTester.getTableItemTester().getBounds(item, 0);
// moved code from WidgetLocator so as to centralize in this class
Rectangle bounds = TableItemTester.getTableItemTester().getBounds(item, 0);
Point p = item.getParent().toDisplay(bounds.x,bounds.y);
return new Rectangle(p.x,p.y,bounds.width,bounds.height);
}
public static Rectangle getBounds(TreeItem item) {
// moved code from WidgetLocator so as to centralize in this class
Rectangle bounds = item.getBounds();
Point p = item.getParent().toDisplay(bounds.x,bounds.y);
return new Rectangle(p.x,p.y,bounds.width,bounds.height);
}
public static Rectangle getBounds(CTabItem item) {
// moved code from WidgetLocator so as to centralize in this class
// CTabItem has getBounds(), though TabItem does not
Rectangle bounds = item.getBounds();
Point p = item.getParent().toDisplay(bounds.x,bounds.y);
return new Rectangle(p.x,p.y,bounds.width,bounds.height);
}
public static Rectangle getBounds(ToolItem item) {
// moved code from WidgetLocator so as to centralize in this class
Rectangle bounds = item.getBounds();
Point p = item.getParent().toDisplay(bounds.x,bounds.y);
return new Rectangle(p.x,p.y,bounds.width,bounds.height);
}
public static Rectangle getBounds(CoolItem item) {
// moved code from WidgetLocator so as to centralize in this class
Rectangle bounds = item.getBounds();
Point p = item.getParent().toDisplay(bounds.x,bounds.y);
return new Rectangle(p.x,p.y,bounds.width,bounds.height);
}
/**
* Translates from SWT keys values to AWT ones, e.g.
* from SWT.ESC
to VK_ESCAPE
.
* For details see
* {@link org.eclipse.swt.widgets.Display#KeyTable}
* Workaround for package scope of
* {@link org.eclipse.swt.widgets.Display#untranslateKey(int)}
*/
public static int swt2Awt(int swtKeyValue) {
if (keyTable == null) {
initKeyTable();
return swt2Awt(swtKeyValue, keyTable);
} else {
return swt2Awt(swtKeyValue, keyTable);
}
}
protected static void initKeyTable() {
Field f = null;
try {
f = Display.class.getDeclaredField("KeyTable");
f.setAccessible(true);
keyTable = (int[][])(f.get(Display.class));
} catch (NoSuchFieldException e) {
// TODO_Tom Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO_Tom Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO_Tom Auto-generated catch block
e.printStackTrace();
}
}
protected static int swt2Awt(int key, int[][] KeyTable) {
if (KeyTable == null) return 0;
// same code as org.eclipse.swt.widgets.Display.untranslateKey#int
for (int i=0; i