/******************************************************************************* * Copyright (c) 2004, 2010 BREDEX GmbH. * 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: * BREDEX GmbH - initial API and implementation and/or initial documentation *******************************************************************************/ package org.eclipse.jubula.rc.swt.driver; import java.util.Iterator; import java.util.List; import org.eclipse.jubula.rc.common.driver.ClickOptions; import org.eclipse.jubula.rc.common.driver.IRunnable; import org.eclipse.jubula.rc.common.exception.StepExecutionException; import org.eclipse.jubula.rc.common.logger.AutServerLogger; import org.eclipse.jubula.rc.swt.listener.ComponentHandler; import org.eclipse.jubula.rc.swt.utils.SwtUtils; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.ToolItem; import org.eclipse.swt.widgets.Widget; /** * This event matcher checks whether a mouse click event matches the * requested properties. The properties are defined by a * <code>ClickOptions</code> instance. * * @author BREDEX GmbH * @created 26.07.2006 */ public class ClickSwtEventMatcher extends DefaultSwtEventMatcher { /** the log */ private static final AutServerLogger LOG = new AutServerLogger(ClickSwtEventMatcher.class); /** * The click options. */ private ClickOptions m_clickOptions; /** * Creates a new matcher which checks SWT events against a mouse event type * that is determined from the given ClickOptions. * @param clickOptions the ClickOptions */ public ClickSwtEventMatcher(ClickOptions clickOptions) { super(getMouseEventId(clickOptions)); m_clickOptions = clickOptions; } /** * Converts the click type to the corresponding AWT event ID. * @param clickOptions The click options. * @return The event ID. */ private static int getMouseEventId(ClickOptions clickOptions) { return (clickOptions.getClickType() == ClickOptions.ClickType.CLICKED) ? SWT.MouseDown : SWT.MouseUp; } /** * {@inheritDoc} */ public boolean isFallBackEventMatching(List eventObjects, final Object graphicsComponent) { // Checks whether we've received a mouse up event. This really only // makes a difference in the case that we're listening for a mouse down // event that never comes rather than the mouse up event that does. // This can happen, for example, when selecting from a TabFolder under // Linux. It may also happen in other situations. Iterator mouseUpIt = eventObjects.iterator(); while (mouseUpIt.hasNext()) { Event event = (Event)mouseUpIt.next(); if (event.widget == graphicsComponent && event.type == SWT.MouseUp) { return true; } } if (isEventlessWidget(graphicsComponent)) { return true; } try { // checks if the component received a mouse-down event from the // right-hand mouse button (open context menu) Iterator eventIt = eventObjects.iterator(); while (eventIt.hasNext()) { Event event = (Event)eventIt.next(); if (event.widget == graphicsComponent && event.button == 3 // right-click && event.type == SWT.MouseDown) { return true; } } // checks if the component is visible (= in hierarchy container) // and if the key-released event occurred if ((ComponentHandler.getAutHierarchy().getHierarchyContainer( (Widget)graphicsComponent) == null) && (m_clickOptions.getClickType() == ClickOptions.ClickType .RELEASED || m_clickOptions.getClickCount() == 0)) { return true; } } catch (IllegalArgumentException e) { if (m_clickOptions.getClickType() == ClickOptions.ClickType.RELEASED || m_clickOptions.getClickCount() == 0) { return true; } } // Check whether the mouse pointer is currently on the very edge of the component // Some components (such as TabFolder) do not send mouse events when a click occurs // on its "border". if (graphicsComponent instanceof Widget && !((Widget)graphicsComponent).isDisposed() && isOnBorder((Widget)graphicsComponent)) { return true; } return false; } /** * * @param graphicsComponent The component to check. * @return <code>true</code> if we cannot expect to receive mouse events * for given component but can reasonably assume that a mouse * click did indeed occur. Otherwise, <code>false</code>. */ private boolean isEventlessWidget(Object graphicsComponent) { // unfortunately we get no events from the MenuBar, // so we have nothing to check! if (graphicsComponent instanceof MenuItem) { return true; // Similar situation with ToolItem. We receive no MouseUp event from // a mouse click on the ToolItem's chevron. } else if (graphicsComponent instanceof ToolItem) { return true; } else if (graphicsComponent instanceof Combo) { // FIXME zeb Must be some way to check if a menu open/close event occurred return true; // We receive no Mouse events on Table and Tree(Table) headers. // This will supposedly be fixed for SWT 3.4. // See http://eclip.se/17871 } else if (graphicsComponent instanceof Table && SwtUtils.isMouseCursorInWidget((Widget)graphicsComponent)) { // Assuming that if a Table or Tree was the target component and // the mouse pointer is currently within the bounds for that Table, // then that is enough confirmation. return true; } return false; } /** * @param graphicsComponent The component to check. * @return <code>true</code> if the mouse cursor is currently directly on * the very edge of the given component. * Otherwise, <code>false</code>. */ private boolean isOnBorder(final Widget graphicsComponent) { try { return new EventThreadQueuerSwtImpl().invokeAndWait("CheckBorderFallbackMatching", new IRunnable<Boolean>() { //$NON-NLS-1$ public Boolean run() throws StepExecutionException { int fuzz = 3; Display d = graphicsComponent.getDisplay(); Rectangle widgetBounds = SwtUtils.getWidgetBounds(graphicsComponent); Point cursorLocation = d.getCursorLocation(); if (widgetBounds.contains(cursorLocation)) { widgetBounds.x += fuzz; widgetBounds.y += fuzz; widgetBounds.width -= (fuzz * 2); widgetBounds.height -= (fuzz * 2); if (!widgetBounds.contains(cursorLocation)) { return Boolean.TRUE; } } return Boolean.FALSE; } }); } catch (Throwable t) { // Since this method is a workaround, it would be unacceptable to // propagate any errors here. Instead, we'll log the problem and // assume that the workaround conditions were not met. LOG.warn("An error occurred during an event-confirmation workaround. The results of the workaround will be ignored.", t); //$NON-NLS-1$ return false; } } }