/*******************************************************************************
* Copyright (c) 2004, 2009 MAKE Technologies Inc and others
* 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:
* MAKE Technologies Inc - initial API and implementation
* Mariot Chauvin <mariot.chauvin@obeo.fr> - refactoring
*******************************************************************************/
package org.eclipse.swtbot.eclipse.gef.finder.widgets;
import java.lang.reflect.Method;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable;
import org.eclipse.swtbot.swt.finder.exceptions.WidgetNotFoundException;
import org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory;
import org.eclipse.swtbot.swt.finder.results.VoidResult;
import org.eclipse.swtbot.swt.finder.utils.MessageFormat;
import org.eclipse.swtbot.swt.finder.utils.internal.Assert;
import org.eclipse.swtbot.swt.finder.widgets.AbstractSWTBot;
import org.hamcrest.Matcher;
/**
* a context menu implementation that finds the menu and invokes it in one UIRunnable so as to avoid the
* disposed-before-clicked issue that occurs within Eclipse.
*
* @author David Green
*
*/
public class SWTBotGefContextMenu extends AbstractSWTBot<Control>{
private final String text;
private final Control control;
public SWTBotGefContextMenu(final Control control, final String text) {
super(control);
this.text = text;
this.control = control;
}
public SWTBotGefContextMenu click() throws WidgetNotFoundException {
final boolean[] clicked = new boolean[1];
// must async exec in case the call opens a dialog
UIThreadRunnable.asyncExec(this.display, new VoidResult() {
public void run() {
Menu menu = control.getMenu();
invokeMenuInternal(menu, WidgetMatcherFactory.withMnemonic(text),clicked, true);
}
});
// use another sync exec to ensure that previous call was processed fully
UIThreadRunnable.syncExec(this.display,new VoidResult() {
public void run() {
// do nothing, just wait for all events to be processed
}
});
if (!clicked[0]) {
throw new WidgetNotFoundException(text);
}
return this;
}
private boolean invokeMenuInternal(final Menu bar, final Matcher<? extends Widget> matcher,final boolean[] clickInitiated, final boolean recursive) {
if (bar != null) {
bar.notifyListeners(SWT.Show, new Event());
try {
MenuItem[] items = bar.getItems();
for (int i = 0; i < items.length; i++) {
MenuItem menuItem = items[i];
if (isSeparator(menuItem)) {
continue;
}
if (matcher.matches(menuItem)) {
clickInitiated[0] = true;
click(menuItem);
return true;
}
if (recursive) {
if (invokeMenuInternal(menuItem.getMenu(), matcher,clickInitiated, recursive)) {
return true;
}
}
}
} finally {
bar.notifyListeners(SWT.Hide, new Event());
}
}
return false;
}
private void click(MenuItem menuItem) {
assertEnabled(menuItem);
int style = menuItem.getStyle();
if (hasStyle(style, SWT.CHECK) || hasStyle(style, SWT.RADIO)) {
menuItem.setSelection(!menuItem.getSelection());
}
Event event = new Event();
event.time = (int) System.currentTimeMillis();
event.widget = menuItem;
event.display = menuItem.getDisplay();
menuItem.notifyListeners(SWT.Selection,event);
}
private boolean hasStyle(int style, int flag) {
return (style & flag) == flag;
}
private boolean isSeparator(MenuItem menuItem) {
// FIXME see https://bugs.eclipse.org/bugs/show_bug.cgi?id=208188
// FIXED > 20071101 https://bugs.eclipse.org/bugs/show_bug.cgi?id=208188#c2
return (menuItem.getStyle() & SWT.SEPARATOR) != 0;
}
private void assertEnabled(MenuItem menuItem) {
Assert.isTrue(isEnabled(menuItem), MessageFormat.format("Widget {0} is not enabled.", this)); //$NON-NLS-1$ //$NON-NLS-2$
}
protected boolean isEnabled(Widget widget) {
try {
Method method = widget.getClass().getMethod("isEnabled");
return (Boolean) method.invoke(widget);
} catch (Exception e) {
return false;
}
}
}