/******************************************************************************* * 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.client.ui.rcp.views; import java.util.Iterator; import java.util.List; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.jface.action.GroupMarker; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.viewers.DecoratingLabelProvider; import org.eclipse.jface.viewers.DecorationContext; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IBaseLabelProvider; import org.eclipse.jface.viewers.IDecorationContext; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jubula.client.core.ClientTest; import org.eclipse.jubula.client.core.businessprocess.ITestExecutionEventListener; import org.eclipse.jubula.client.core.businessprocess.ITestResultEventListener; import org.eclipse.jubula.client.core.businessprocess.TestExecutionEvent; import org.eclipse.jubula.client.core.businessprocess.TestExecutionEvent.State; import org.eclipse.jubula.client.core.businessprocess.TestResultBP; import org.eclipse.jubula.client.core.events.DataChangedEvent; import org.eclipse.jubula.client.core.events.DataEventDispatcher; import org.eclipse.jubula.client.core.events.DataEventDispatcher.DataState; import org.eclipse.jubula.client.core.events.DataEventDispatcher.IDataChangedListener; import org.eclipse.jubula.client.core.events.DataEventDispatcher.IProjectLoadedListener; import org.eclipse.jubula.client.core.model.IPersistentObject; import org.eclipse.jubula.client.core.model.IProjectPO; import org.eclipse.jubula.client.core.model.ITestSuitePO; import org.eclipse.jubula.client.core.model.TestResult; import org.eclipse.jubula.client.core.model.TestResultNode; import org.eclipse.jubula.client.core.persistence.GeneralStorage; import org.eclipse.jubula.client.ui.constants.CommandIDs; import org.eclipse.jubula.client.ui.constants.Constants; import org.eclipse.jubula.client.ui.constants.ContextHelpIds; import org.eclipse.jubula.client.ui.editors.TestResultViewer; import org.eclipse.jubula.client.ui.provider.contentprovider.TestResultTreeViewContentProvider; import org.eclipse.jubula.client.ui.provider.labelprovider.TestResultTreeViewLabelProvider; import org.eclipse.jubula.client.ui.rcp.Plugin; import org.eclipse.jubula.client.ui.rcp.constants.RCPCommandIDs; import org.eclipse.jubula.client.ui.rcp.i18n.Messages; import org.eclipse.jubula.client.ui.utils.CommandHelper; import org.eclipse.jubula.client.ui.utils.OpenViewUtils; import org.eclipse.jubula.client.ui.views.IJBPart; import org.eclipse.jubula.client.ui.views.ITreeViewerContainer; import org.eclipse.jubula.client.ui.views.NonSortedPropertySheetPage; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.menus.CommandContributionItem; import org.eclipse.ui.part.ViewPart; import org.eclipse.ui.views.properties.IPropertySheetPage; /** * @author BREDEX GmbH * @created 11.10.2004 */ public class TestResultTreeView extends ViewPart implements ITreeViewerContainer, IJBPart, IDataChangedListener, ITestResultEventListener, ITestExecutionEventListener, IAdaptable, IProjectLoadedListener { /** number of columns = 1 */ private static final int NUM_COLUMNS_1 = 1; /** vertical spacing = 2 */ private static final int VERTICAL_SPACING = 3; /** margin width = 0 */ private static final int MARGIN_WIDTH = 2; /** margin height = 2 */ private static final int MARGIN_HEIGHT = 2; /** react on selection to open log view */ private ISelectionChangedListener m_selectionListener = new OpenViewUtils.TestResultNodeSelectionListener(); /** TreeViewer */ private TreeViewer m_treeViewer; /** the parent composite */ private Composite m_parentComposite; /** * The default constructor. */ public TestResultTreeView() { super(); } /** {@inheritDoc} */ public Object getAdapter(Class adapter) { if (adapter.equals(IPropertySheetPage.class)) { return new NonSortedPropertySheetPage(); } return super.getAdapter(adapter); } /** * {@inheritDoc} */ public void createPartControl(Composite parent) { m_parentComposite = parent; GridLayout layout = new GridLayout(); layout.numColumns = NUM_COLUMNS_1; layout.verticalSpacing = VERTICAL_SPACING; layout.marginWidth = MARGIN_WIDTH; layout.marginHeight = MARGIN_HEIGHT; parent.setLayout(layout); Composite composite = new Composite(parent, SWT.NONE); GridLayout compLayout = new GridLayout(NUM_COLUMNS_1, false); compLayout.marginWidth = 0; compLayout.marginHeight = 0; composite.setLayout(compLayout); GridData gridData = new GridData (GridData.HORIZONTAL_ALIGN_FILL | GridData.FILL_BOTH); composite.setLayoutData(gridData); setTreeViewer(new TreeViewer(composite)); getTreeViewer().setContentProvider( new TestResultTreeViewContentProvider()); getTreeViewer().setLabelProvider(new DecoratingLabelProvider( new TestResultTreeViewLabelProvider(), Plugin.getDefault() .getWorkbench().getDecoratorManager().getLabelDecorator())); ClientTest.instance().addTestExecutionEventListener(this); getTreeViewer().setUseHashlookup(true); getTreeViewer().setInput(getInput()); getTreeViewer().expandToLevel(0); addTreeListener(); GridData layoutData = new GridData(); layoutData.grabExcessHorizontalSpace = true; layoutData.horizontalAlignment = GridData.FILL; layoutData = new GridData(); layoutData.grabExcessHorizontalSpace = true; layoutData.grabExcessVerticalSpace = true; layoutData.horizontalAlignment = GridData.FILL; layoutData.verticalAlignment = GridData.FILL; Plugin.getHelpSystem().setHelp(getTreeViewer().getControl(), ContextHelpIds.RESULT_TREE_VIEW); getTreeViewer().getControl().setLayoutData(layoutData); getSite().setSelectionProvider(getTreeViewer()); createContextMenu(); final DataEventDispatcher ded = DataEventDispatcher.getInstance(); ded.addDataChangedListener(this, true); ded.addProjectLoadedListener(this, true); getTreeViewer().addSelectionChangedListener(m_selectionListener); } /** * Create context menu. */ private void createContextMenu() { // Create menu manager. MenuManager menuMgr = new MenuManager(); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager mgr) { fillContextMenu(mgr); } }); // Create menu. Menu menu = menuMgr.createContextMenu(getTreeViewer().getControl()); getTreeViewer().getControl().setMenu(menu); // Register menu for extension. getViewSite().registerContextMenu(menuMgr, getTreeViewer()); } /** * {@inheritDoc} */ public void dispose() { ClientTest.instance() .removeTestExecutionEventListener(this); DataEventDispatcher.getInstance().removeProjectLoadedListener(this); DataEventDispatcher.getInstance().removeDataChangedListener(this); getSite().setSelectionProvider(null); super.dispose(); getTreeViewer().removeSelectionChangedListener(m_selectionListener); } /** * Fills the context menu. * @param mgr IMenuManager */ protected void fillContextMenu(IMenuManager mgr) { mgr.add(CommandHelper.createContributionItem( RCPCommandIDs.FIND, null, Messages.FindContextMenu, CommandContributionItem.STYLE_PUSH)); CommandHelper.createContributionPushItem(mgr, CommandIDs.OPEN_SPECIFICATION_COMMAND_ID); CommandHelper.createContributionPushItem(mgr, CommandIDs.SHOW_SPECIFICATION_COMMAND_ID); CommandHelper.createContributionPushItem(mgr, CommandIDs.EXPAND_TREE_ITEM_COMMAND_ID); mgr.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS)); } /** * {@inheritDoc} */ public void setFocus() { getTreeViewer().getControl().setFocus(); Plugin.showStatusLine(this); } /** * @return The parent composite of this workbench part. */ public Composite getParentComposite() { return m_parentComposite; } /** * Clears the view/tree. */ public void clear() { Plugin.getDisplay().syncExec(new Runnable() { public void run() { getTreeViewer().setInput(null); getTreeViewer().refresh(); } }); } /** * refreshes the content, if needed */ public void checkContent() { if (getTreeViewer().getInput() != null && !((TestResultNode)getTreeViewer().getInput()). equals(TestResultBP.getInstance().getResultTestModel())) { getTreeViewer().setInput(getInput()); getTreeViewer().refresh(); } } /** * {@inheritDoc} */ public void stateChanged(TestExecutionEvent event) { if (event.getState() == State.TEST_EXEC_RESULT_TREE_READY) { putDecorationContextProperty( TestResultViewer.DECORATION_CONTEXT_SUITE_END_TIME_ID, null); Display.getDefault().syncExec(new Runnable() { public void run() { if (!getTreeViewer().getControl().isDisposed()) { TestResult oldInput = ((TestResult) getTreeViewer().getInput()); if (oldInput != null) { removeListenerFrom(oldInput.getRootResultNode()); } getTreeViewer().getTree().setRedraw(false); getTreeViewer().setInput(getInput()); TestResult newInput = ((TestResult) getTreeViewer().getInput()); if (newInput != null) { addListenerTo(newInput.getRootResultNode()); } else { return; } getTreeViewer().expandToLevel(1); if (Plugin.getDefault().getPreferenceStore() .getBoolean(Constants.TRACKRESULTS_KEY)) { TestResult input = (TestResult)getTreeViewer().getInput(); if (input != null) { ISelection selection = new StructuredSelection( input.getRootResultNode()); getTreeViewer().setSelection(selection); } } getTreeViewer().getTree().setRedraw(true); } } }); } } /** * the initial model * @return TestResultNode rootNode */ protected TestResult getInput() { return TestResultBP.getInstance().getResultTestModel(); } /** * should be called when a test result is changed * @param resultNode The added event */ public void testResultChanged(final TestResultNode resultNode) { if (!getTreeViewer().getControl().isDisposed()) { Display.getDefault().syncExec(new Runnable() { public void run() { refreshNode(resultNode); if (resultNode != null && (Plugin.getDefault() .getPreferenceStore() .getBoolean(Constants.TRACKRESULTS_KEY) || (resultNode.getNode() instanceof ITestSuitePO))) { getTreeViewer().reveal(resultNode); } } }); } } /** * refreshes a Node * @param findNode TestResultNodeGUI */ protected void refreshNode(final TestResultNode findNode) { if (Plugin.getDefault().getPreferenceStore().getBoolean( Constants.TRACKRESULTS_KEY)) { getTreeViewer().expandToLevel(findNode, 1); } getTreeViewer().refresh(findNode); } /** * Adds a Test Result. * @param parent TestResultNode * @param index int * @param newNode TestResultNode */ public void testResultNodeUpdated(final TestResultNode parent, final int index, final TestResultNode newNode) { addListenerTo(newNode); if (!getTreeViewer().getControl().isDisposed()) { Display.getDefault().syncExec(new Runnable() { public void run() { // Refreshing the parent allows Event Handlers to be // displayed as they are being executed, rather than having // to wait until the end of the test. if (parent != null) { refreshNode(parent); } refreshNode(newNode); } }); } } /** * {@inheritDoc} */ public void endTestExecution() { TestResult input = getInput(); if (input != null) { putDecorationContextProperty( TestResultViewer.DECORATION_CONTEXT_SUITE_END_TIME_ID, input.getEndTime()); } Display.getDefault().syncExec(new Runnable() { public void run() { if (!getTreeViewer().getControl().isDisposed()) { if (getTreeViewer().getInput() != null) { ISelection selection = new StructuredSelection(((TestResult) getTreeViewer().getInput()) .getRootResultNode()); getTreeViewer().setSelection(null); getTreeViewer().setSelection(selection); } getTreeViewer().refresh(); } } }); } /** * adds listener to all resultObjects in resultTestSuite * @param resultNode the resultNode object to add listener */ void addListenerTo(TestResultNode resultNode) { List resultNodeList = resultNode.getResultNodeList(); Iterator it = resultNodeList.iterator(); while (it.hasNext()) { TestResultNode resNode = (TestResultNode)it.next(); resNode.addTestResultChangedListener(this); addListenerTo(resNode); } } /** * Removes the listener from all resultObjects in resultTestSuite * @param resultNode the resultNode object to remove listener from */ public void removeListenerFrom(TestResultNode resultNode) { List resultNodeList = resultNode.getResultNodeList(); Iterator it = resultNodeList.iterator(); while (it.hasNext()) { TestResultNode resTest = (TestResultNode)it.next(); resTest.removeTestResultChangedListener(this); removeListenerFrom(resTest); } } /** {@inheritDoc} */ public void handleDataChanged(DataChangedEvent... events) { for (DataChangedEvent e : events) { handleDataChanged(e.getPo(), e.getDataState()); } } /** * {@inheritDoc} */ public void handleDataChanged(final IPersistentObject po, final DataState dataState) { Display.getDefault().asyncExec(new Runnable() { public void run() { switch (dataState) { case Added: if (po instanceof IProjectPO) { clear(); } break; case Deleted: if (po instanceof IProjectPO) { IProjectPO project = GeneralStorage.getInstance().getProject(); if (project != null && project.equals(po)) { clear(); } } break; case Renamed: break; case StructureModified: break; default: break; } } }); } /** * Adds DoubleClick-Support to Treeview. Adds SelectionChanged-Support to * TreeView. */ private void addTreeListener() { getTreeViewer().addDoubleClickListener(new IDoubleClickListener() { @SuppressWarnings("synthetic-access") public void doubleClick(DoubleClickEvent event) { handleDClick(); } }); } /** * handles a DClick on the tree */ private void handleDClick() { CommandHelper.executeCommand( CommandIDs.SHOW_SPECIFICATION_COMMAND_ID, getSite()); } /** * {@inheritDoc} */ public void handleProjectLoaded() { clear(); } /** * @param treeViewer the treeViewer to set */ private void setTreeViewer(TreeViewer treeViewer) { m_treeViewer = treeViewer; } /** * @return the treeViewer */ public TreeViewer getTreeViewer() { return m_treeViewer; } /** * Attempts a {@link DecorationContext#putProperty(String, Object)} with the * given arguments and the current viewer. Fails silently if any objects are * of incorrect type (e.g. label provider not a * {@link DecoratingLabelProvider}, decorating context not a * {@link DecorationContext}, etc). * * @see DecorationContext#putProperty(String, Object) * @param property The property. * @param value The value of the property or <code>null</code> if the * property is to be removed. */ private void putDecorationContextProperty(String property, Object value) { TreeViewer viewer = getTreeViewer(); if (viewer != null) { IBaseLabelProvider labelProvider = viewer.getLabelProvider(); if (labelProvider instanceof DecoratingLabelProvider) { IDecorationContext context = ((DecoratingLabelProvider)labelProvider) .getDecorationContext(); if (context instanceof DecorationContext) { ((DecorationContext)context).putProperty( property, value); } } } } @Override public void receiveExecutionNotification(String notification) { // nothing } }