/******************************************************************************* * Copyright (c) 2012 Google, Inc. * 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: * Google, Inc. - initial API and implementation *******************************************************************************/ package com.windowtester.runtime.swt.locator.eclipse; import java.io.Serializable; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IViewReference; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.views.IViewDescriptor; import com.windowtester.runtime.IUIContext; import com.windowtester.runtime.WaitTimedOutException; import com.windowtester.runtime.WidgetNotFoundException; import com.windowtester.runtime.WidgetSearchException; import com.windowtester.runtime.condition.ICondition; import com.windowtester.runtime.condition.IConditionHandler; import com.windowtester.runtime.condition.IsVisible; import com.windowtester.runtime.swt.condition.SWTIdleCondition; import com.windowtester.runtime.swt.internal.condition.eclipse.ViewClosedConditionHandler; import com.windowtester.runtime.swt.internal.condition.eclipse.ViewCondition; import com.windowtester.runtime.swt.internal.condition.eclipse.ViewShowingConditionHandler; import com.windowtester.runtime.swt.internal.condition.eclipse.ViewZoomedConditionHandler; import com.windowtester.runtime.swt.internal.condition.eclipse.ViewCondition.Active; import com.windowtester.runtime.swt.internal.condition.eclipse.ViewCondition.Dirty; import com.windowtester.runtime.swt.internal.display.RunnableWithResult; import com.windowtester.runtime.swt.internal.finder.RetrySupport; import com.windowtester.runtime.swt.internal.finder.eclipse.views.ViewExplorer; import com.windowtester.runtime.swt.internal.finder.eclipse.views.ViewFinder; import com.windowtester.runtime.swt.internal.finder.eclipse.views.ViewFinder.IViewMatcher; import com.windowtester.runtime.swt.internal.locator.ICloseableLocator; import com.windowtester.runtime.swt.internal.matchers.eclipse.ViewComponentMatcher; import com.windowtester.runtime.swt.internal.matchers.eclipse.ViewComponentMatcher.IViewControlProvider; import com.windowtester.runtime.swt.internal.widgets.ISWTWidgetMatcher; import com.windowtester.runtime.swt.locator.SWTWidgetLocator; /** * * Locates eclipse workbench views. * <p> * Views are identified by their <code>view-id</code> which is specified * when the view is contributed to the workbench via the <code>org.eclipse.ui.views</code> * extension point. * * For instance, given that the resource navigator view has an id of "org.eclipse.ui.views.ResourceNavigator", * the locator to identify the tree in the resource navigator could be written like this: * * <pre> * new SWTWidgetLocator(Tree.class, * new ViewLocator("org.eclipse.ui.views.ResourceNavigator"); * </pre> * * @see org.eclipse.ui.IViewPart * */ public class ViewLocator extends SWTWidgetLocator implements IWorkbenchPartLocator, IsVisible { private static final long serialVersionUID = 8106851292164382419L; private final class ViewHelper implements IViewControlProvider, IViewMatcher { private final IViewMatcher viewMatcher; private ViewHelper(IViewMatcher viewMatcher) { this.viewMatcher = viewMatcher; } public Control getViewControl() { return ViewFinder.getViewControl(viewMatcher); } public String getViewLabel() { return viewMatcher.getLabel(); } /* (non-Javadoc) * @see com.windowtester.runtime.swt.internal.finder.eclipse.views.ViewFinder.IViewMatcher#matches(org.eclipse.ui.IViewReference) */ public boolean matches(IViewReference view) { return viewMatcher.matches(view); } /* (non-Javadoc) * @see com.windowtester.runtime.swt.internal.finder.eclipse.views.ViewFinder.IViewMatcher#matches(org.eclipse.ui.IViewPart) */ public boolean matches(IViewPart part) { return viewMatcher.matches(part); } /* (non-Javadoc) * @see com.windowtester.runtime.swt.internal.finder.eclipse.views.ViewFinder.IViewMatcher#getLabel() */ public String getLabel() { return viewMatcher.getLabel(); } /* (non-Javadoc) * @see com.windowtester.runtime.swt.internal.finder.eclipse.views.ViewFinder.IViewMatcher#matches(org.eclipse.ui.views.IViewDescriptor) */ public boolean matches(IViewDescriptor view) { // TODO Auto-generated method stub return viewMatcher.matches(view); } } private static class Closer implements ICloseableLocator, Serializable { private static final long serialVersionUID = 3502312171322974878L; private final transient ViewLocator view; public Closer(ViewLocator view) { this.view = view; } /* (non-Javadoc) * @see com.windowtester.runtime.swt.internal.locator.ICloseableLocator#doClose(com.windowtester.runtime.IUIContext, com.windowtester.runtime.locator.IWidgetReference, com.windowtester.runtime.IClickDescription) */ public void doClose(IUIContext ui) throws WidgetSearchException { view.doClose(ui); } } /** * Closes the given view. * @throws WidgetSearchException */ private void doClose(IUIContext ui) throws WaitTimedOutException, WidgetSearchException { waitForIdle(ui); //There is a race condition here -- the view may be in the process of closing... //ensureViewIsVisible(ui); hideView(); ensureViewIsClosed(ui); } private void ensureViewIsClosed(IUIContext ui) { ui.wait(isVisible(false)); } private void hideView() throws WidgetNotFoundException, WidgetSearchException { final IViewReference reference = ViewFinder.findMatch(viewHelper); if (reference == null) throw new WidgetNotFoundException("View: " + viewHelper.getLabel() + " not found"); final Display display = getDisplay(); final IWorkbenchPage page = getActiveWorkbenchPage(display); if (page == null) throw new WidgetSearchException("unable to find active workbench page"); // notice that this is done as an async in case the view is dirty // and forces a prompt display.asyncExec(new Runnable() { public void run() { page.hideView(reference); } }); } // private void ensureViewIsVisible(final IUIContext ui) throws WidgetSearchException { // RetrySupport.retryUntilArrayResultIsNonEmpty(new RunnableWithResult(){ // public Object runWithResult() { // return ui.findAll(ViewLocator.this); // } // }); // } private void waitForIdle(IUIContext ui) throws WaitTimedOutException { ui.wait(new SWTIdleCondition()); } private Display getDisplay() { return PlatformUI.getWorkbench().getDisplay(); } /** * @param display * @return */ private IWorkbenchPage getActiveWorkbenchPage(final Display display) { return (IWorkbenchPage) RetrySupport.retryUntilResultIsNonNull(new RunnableWithResult() { public Object runWithResult() { final IWorkbenchPage[] page = new IWorkbenchPage[1]; display.syncExec(new Runnable() { public void run() { IWorkbench workbench = PlatformUI.getWorkbench(); if (workbench == null) return; IWorkbenchWindow window = workbench.getActiveWorkbenchWindow(); if (window == null) return; page[0] = window.getActivePage(); } }); return page[0]; } }); } /** The id/name of the target view */ //private final String _viewId; private final transient ViewHelper viewHelper; //view id is cached for serialization purposes private final String viewId; /** * Create an instance that locates the given view. * @param viewId the id of the view to locate */ public ViewLocator(String viewId) { this(ViewFinder.idMatcher(viewId)); //the legacy default } private ViewLocator(final IViewMatcher viewMatcher) { super(Control.class); this.viewHelper = new ViewHelper(viewMatcher); this.viewId = viewMatcher.getLabel(); } /** * Create a new View Locator that identifies views by view name. * @param viewName the view id of the target view * @return a new name-matching View Locator */ public static ViewLocator forName(String viewName) { return new ViewLocator(ViewFinder.nameMatcher(viewName)); } /** * Create a new View Locator that identifies views by view id. * @param viewId the view id of the target view * @return a new id-matching View Locator */ public static ViewLocator forId(String viewId) { return new ViewLocator(ViewFinder.idMatcher(viewId)); } /** * @see java.lang.Object#toString() */ public String toString() { return "ViewLocator ["+ getViewId() +"]"; } /** * Get this view locator's view id. */ public String getViewId() { return viewId; } /* (non-Javadoc) * @see com.windowtester.runtime.swt.locator.SWTWidgetLocator#buildMatcher() */ protected ISWTWidgetMatcher buildMatcher() { return new ViewComponentMatcher(new IViewControlProvider() { public Control getViewControl() { return ViewFinder.getViewControl(viewHelper); } public String getViewLabel() { return getViewId(); } }); // //note we need to adapt the old matcher to the new API // return new AdapterFactory().adapt(new ViewComponentMatcher(new IViewControlProvider() { // public Control getViewControl() { // return ViewFinder.getViewControl(viewHelper); // } // // public String getViewLabel() { // return getViewId(); // } // })); } /* * Name is just the view id... */ public String getNameOrLabel() { return getViewId(); } /* (non-Javadoc) * @see com.windowtester.runtime.swt.locator.SWTWidgetLocator#isVisible(com.windowtester.runtime.IUIContext) */ public boolean isVisible(IUIContext ui) throws WidgetSearchException { return ViewCondition.isVisible(viewHelper).test(); } /* (non-Javadoc) * @see com.windowtester.runtime.WidgetLocator#getAdapter(java.lang.Class) */ @SuppressWarnings("unchecked") public Object getAdapter(Class adapter) { if (adapter == ICloseableLocator.class) return new Closer(this); return super.getAdapter(adapter); } ////////////////////////////////////////////////////////////////////////////// // // Condition factories // ////////////////////////////////////////////////////////////////////////////// public ICondition isActive() { return ViewCondition.isActive(viewHelper); } public ICondition isActive(boolean expected) { Active active = ViewCondition.isActive(viewHelper); if (!expected) return active.not(); return active; } public ICondition isDirty() { return ViewCondition.isDirty(viewHelper); } public ICondition isDirty(boolean expected) { Dirty dirty = ViewCondition.isDirty(viewHelper); if (!expected) return dirty.not(); return dirty; } /** * Create a condition handler that ensures that this view is closed. * * @since 3.7.1 */ public IConditionHandler isClosed() { return ViewClosedConditionHandler.forView(this); } /** * Create a condition handler that ensures that this view is showing. * * @since 3.8.1 */ public IConditionHandler isShowing() { return ViewShowingConditionHandler.forView(this); } /** * Create a condition handler that ensures that this view is zoomed. * * @since 4.0.0 */ public IConditionHandler isZoomed() { return ViewZoomedConditionHandler.forView(this, viewHelper); } /** * Get the associated view reference. * @return the associated view reference (or null) if there is none * @since 3.8.1 */ public IViewDescriptor getDescriptor() { return new ViewExplorer().findMatchInRegistry(viewHelper); } }