package abbot.tester.swt;
import java.util.Iterator;
import java.util.List;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import com.windowtester.runtime.swt.internal.abbot.SWTWorkarounds;
import com.windowtester.runtime.swt.internal.util.TextUtils;
/**
* Allows menu items to be automatically selected and expanded.
*
* @version $Id: MenuItemTester.java,v 1.3 2007-11-27 17:17:39 pq Exp $
*/
public class MenuItemTester extends ItemTester {
public static final String copyright = "Licensed Materials -- Property of IBM\n"+
"(c) Copyright International Business Machines Corporation, 2003\nUS Government "+
"Users Restricted Rights - Use, duplication or disclosure restricted by GSA "+
"ADP Schedule Contract with IBM Corp.";
public static final String ARM_LISTENER_NAME = "a4sArmListener";
public static final String SELECTION_LISTENER_NAME = "a4sSelectionListener";
public static final String WATCHER_NAME = "a4sWatcher";
public static final int PATH_CLICKING_WAIT_TIME = 500000;
public static final String DEFAULT_MENUITEM_PATH_DELIMITER = "/";
/*
* These getter methods return a particular property of the given widget.
* @see the corresponding member function in class Widget
*/
/* Begin getters */
/**
* Proxy for {@link MenuItem#getAccelerator()}.
* <p/>
* @param item the item under test.
* @return the accelerator.
*/
public int getAccelerator(final MenuItem item){
Integer result = (Integer) Robot.syncExec(item.getDisplay(), new RunnableWithResult(){
public Object runWithResult(){
return new Integer(item.getAccelerator());
}
});
return result.intValue();
}
/**
* Proxy for {@link MenuItem#getEnabled()}.
* <p/>
* @param item the item under test.
* @return the item's enabled state.
*/
public boolean getEnabled(final MenuItem item){
Boolean result = (Boolean) Robot.syncExec(item.getDisplay(), new RunnableWithResult(){
public Object runWithResult(){
return Boolean.valueOf(item.getEnabled());
}
});
return result.booleanValue();
}
/**
* Proxy for {@link MenuItem#getSelection()}.
* <p/>
* @param item the item under test.
* @return true if the item is selected.
*/
public boolean getSelection(final MenuItem item){
Boolean result = (Boolean) Robot.syncExec(item.getDisplay(), new RunnableWithResult(){
public Object runWithResult(){
return Boolean.valueOf(item.getSelection());
}
});
return result.booleanValue();
}
/**
* Proxy for {@link MenuItem#getMenu()}.
* <p/>
* @param item the item under test.
* @return the item's menu.
*/
public Menu getMenu(final MenuItem item){
Menu result = (Menu) Robot.syncExec(item.getDisplay(), new RunnableWithResult(){
public Object runWithResult(){
return item.getMenu();
}
});
return result;
}
/**
* Proxy for {@link MenuItem#getParent()}.
* <p/>
* @param item the item under test.
* @return the item's parent.
*/
public Menu getParent(final MenuItem item){
Menu result = (Menu) Robot.syncExec(item.getDisplay(), new RunnableWithResult(){
public Object runWithResult(){
return item.getParent();
}
});
return result;
}
/**
* Proxy for {@link MenuItem#isEnabled()}.
* <p/>
* @param item the item under test.
* @return true if the item is enabled.
*/
public boolean isEnabled(final MenuItem item) {
Boolean result = (Boolean) Robot.syncExec(item.getDisplay(), new RunnableWithResult(){
public Object runWithResult(){
return Boolean.valueOf(item.isEnabled());
}
});
return result.booleanValue();
}
/**
* Computes the bounds of the item given.
* <p/>
* @param item the item under test.
* @return the bounds of the item.
*/
public Rectangle getBounds(final MenuItem item){
Rectangle result = (Rectangle) Robot.syncExec(item.getDisplay(), new RunnableWithResult(){
public Object runWithResult(){
return SWTWorkarounds.getBounds(item);
}
});
return result;
}
/* end getters */
// returns the path for the given menu item
public String getPathString(MenuItem item){
return getPathString(item,DEFAULT_MENUITEM_PATH_DELIMITER);
}
public String getPathString(MenuItem item, String delimiter){
String path = "";
Menu parent;
while(item!=null){
//!pq: adding fix to escape use of delimiter in menu items
path= TextUtils.escapeSlashes(getText(item))+delimiter+path;
parent = getParent(item);
item = ((MenuTester)getTester(Menu.class)).getParentItem(parent);
}
path = path.substring(0,path.length()-1);
return path;
}
// public void actionSelectMenuItem(final String path, final Control parentControl, final Decorations parentDecorations, final int delay) {
// actionSelectMenuItem(path,parentControl,parentDecorations,delay,DEFAULT_MENUITEM_PATH_DELIMITER);
// }
//
// public void actionSelectMenuItem(final String path, final Control parentControl, final Decorations parentDecorations, final int delay, String delimiter)
// {
// final boolean [] done = {false};
// final StringBuffer debugBuffer = new StringBuffer();
// debugBuffer.append("PATH:[");
// debugBuffer.append(path);
// debugBuffer.append("]\n");
// final StringTokenizer st = new StringTokenizer(path,delimiter);
// final int expectedNumberOfArmEvents = st.countTokens();
// final List watchers = new ArrayList(expectedNumberOfArmEvents);
// //final List<Watcher> watchers = new ArrayList<Watcher>(expectedNumberOfArmEvents);
// for (int i = 0; i < expectedNumberOfArmEvents; i++) {
// watchers.add(i,new Watcher(debugBuffer));
// }
// Thread t = new Thread(){
// public void run(){
// actionClickMenuItemByPathImp(st,parentControl,parentDecorations, delay, watchers,debugBuffer,done);
// Log.log("Leaving MenuItemTester thread for:"+path);
// }
// };
// t.start();
// wait(new Condition() {
//
// public boolean test() {
// return (done[0] && (getNumberOfArmedEvents(watchers) == expectedNumberOfArmEvents));
// }
//
// public String toString() {
// return path + " to have "+expectedNumberOfArmEvents+"SWT.Arm events\n\nfor path:"
// +path+"\n\n"+debugBuffer.toString();
// }
//
// });
// }
// /**
// * Clicks each MenuItem along the path. Use '/' as the path delimiter, and
// * note that "/item1/" resolves to a path of length 3, with item texts "", "item1", and "".
// * In addition, a test will freeze if clicking the last item in the path
// * opens another menu.
// *
// * @param path the path to the menu item
// * @param parentControl the Control that owns the root pop-up menu, or null if not popup
// * @param parentDecorations the parent Decorations
// * @param delay time (in ms) to wait between item clicks
// * @deprecated Use actionSelectMenuItem
// */
// // TODO: resolve naming issues for this method (see email), refactor to use new
// // matcher API, limit search to menu container as appropriate, determine when
// // to quit
// // not threadsafe
// public void actionClickMenuItemByPath(final String path, final Control parentControl,
// final Decorations parentDecorations, final int delay){
// System.out.println ("You should call actionSelectMenuItem instead. This method will be going away soon.");
// actionSelectMenuItem(path, parentControl, parentDecorations, delay);
// }
// private void actionClickMenuItemByPathImp(StringTokenizer path, Control parentControl, Decorations parentDecorations, int delay, List watchers, StringBuffer buffer, boolean [] done){
// //private void actionClickMenuItemByPathImp(StringTokenizer path, Control parentControl, Decorations parentDecorations, int delay, List<Watcher> watchers, StringBuffer buffer){
// // rt-click control if menu is a popup
// if(parentControl != null){
// Rectangle bounds = ((ControlTester)WidgetTester.getTester(Control.class)).
// getBounds(parentControl);
// actionClick(parentControl,bounds.x/2,bounds.y/2,"BUTTON1");
// }
//
// MenuItem lastItem = null;
// int watcherIndex = 0;
// try {
// do {
// String token = path.nextToken();
// lastItem = resolveAndClickItem(token, parentControl, parentDecorations, ((lastItem==null) ? (Widget)parentDecorations : (Widget)lastItem),(Watcher)watchers.get(watcherIndex));
// actionDelay(delay);
// watcherIndex++;
// } while(path.hasMoreTokens());
// } catch (WidgetNotFoundException e) {
// e.printStackTrace();
// buffer.append("\nWidgetNotFoundException:");
// buffer.append(e.getMessage());
// int numberOfArmedMenus = getNumberOfArmedEvents(watchers);
// escapeArmedMenus(numberOfArmedMenus);
// } catch (MultipleWidgetsFoundException e) {
// e.printStackTrace();
// buffer.append("\nMultipleWidgetsFoundException:");
// buffer.append(e.getMessage());
// int numberOfArmedMenus = getNumberOfArmedEvents(watchers);
// escapeArmedMenus(numberOfArmedMenus);
// } finally {
// done[0] = true;
// }
// }
//
// private MenuItem resolveAndClickItem(String text, Control parentControl,
// Decorations parentDecorations, Widget parent, Watcher watcher)
// throws WidgetNotFoundException, MultipleWidgetsFoundException
// {
// BasicFinder finder = new BasicFinder(new TestHierarchy(parentDecorations.getDisplay()));
// MenuItem item = null;
// item = (MenuItem)finder.find(parent, new TextMatcher(text));
// addWatcher(item, watcher);
// actionClick(item);
// final MenuItem itemT = item;
// wait(new Condition() {
// public boolean test() {
// return itemT.isDisposed() || isArmedOrSelected(itemT);
// }
// }, PATH_CLICKING_WAIT_TIME);
//
// if(item != null && !item.isDisposed()) {
// removeWatcher(item);
// }
// return item;
// }
private void escapeArmedMenus(int numberOfArmEvents) {
for (int i = 0; i <= numberOfArmEvents;i++) {
actionDelay(200);
keyPress(SWT.ESC);
keyRelease(SWT.ESC);
}
}
// Listener to signal when a menu item has been clicked
private class Watcher implements Listener{
private volatile boolean _gotArm = false;
//private volatile boolean _gotSelection = false;
private StringBuffer _debugBuffer;
public Watcher(StringBuffer buffer) {
_debugBuffer = buffer;
}
public void handleEvent(org.eclipse.swt.widgets.Event e){
if(e.type==SWT.Arm){
_debugBuffer.append("\n[SWT.Arm]:");
_debugBuffer.append(e);
_gotArm = true;
}
if(e.type==SWT.Selection) {
_debugBuffer.append("\n[SWT.Selection]:");
_debugBuffer.append(e);
//_gotSelection= true;
}
}
public boolean gotArmEvent() {return _gotArm;}
//public boolean gotSelectionEvent() {return _gotSelection;}
}
// adds a watcher-listener to the given menu item
private void addWatcher(final MenuItem item, final Watcher watcher){
item.getDisplay().syncExec(new Runnable(){
public void run(){
item.setData(WATCHER_NAME,watcher);
item.addListener(SWT.Arm,watcher);
item.addListener(SWT.Selection,watcher);
}
});
}
// removes the given item's watcher-listener
private void removeWatcher(final MenuItem item){
item.getDisplay().syncExec(new Runnable(){
public void run(){
Watcher watcher = (Watcher)item.getData(WATCHER_NAME);
if(watcher!=null) {
item.removeListener(SWT.Arm,watcher);
item.removeListener(SWT.Selection, watcher);
}
}
});
}
private int getNumberOfArmedEvents(List watchers) {
//private int getNumberOfArmedEvents(List <Watcher> watchers) {
int numberOfArmedEvents = 0;
Iterator it = watchers.iterator();
while (it.hasNext()) {
//for (Watcher watcher : watchers) {
Watcher watcher = (Watcher)it.next();
if (watcher.gotArmEvent()) {
numberOfArmedEvents++;
}
}
return numberOfArmedEvents;
}
// tests whether the given item has received the Arm or Selection event
private boolean isArmedOrSelected(final MenuItem item){
Boolean result = (Boolean) Robot.syncExec(item.getDisplay(), new RunnableWithResult() {
public Object runWithResult() {
if (item.isDisposed()) {
return Boolean.valueOf(false);
}
Watcher lWatcher = (Watcher)item.getData(WATCHER_NAME);
return Boolean.valueOf((lWatcher == null) ? false : lWatcher.gotArmEvent());
}
});
return result.booleanValue();
}
/**
* Factory method.
*/
public static MenuItemTester getMenuItemTester() {
return (MenuItemTester)(getTester(MenuItem.class));
}
// /**
// * Get an instrumented <code>MenuItem</code> from its <code>id</code>
// * Because we instrumented it, we assume it not only can be found,
// * but is unique, so we don't even try to catch the *Found exceptions.
// * CONTRACT: instrumented <code>MenuItem</code> must be unique and findable with param.
// */
// public static MenuItem getInstrumentedMenuItem(String id) {
// return getInstrumentedMenuItem(id, null);
// }
//
// /**
// * Get an instrumented <code>MenuItem</code> from its <code>id</code>
// * and the <code>title</code> of its shell (e.g. of the wizard
// * containing it).
// * Because we instrumented it, we assume it not only can be found,
// * but is unique, so we don't even try to catch the *Found exceptions.
// * CONTRACT: instrumented <code>MenuItem</code> must be unique and findable with param.
// */
// public static MenuItem getInstrumentedMenuItem(String id, String title) {
// return getInstrumentedMenuItem(id, title, null);
// }
//
// /**
// * Get an instrumented <code>MenuItem</code> from its
// * <ol>
// * <li><code>id</code></li>
// * <li><code>title</code> of its shell (e.g. of the wizard containing it)</li>
// * <li><code>text</code> that it contains (<code>""</code> if none)</li>
// * </ol>
// * Because we instrumented it, we assume it not only can be found,
// * but is unique, so we don't even try to catch the *Found exceptions.
// * CONTRACT: instrumented <code>MenuItem</code> must be unique and findable with param.
// */
// public static MenuItem getInstrumentedMenuItem(
// String id, String title, String text) {
// return getInstrumentedMenuItem(id, title, text, null);
// }
//
// /**
// * Get an instrumented <code>MenuItem</code> from its
// * <ol>
// * <li><code>id</code></li>
// * <li><code>title</code> of its shell (e.g. of the wizard containing it)</li>
// * <li><code>text</code> that it contains (<code>""</code> if none)</li>
// * <li><code>shell</code> that contains it</li>
// * </ol>
// * Because we instrumented it, we assume it not only can be found,
// * but is unique, so we don't even try to catch the *Found exceptions.
// * CONTRACT: instrumented <code>MenuItem</code> must be unique and findable with param.
// */
// public static MenuItem getInstrumentedMenuItem(
// String id, String title, String text, Shell shell) {
// MenuItem ret = null;
// try {
// ret = catchInstrumentedMenuItem(id, title, text, shell);
// } catch (WidgetNotFoundException nf) {
// Assert.fail("no instrumented MenuItem \"" + id + "\" found");
// } catch (MultipleWidgetsFoundException mf) {
// Assert.fail("many instrumented MenuItems \"" + id + "\" found");
// }
// Assert.assertNotNull("ERROR: null instrumented MenuItem", ret);
// return ret;
// }
//
// /**
// * Get an instrumented <code>MenuItem</code>.
// *
// * Look in its
// * <ol>
// * <li><code>id</code></li>
// * <li><code>title</code> of its shell (e.g. of the wizard containing it)</li>
// * <li><code>text</code> that it contains (<code>""</code> if none)</li>
// * <li><code>shell</code> that contains it</li>
// * </ol>
// * but don't assume it can only be found!
// */
// public static MenuItem catchInstrumentedMenuItem(
// String id, String title, String text, Shell shell)
// throws WidgetNotFoundException, MultipleWidgetsFoundException {
// MenuItem ret = null;
// WidgetFinder finder = BasicFinder.getDefault();
// if (shell==null) {
// try {
// /* try to find the shell */
// shell = (Shell)finder.find(new TextMatcher(title));
// } catch (WidgetNotFoundException e) {
// shell = null;
// } catch (MultipleWidgetsFoundException e) {
// try {
// shell = (Shell) finder.find(new ClassMatcher(Shell.class));
// } catch (WidgetNotFoundException e1) {
// shell = null;
// } catch (MultipleWidgetsFoundException e1) {
// shell = null;
// }
// }
// }
// /* Decide what to search on: first id, then text if id not available */
// Matcher miMatcher;
// if (id!=null) {
// miMatcher = new NameMatcher(id);
// } else {
// miMatcher = new TextMatcher(text);
// }
// try {
// if (shell == null) {
// ret = (MenuItem)finder.find(miMatcher);
// } else {
// ret = (MenuItem)finder.find(shell, miMatcher);
// }
// } catch (WidgetNotFoundException nf) {
// Assert.fail("no instrumented MenuItem \"" + id + "\" found");
// } catch (MultipleWidgetsFoundException mf) {
// Assert.fail("many instrumented MenuItems \"" + id + "\" found");
// }
//
// return ret;
// }
}