You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@taverna.apache.org by re...@apache.org on 2015/03/26 19:52:35 UTC

[46/51] [partial] incubator-taverna-workbench git commit: all packages are moved to org.apache.taverna.*

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/a9a52bd5/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigUIFactory.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigUIFactory.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigUIFactory.java
deleted file mode 100644
index 8746b54..0000000
--- a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/config/ServiceDescriptionConfigUIFactory.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*******************************************************************************
- * Copyright (C) 2007 The University of Manchester
- *
- *  Modifications to the initial code base are copyright of their
- *  respective authors, or their employers as appropriate.
- *
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Lesser General Public License
- *  as published by the Free Software Foundation; either version 2.1 of
- *  the License, or (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful, but
- *  WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Lesser General Public License for more details.
- *
- *  You should have received a copy of the GNU Lesser General Public
- *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- ******************************************************************************/
-package net.sf.taverna.t2.workbench.ui.servicepanel.config;
-
-import javax.swing.JPanel;
-
-import uk.org.taverna.configuration.Configurable;
-import uk.org.taverna.configuration.ConfigurationUIFactory;
-
-import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
-import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionsConfiguration;
-
-public class ServiceDescriptionConfigUIFactory implements ConfigurationUIFactory {
-	private ServiceDescriptionsConfiguration serviceDescriptionsConfiguration;
-	private ServiceDescriptionRegistry serviceDescriptionRegistry;
-
-	@Override
-	public boolean canHandle(String uuid) {
-		return uuid.equals(serviceDescriptionsConfiguration.getUUID());
-	}
-
-	@Override
-	public Configurable getConfigurable() {
-		return serviceDescriptionsConfiguration;
-	}
-
-	@Override
-	public JPanel getConfigurationPanel() {
-		return new ServiceDescriptionConfigPanel(serviceDescriptionsConfiguration, serviceDescriptionRegistry);
-	}
-
-	public void setServiceDescriptionRegistry(ServiceDescriptionRegistry serviceDescriptionRegistry) {
-		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
-	}
-
-	public void setServiceDescriptionsConfiguration(ServiceDescriptionsConfiguration serviceDescriptionsConfiguration) {
-		this.serviceDescriptionsConfiguration = serviceDescriptionsConfiguration;
-	}
-}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/a9a52bd5/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/menu/AddServiceProviderMenu.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/menu/AddServiceProviderMenu.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/menu/AddServiceProviderMenu.java
deleted file mode 100644
index f975778..0000000
--- a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/menu/AddServiceProviderMenu.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*******************************************************************************
- * Copyright (C) 2007 The University of Manchester
- *
- *  Modifications to the initial code base are copyright of their
- *  respective authors, or their employers as appropriate.
- *
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Lesser General Public License
- *  as published by the Free Software Foundation; either version 2.1 of
- *  the License, or (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful, but
- *  WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Lesser General Public License for more details.
- *
- *  You should have received a copy of the GNU Lesser General Public
- *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- ******************************************************************************/
-package net.sf.taverna.t2.workbench.ui.servicepanel.menu;
-
-import java.awt.Component;
-import java.awt.event.ActionEvent;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-import javax.swing.AbstractAction;
-import javax.swing.JButton;
-import javax.swing.JPopupMenu;
-
-import net.sf.taverna.t2.servicedescriptions.ConfigurableServiceProvider;
-import net.sf.taverna.t2.servicedescriptions.ServiceDescription;
-import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionProvider;
-import net.sf.taverna.t2.servicedescriptions.ServiceDescriptionRegistry;
-import net.sf.taverna.t2.workbench.ui.servicepanel.ServicePanel;
-import net.sf.taverna.t2.workbench.ui.servicepanel.actions.AddServiceProviderAction;
-
-/**
- * A menu that provides a set up menu actions for adding new service providers
- * to the Service Panel.
- * <p>
- * The Actions are discovered from the {@link ServiceDescriptionProvider}s found
- * through the SPI.
- *
- * @author Stuart Owen
- * @author Stian Soiland-Reyes
- * @author Alan R Williams
- *
- * @see ServiceDescription
- * @see ServicePanel
- * @see ServiceDescriptionRegistry#addServiceDescriptionProvider(ServiceDescriptionProvider)
- */
-@SuppressWarnings("serial")
-public class AddServiceProviderMenu extends JButton {
-	public static class ServiceProviderComparator implements
-			Comparator<ServiceDescriptionProvider> {
-		@Override
-		public int compare(ServiceDescriptionProvider o1,
-				ServiceDescriptionProvider o2) {
-			return o1.getName().toLowerCase().compareTo(
-					o2.getName().toLowerCase());
-		}
-	}
-
-	private final static String ADD_SERVICE_PROVIDER_MENU_NAME = "Import new services";
-
-	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
-
-	public AddServiceProviderMenu(ServiceDescriptionRegistry serviceDescriptionRegistry) {
-		super();
-		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
-
-		final Component c = createCustomComponent();
-		setAction(new AbstractAction(ADD_SERVICE_PROVIDER_MENU_NAME) {
-			@Override
-			public void actionPerformed(ActionEvent e) {
-				((JPopupMenu) c).show(AddServiceProviderMenu.this, 0,
-						AddServiceProviderMenu.this.getHeight());
-			}
-		});
-	}
-
-	private Component createCustomComponent() {
-		JPopupMenu addServiceMenu = new JPopupMenu(
-				ADD_SERVICE_PROVIDER_MENU_NAME);
-		addServiceMenu.setToolTipText("Add a new service provider");
-		boolean isEmpty = true;
-		List<ConfigurableServiceProvider> providers = new ArrayList<>(
-				serviceDescriptionRegistry.getUnconfiguredServiceProviders());
-		Collections.sort(providers,  new ServiceProviderComparator());
-		for (ConfigurableServiceProvider provider : providers) {
-			/*
-			 * Skip BioCatalogue's ConfigurableServiceProviderS as they should
-			 * not be used to add servcie directlry but rather though the
-			 * Service Catalogue perspective
-			 */
-			if (provider.getId().toLowerCase().contains("servicecatalogue"))
-				continue;
-
-			AddServiceProviderAction addAction = new AddServiceProviderAction(
-					provider, this);
-			addAction.setServiceDescriptionRegistry(serviceDescriptionRegistry);
-			addServiceMenu.add(addAction);
-			isEmpty = false;
-		}
-		if (isEmpty)
-			addServiceMenu.setEnabled(false);
-		return addServiceMenu;
-	}
-}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/a9a52bd5/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/Filter.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/Filter.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/Filter.java
deleted file mode 100644
index e67e8f5..0000000
--- a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/Filter.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*******************************************************************************
- * Copyright (C) 2007-2009 The University of Manchester   
- * 
- *  Modifications to the initial code base are copyright of their
- *  respective authors, or their employers as appropriate.
- * 
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Lesser General Public License
- *  as published by the Free Software Foundation; either version 2.1 of
- *  the License, or (at your option) any later version.
- *    
- *  This program is distributed in the hope that it will be useful, but
- *  WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Lesser General Public License for more details.
- *    
- *  You should have received a copy of the GNU Lesser General Public
- *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- ******************************************************************************/
-package net.sf.taverna.t2.workbench.ui.servicepanel.tree;
-
-import javax.swing.tree.DefaultMutableTreeNode;
-
-public interface Filter {
-	boolean pass(DefaultMutableTreeNode node);
-
-	String filterRepresentation(String original);
-
-	void setSuperseded(boolean superseded);
-
-	boolean isSuperseded();
-}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/a9a52bd5/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeCellRenderer.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeCellRenderer.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeCellRenderer.java
deleted file mode 100644
index 21f43c5..0000000
--- a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeCellRenderer.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*******************************************************************************
- * Copyright (C) 2007-2009 The University of Manchester   
- * 
- *  Modifications to the initial code base are copyright of their
- *  respective authors, or their employers as appropriate.
- * 
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Lesser General Public License
- *  as published by the Free Software Foundation; either version 2.1 of
- *  the License, or (at your option) any later version.
- *    
- *  This program is distributed in the hope that it will be useful, but
- *  WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Lesser General Public License for more details.
- *    
- *  You should have received a copy of the GNU Lesser General Public
- *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- ******************************************************************************/
-package net.sf.taverna.t2.workbench.ui.servicepanel.tree;
-
-import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.folderClosedIcon;
-import static net.sf.taverna.t2.workbench.icons.WorkbenchIcons.folderOpenIcon;
-
-import java.awt.Component;
-
-import javax.swing.JTree;
-import javax.swing.tree.DefaultTreeCellRenderer;
-
-@SuppressWarnings("serial")
-public class FilterTreeCellRenderer extends DefaultTreeCellRenderer {
-	private Filter filter = null;
-
-	@Override
-	public Component getTreeCellRendererComponent(JTree tree, Object value,
-			boolean sel, boolean expanded, boolean leaf, int row,
-			boolean hasFocus) {
-
-		super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf,
-				row, hasFocus);
-		Filter filter = getFilter();
-		if (filter != null)
-			setText(filter.filterRepresentation(getText()));
-		if (expanded)
-			setIcon(folderOpenIcon);
-		else
-			setIcon(folderClosedIcon);
-		return this;
-	}
-
-	public Filter getFilter() {
-		return filter;
-	}
-
-	public void setFilter(Filter currentFilter) {
-		this.filter = currentFilter;
-	}
-}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/a9a52bd5/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeModel.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeModel.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeModel.java
deleted file mode 100644
index 191ed66..0000000
--- a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeModel.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*******************************************************************************
- * Copyright (C) 2007-2009 The University of Manchester   
- * 
- *  Modifications to the initial code base are copyright of their
- *  respective authors, or their employers as appropriate.
- * 
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Lesser General Public License
- *  as published by the Free Software Foundation; either version 2.1 of
- *  the License, or (at your option) any later version.
- *    
- *  This program is distributed in the hope that it will be useful, but
- *  WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Lesser General Public License for more details.
- *    
- *  You should have received a copy of the GNU Lesser General Public
- *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- ******************************************************************************/
-package net.sf.taverna.t2.workbench.ui.servicepanel.tree;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.swing.tree.DefaultTreeModel;
-import javax.swing.tree.TreePath;
-
-import org.apache.log4j.Logger;
-
-public final class FilterTreeModel extends DefaultTreeModel {
-	private static final long serialVersionUID = -8931308369832839862L;
-	private static final Logger logger = Logger
-			.getLogger(FilterTreeModel.class);
-	
-	Filter currentFilter;
-
-	public FilterTreeModel(FilterTreeNode node) {
-		this(node, null);
-	}
-	
-	public FilterTreeModel(FilterTreeNode node, Filter filter) {
-		super(node);
-		currentFilter = filter;
-		node.setFilter(filter);
-	}
-
-	public void setFilter(Filter filter) {
-		if (root != null) {
-			currentFilter = filter;
-			((FilterTreeNode) root).setFilter(filter);
-			Object[] path = { root };
-			fireTreeStructureChanged(this, path, null, null);
-		}
-	}
-
-	@Override
-	public int getChildCount(Object parent) {
-		if (parent instanceof FilterTreeNode)
-			return (((FilterTreeNode) parent).getChildCount());
-		return 0;
-	}
-
-	@Override
-	public Object getChild(Object parent, int index) {
-		if (parent instanceof FilterTreeNode)
-			return (((FilterTreeNode) parent).getChildAt(index));
-		return null;
-	}
-
-	/**
-	 * @return the currentFilter
-	 */
-	public Filter getCurrentFilter() {
-		return currentFilter;
-	}
-
-	public TreePath getTreePathForObjectPath(List<Object> path) {
-		List<FilterTreeNode> resultList = new ArrayList<>();
-		FilterTreeNode current = (FilterTreeNode) root;
-		resultList.add(current);
-		for (int i = 1; (i < path.size()) && (current != null); i++) {
-			logger.debug("Looking in " + current.getUserObject() + " for " + path.get(i));
-			current = current.getChildForObject(path.get(i));
-			if (current != null)
-				resultList.add(current);
-		}
-		if (current != null)
-			return new TreePath(resultList.toArray());
-		return null;
-	}
-}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/a9a52bd5/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeNode.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeNode.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeNode.java
deleted file mode 100644
index 83fd439..0000000
--- a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeNode.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*******************************************************************************
- * Copyright (C) 2007-2009 The University of Manchester   
- * 
- *  Modifications to the initial code base are copyright of their
- *  respective authors, or their employers as appropriate.
- * 
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Lesser General Public License
- *  as published by the Free Software Foundation; either version 2.1 of
- *  the License, or (at your option) any later version.
- *    
- *  This program is distributed in the hope that it will be useful, but
- *  WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Lesser General Public License for more details.
- *    
- *  You should have received a copy of the GNU Lesser General Public
- *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- ******************************************************************************/
-package net.sf.taverna.t2.workbench.ui.servicepanel.tree;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.swing.tree.DefaultMutableTreeNode;
-
-import org.apache.log4j.Logger;
-
-public class FilterTreeNode extends DefaultMutableTreeNode {
-	private static final long serialVersionUID = 1933553584349932151L;
-	@SuppressWarnings("unused")
-	private static Logger logger = Logger.getLogger(FilterTreeNode.class);
-	
-	private Filter filter;
-	private boolean passed = true;
-	private List<FilterTreeNode> filteredChildren = new ArrayList<>();
-
-	public FilterTreeNode(Object userObject) {
-		super(userObject);
-		userObject.toString();
-	}
-
-	public Filter getFilter() {
-		return filter;
-	}
-	
-	public void setFilter(Filter filter) {
-		if ((filter == null) || !filter.isSuperseded()) {
-			this.filter = filter;
-			passed = false;
-			filteredChildren.clear();
-			if (filter == null) {
-				passed = true;
-				passFilterDown(null);
-			} else if (filter.pass(this)) {
-				passed = true;
-				passFilterDown(null);
-			} else {
-				passFilterDown(filter);
-				passed = filteredChildren.size() != 0;
-			}
-		}
-	}
-
-	private void passFilterDown(Filter filter) {
-		int realChildCount = super.getChildCount();
-		for (int i = 0; i < realChildCount; i++) {
-			FilterTreeNode realChild = (FilterTreeNode) super.getChildAt(i);
-			realChild.setFilter(filter);
-			if (realChild.isPassed())
-				filteredChildren.add(realChild);
-		}
-	}
-
-	public void add(FilterTreeNode node) {
-		super.add(node);
-		node.setFilter(filter);
-		// TODO work up
-		if (node.isPassed())
-			filteredChildren.add(node);
-	}
-	
-	@Override
-	public void remove(int childIndex) {
-		if (filter != null)
-			// as child indexes might be inconsistent..
-			throw new IllegalStateException("Can't remove while the filter is active");
-		super.remove(childIndex);
-	}
-
-	@Override
-	public int getChildCount() {
-		if (filter == null)
-			return super.getChildCount();
-		return filteredChildren.size();
-	}
-
-	@Override
-	public FilterTreeNode getChildAt(int index) {
-		if (filter == null)
-			return (FilterTreeNode) super.getChildAt(index);
-		return filteredChildren.get(index);
-	}
-
-	public boolean isPassed() {
-		return passed;
-	}
-	
-	public Set<FilterTreeNode> getLeaves() {
-		Set<FilterTreeNode> result = new HashSet<>();
-		if (super.getChildCount() == 0) {
-			result.add(this);
-			return result;
-		}
-
-		for (int i = 0; i < super.getChildCount(); i++) {
-			FilterTreeNode child = (FilterTreeNode) super.getChildAt(i);
-			result.addAll(child.getLeaves());
-		}
-		return result;
-	}
-
-	public FilterTreeNode getChildForObject(Object userObject) {
-		FilterTreeNode result = null;
-		for (int i=0; (i < super.getChildCount()) && (result == null); i++) {
-			FilterTreeNode child = (FilterTreeNode) super.getChildAt(i);
-			Object nodeObject = child.getUserObject();
-//			logger.info("nodeObject is a " + nodeObject.getClass() + " - " +
-//					"userObject is a " + userObject.getClass());
-			if (nodeObject.toString().equals(userObject.toString())) {
-				result = child;
-//				logger.info(nodeObject + " is equal to " + userObject);
-//			} else {
-//				logger.info(nodeObject + " is not equal to " + userObject);
-			}
-		}
-		return result;
-	}
-}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/a9a52bd5/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeSelectionModel.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeSelectionModel.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeSelectionModel.java
deleted file mode 100644
index a5adfe9..0000000
--- a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/FilterTreeSelectionModel.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*******************************************************************************
- * Copyright (C) 2007 The University of Manchester   
- * 
- *  Modifications to the initial code base are copyright of their
- *  respective authors, or their employers as appropriate.
- * 
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Lesser General Public License
- *  as published by the Free Software Foundation; either version 2.1 of
- *  the License, or (at your option) any later version.
- *    
- *  This program is distributed in the hope that it will be useful, but
- *  WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Lesser General Public License for more details.
- *    
- *  You should have received a copy of the GNU Lesser General Public
- *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- ******************************************************************************/
-package net.sf.taverna.t2.workbench.ui.servicepanel.tree;
-
-import javax.swing.tree.DefaultTreeSelectionModel;
-import javax.swing.tree.TreePath;
-import javax.swing.tree.TreeSelectionModel;
-
-public class FilterTreeSelectionModel extends DefaultTreeSelectionModel{
-	private static final long serialVersionUID = 3127644524735089630L;
-	
-	public FilterTreeSelectionModel(){
-		super();
-		setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
-	}
-	
-	@Override
-	public void setSelectionPath(TreePath path) {
-		/*
-		 * Nothing happens here - only calls to mySetSelectionPath() will have
-		 * the effect of a node being selected.
-		 */
-	}
-	
-	public void mySetSelectionPath(TreePath path) {
-		super.setSelectionPath(path);
-	}
-}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/a9a52bd5/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/MyFilter.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/MyFilter.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/MyFilter.java
deleted file mode 100644
index 8baa0eb..0000000
--- a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/MyFilter.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*******************************************************************************
- * Copyright (C) 2007-2009 The University of Manchester   
- * 
- *  Modifications to the initial code base are copyright of their
- *  respective authors, or their employers as appropriate.
- * 
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Lesser General Public License
- *  as published by the Free Software Foundation; either version 2.1 of
- *  the License, or (at your option) any later version.
- *    
- *  This program is distributed in the hope that it will be useful, but
- *  WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Lesser General Public License for more details.
- *    
- *  You should have received a copy of the GNU Lesser General Public
- *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- ******************************************************************************/
-package net.sf.taverna.t2.workbench.ui.servicepanel.tree;
-
-import javax.swing.tree.DefaultMutableTreeNode;
-
-public class MyFilter implements Filter {
-	private static final String HTML_MATCH_END = "</font><font color=\"black\">";
-	private static final String HTML_MATCH_START = "</font><font color=\"red\">";
-	private static final String HTML_POSTFIX = "</font></html>";
-	private static final String HTML_PREFIX = "<html><font color=\"black\">";
-
-	private String filterString;
-	private boolean superseded;
-	private String filterLowerCase;
-
-	public MyFilter(String filterString) {
-		this.filterString = filterString;
-		this.filterLowerCase = filterString.toLowerCase();
-		this.superseded = false;
-	}
-
-	private boolean basicFilter(DefaultMutableTreeNode node) {
-		if (filterString.isEmpty())
-			return true;
-		return node.getUserObject().toString().toLowerCase()
-				.contains(filterLowerCase);
-	}
-
-	@Override
-	public boolean pass(DefaultMutableTreeNode node) {
-		return basicFilter(node);
-	}
-
-	@Override
-	public String filterRepresentation(String original) {
-		StringBuilder sb = new StringBuilder(HTML_PREFIX);
-		int from = 0;
-		String originalLowerCase = original.toLowerCase();
-		int index = originalLowerCase.indexOf(filterLowerCase, from);
-		while (index > -1) {
-			sb.append(original.substring(from, index));
-			sb.append(HTML_MATCH_START);
-			sb.append(original.substring(index,
-					index + filterLowerCase.length()));
-			sb.append(HTML_MATCH_END);
-			from = index + filterLowerCase.length();
-			index = originalLowerCase.indexOf(filterLowerCase, from);
-		}
-		if (from < original.length())
-			sb.append(original.substring(from, original.length()));
-		return sb.append(HTML_POSTFIX).toString();
-	}
-
-	/**
-	 * @return the superseded
-	 */
-	@Override
-	public boolean isSuperseded() {
-		return superseded;
-	}
-
-	/**
-	 * @param superseded
-	 *            the superseded to set
-	 */
-	@Override
-	public void setSuperseded(boolean superseded) {
-		this.superseded = superseded;
-	}
-}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/a9a52bd5/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/TreePanel.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/TreePanel.java b/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/TreePanel.java
deleted file mode 100644
index 46eca53..0000000
--- a/taverna-activity-palette-ui/src/main/java/net/sf/taverna/t2/workbench/ui/servicepanel/tree/TreePanel.java
+++ /dev/null
@@ -1,371 +0,0 @@
-/*******************************************************************************
- * Copyright (C) 2007-2009 The University of Manchester
- *
- *  Modifications to the initial code base are copyright of their
- *  respective authors, or their employers as appropriate.
- *
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Lesser General Public License
- *  as published by the Free Software Foundation; either version 2.1 of
- *  the License, or (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful, but
- *  WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  Lesser General Public License for more details.
- *
- *  You should have received a copy of the GNU Lesser General Public
- *  License along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- ******************************************************************************/
-package net.sf.taverna.t2.workbench.ui.servicepanel.tree;
-
-import static java.awt.BorderLayout.CENTER;
-import static java.awt.BorderLayout.NORTH;
-import static java.awt.BorderLayout.WEST;
-import static java.awt.Color.GRAY;
-import static java.awt.GridBagConstraints.HORIZONTAL;
-import static java.awt.GridBagConstraints.NONE;
-import static javax.swing.SwingUtilities.invokeLater;
-import static net.sf.taverna.t2.lang.ui.EdgeLineBorder.TOP;
-
-import java.awt.BorderLayout;
-import java.awt.Component;
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.KeyAdapter;
-import java.awt.event.KeyEvent;
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.Timer;
-import java.util.TimerTask;
-
-import javax.swing.JButton;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JTextField;
-import javax.swing.JTree;
-import javax.swing.border.CompoundBorder;
-import javax.swing.border.EmptyBorder;
-import javax.swing.event.TreeExpansionEvent;
-import javax.swing.event.TreeExpansionListener;
-import javax.swing.tree.TreeCellRenderer;
-import javax.swing.tree.TreePath;
-
-import net.sf.taverna.t2.lang.ui.EdgeLineBorder;
-
-import org.apache.log4j.Logger;
-
-@SuppressWarnings("serial")
-public abstract class TreePanel extends JPanel {
-	private static int MAX_EXPANSION = 100;
-	private static final int SEARCH_WIDTH = 15;
-	private static Logger logger = Logger.getLogger(TreePanel.class);
-
-	protected Set<List<Object>> expandedPaths = new HashSet<>();
-	protected FilterTreeModel filterTreeModel;
-	protected JTextField searchField = new JTextField(SEARCH_WIDTH);
-	protected JTree tree = new JTree();
-	protected JScrollPane treeScrollPane;
-
-	private String availableObjectsString = "";
-	private String matchingObjectsString = "";
-	private String noMatchingObjectsString = "";
-
-	private TreeExpandCollapseListener treeExpandListener = new TreeExpandCollapseListener();
-	private Object filterLock = new Object();
-
-	public TreePanel(FilterTreeModel treeModel) {
-		filterTreeModel = treeModel;
-	}
-
-	public void expandTreePaths() throws InterruptedException,
-			InvocationTargetException {
-//		Filter appliedFilter = filterTreeModel.getCurrentFilter();
-//		if (appliedFilter == null) {
-			for (int i = 0; (i < tree.getRowCount()) && (i < MAX_EXPANSION); i++)
-				tree.expandRow(i);
-//		} else {
-//			boolean rowsFinished = false;
-//			for (int i = 0; (!appliedFilter.isSuperseded()) && (!rowsFinished)
-//					&& (i < MAX_EXPANSION); i++) {
-//				TreePath tp = tree.getPathForRow(i);
-//				if (tp == null) {
-//					rowsFinished = true;
-//				} else {
-//					if (!appliedFilter.pass((DefaultMutableTreeNode) tp
-//							.getLastPathComponent())) {
-//						tree.expandRow(i);
-//					}
-//				}
-//			}
-//		}
-	}
-
-	public void expandAll(FilterTreeNode node, boolean expand) {
-        @SuppressWarnings("unused")
-		FilterTreeNode root = (FilterTreeNode) tree.getModel().getRoot();
-
-        // Traverse tree from root
-        expandAll(new TreePath(node.getPath()), expand);
-    }
-
-    @SuppressWarnings("rawtypes")
-	private void expandAll(TreePath parent, boolean expand) {
-        // Traverse children
-        FilterTreeNode node = (FilterTreeNode) parent.getLastPathComponent();
-        if (node.getChildCount() >= 0)
-            for (Enumeration e=node.children(); e.hasMoreElements(); ) {
-                FilterTreeNode n = (FilterTreeNode) e.nextElement();
-                TreePath path = parent.pathByAddingChild(n);
-                expandAll(path, expand);
-            }
-
-        // Expansion or collapse must be done bottom-up
-        if (expand)
-            tree.expandPath(parent);
-        else
-            tree.collapsePath(parent);
-    }
-
-	protected void initialize() {
-		setLayout(new BorderLayout());
-		treeScrollPane = new JScrollPane(tree);
-		tree.setModel(filterTreeModel);
-		tree.addTreeExpansionListener(treeExpandListener);
-		tree.setCellRenderer(createCellRenderer());
-		tree.setSelectionModel(new FilterTreeSelectionModel());
-
-		JPanel topPanel = new JPanel();
-		topPanel.setBorder(new CompoundBorder(new EdgeLineBorder(TOP, GRAY), new EmptyBorder(10, 5, 0, 5)));
-		topPanel.setLayout(new GridBagLayout());
-		GridBagConstraints c = new GridBagConstraints();
-
-		JLabel filterLabel = new JLabel("Filter:  ");
-		c.fill = NONE;
-		c.gridx = 0;
-		c.gridy = 0;
-		c.weightx = 0.0;
-		c.anchor = GridBagConstraints.LINE_START;
-		topPanel.add(filterLabel, c);
-
-		c.fill = HORIZONTAL;
-		c.gridx = 1;
-		c.gridy = 0;
-		c.weightx = 1.0;
-		topPanel.add(searchField, c);
-
-
-		c.fill = NONE;
-		c.gridx = 2;
-		c.gridy = 0;
-		c.weightx = 0.0;
-		final JButton clearButton = new JButton("Clear");
-		clearButton.addActionListener(new ActionListener() {
-			@Override
-			public void actionPerformed(ActionEvent e) {
-				searchField.setText("");
-				invokeLater(new RunFilter());
-				clearButton.getParent().requestFocusInWindow();// so that the button does not stay focused after it is clicked on and did its action
-			}
-		});
-		topPanel.add(clearButton, c);
-
-		c.gridx = 3;
-		c.weightx = 0.2;
-		topPanel.add(new JPanel(), c);
-
-		JPanel topExtraPanel = new JPanel(new BorderLayout());
-
-		topExtraPanel.add(topPanel, NORTH);
-
-		Component extraComponent = createExtraComponent();
-		if (extraComponent != null) {
-			JPanel extraPanel  = new JPanel();
-			extraPanel.setLayout(new BorderLayout());
-			extraPanel.add(extraComponent, WEST);
-			topExtraPanel.add(extraPanel, CENTER);
-		}
-
-		add(topExtraPanel, NORTH);
-		add(treeScrollPane, CENTER);
-
-		searchField.addKeyListener(new SearchFieldKeyAdapter());
-	}
-
-	protected Component createExtraComponent() {
-		return null;
-	}
-
-	protected TreeCellRenderer createCellRenderer() {
-		return new FilterTreeCellRenderer();
-	}
-
-	public void runFilter() throws InterruptedException,
-			InvocationTargetException {
-		/*
-		 * Special lock object, don't do a synchronized model, as the lock on
-		 * JComponent might deadlock when painting the panel - see comments at
-		 * http://www.mygrid.org.uk/dev/issues/browse/T2-1438
-		 */
-		synchronized (filterLock) {
-			tree.removeTreeExpansionListener(treeExpandListener);
-			String text = searchField.getText();
-			FilterTreeNode root = (FilterTreeNode) tree.getModel().getRoot();
-			if (text.isEmpty()) {
-				setFilter(null);
-				root.setUserObject(getAvailableObjectsString());
-				filterTreeModel.nodeChanged(root);
-				for (List<Object> tp : expandedPaths) {
-	//				for (int i = 0; i < tp.length; i++)
-	//					logger.info("Trying to expand " + tp[i]);
-					tree.expandPath(filterTreeModel.getTreePathForObjectPath(tp));
-				}
-			} else {
-				setFilter(createFilter(text));
-				root.setUserObject(root.getChildCount() > 0 ? getMatchingObjectsString()
-						: getNoMatchingObjectsString());
-				filterTreeModel.nodeChanged(root);
-				expandTreePaths();
-			}
-			tree.addTreeExpansionListener(treeExpandListener);
-		}
-	}
-
-	/**
-	 * @return the availableObjectsString
-	 */
-	public String getAvailableObjectsString() {
-		return availableObjectsString;
-	}
-
-	/**
-	 * @param availableObjectsString the availableObjectsString to set
-	 */
-	public void setAvailableObjectsString(String availableObjectsString) {
-		this.availableObjectsString = availableObjectsString;
-	}
-
-	/**
-	 * @return the matchingObjectsString
-	 */
-	public String getMatchingObjectsString() {
-		return matchingObjectsString;
-	}
-
-	/**
-	 * @param matchingObjectsString the matchingObjectsString to set
-	 */
-	public void setMatchingObjectsString(String matchingObjectsString) {
-		this.matchingObjectsString = matchingObjectsString;
-	}
-
-	/**
-	 * @return the noMatchingObjectsString
-	 */
-	public String getNoMatchingObjectsString() {
-		return noMatchingObjectsString;
-	}
-
-	/**
-	 * @param noMatchingObjectsString the noMatchingObjectsString to set
-	 */
-	public void setNoMatchingObjectsString(String noMatchingObjectsString) {
-		this.noMatchingObjectsString = noMatchingObjectsString;
-	}
-
-	public Filter createFilter(String text) {
-		return new MyFilter(text);
-	}
-
-	public void setFilter(Filter filter) {
-		if (tree.getCellRenderer() instanceof FilterTreeCellRenderer)
-			((FilterTreeCellRenderer)tree.getCellRenderer()).setFilter(filter);
-		filterTreeModel.setFilter(filter);
-	}
-
-	protected class ExpandRowRunnable implements Runnable {
-		int rowNumber;
-
-		public ExpandRowRunnable(int rowNumber) {
-			this.rowNumber = rowNumber;
-		}
-
-		@Override
-		public void run() {
-			tree.expandRow(rowNumber);
-		}
-	}
-
-	protected class RunFilter implements Runnable {
-		@Override
-		public void run() {
-			Filter oldFilter = filterTreeModel.getCurrentFilter();
-			if (oldFilter != null)
-				oldFilter.setSuperseded(true);
-			try {
-				runFilter();
-			} catch (InterruptedException e) {
-				Thread.interrupted();
-			} catch (InvocationTargetException e) {
-				logger.error("", e);
-			}
-		}
-	}
-
-	protected class SearchFieldKeyAdapter extends KeyAdapter {
-		private final Runnable runFilterRunnable;
-		Timer timer = new Timer("Search field timer", true);
-
-		private SearchFieldKeyAdapter() {
-			this.runFilterRunnable = new RunFilter();
-		}
-
-		@Override
-		public void keyReleased(KeyEvent e) {
-			timer.cancel();
-			timer = new Timer();
-			timer.schedule(new TimerTask() {
-				@Override
-				public void run() {
-					invokeLater(runFilterRunnable);
-				}
-			}, 500);
-		}
-	}
-
-	private void noteExpansions() {
-		expandedPaths.clear();
-		TreePath rootPath = new TreePath(filterTreeModel.getRoot());
-		for (Enumeration<TreePath> e = tree.getExpandedDescendants(rootPath); e.hasMoreElements();) {
-			List<Object> userObjects = new ArrayList<>();
-			Object[] expandedPath = e.nextElement().getPath();
-			for (int i = 0; i < expandedPath.length; i++) {
-				FilterTreeNode node = (FilterTreeNode) expandedPath[i];
-//				logger.info("The object in the path is a " + expandedPath[i].getClass());
-				userObjects.add(node.getUserObject());
-//				logger.info("Added " + node.getUserObject() + " to path");
-			}
-			expandedPaths.add(userObjects);
-		}
-	}
-	
-	protected class TreeExpandCollapseListener implements TreeExpansionListener {
-		@Override
-		public void treeCollapsed(TreeExpansionEvent event) {
-			noteExpansions();
-		}
-
-		@Override
-		public void treeExpanded(TreeExpansionEvent event) {
-			noteExpansions();
-		}
-	}
-}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/a9a52bd5/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/PathElementFilterTreeNode.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/PathElementFilterTreeNode.java b/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/PathElementFilterTreeNode.java
new file mode 100644
index 0000000..8a4a247
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/PathElementFilterTreeNode.java
@@ -0,0 +1,33 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.ui.servicepanel;
+
+import org.apache.taverna.workbench.ui.servicepanel.tree.FilterTreeNode;
+
+/**
+ * @author alanrw
+ */
+public class PathElementFilterTreeNode extends FilterTreeNode {
+	private static final long serialVersionUID = 6491242031931630314L;
+
+	public PathElementFilterTreeNode(String userObject) {
+		super(userObject);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/a9a52bd5/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/RootFilterTreeNode.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/RootFilterTreeNode.java b/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/RootFilterTreeNode.java
new file mode 100644
index 0000000..8ae210e
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/RootFilterTreeNode.java
@@ -0,0 +1,33 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.ui.servicepanel;
+
+import org.apache.taverna.workbench.ui.servicepanel.tree.FilterTreeNode;
+
+/**
+ * @author alanrw
+ */
+public class RootFilterTreeNode extends FilterTreeNode {
+	private static final long serialVersionUID = 1047743498806473971L;
+
+	public RootFilterTreeNode(String userObject) {
+		super(userObject);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/a9a52bd5/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServiceFilter.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServiceFilter.java b/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServiceFilter.java
new file mode 100644
index 0000000..c4dd75a
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServiceFilter.java
@@ -0,0 +1,157 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.ui.servicepanel;
+
+import static java.beans.Introspector.getBeanInfo;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+
+import org.apache.taverna.servicedescriptions.ServiceDescription;
+import org.apache.taverna.workbench.ui.servicepanel.tree.Filter;
+
+import org.apache.log4j.Logger;
+
+public class ServiceFilter implements Filter {
+	private static Logger logger = Logger.getLogger(ServiceFilter.class);
+
+	private String filterString;
+	private boolean superseded;
+	private String[] filterLowerCaseSplit;
+	private final Object rootToIgnore;
+
+	public ServiceFilter(String filterString, Object rootToIgnore) {
+		this.filterString = filterString;
+		this.rootToIgnore = rootToIgnore;
+		this.filterLowerCaseSplit = filterString.toLowerCase().split(" ");
+		this.superseded = false;
+	}
+
+	private boolean basicFilter(DefaultMutableTreeNode node) {
+		if (node == rootToIgnore)
+			return false;
+		if (filterString.isEmpty())
+			return true;
+
+		if (node.getUserObject() instanceof ServiceDescription) {
+			ServiceDescription serviceDescription = (ServiceDescription) node
+					.getUserObject();
+			for (String searchTerm : filterLowerCaseSplit) {
+				if (superseded)
+					return false;
+				String[] typeSplit = searchTerm.split(":", 2);
+				String type;
+				String keyword;
+				if (typeSplit.length == 2) {
+					type = typeSplit[0];
+					keyword = typeSplit[1].toLowerCase();
+				} else {
+					type = null;
+					keyword = searchTerm.toLowerCase();
+				}
+				try {
+					if (!doesPropertySatisfy(serviceDescription, type, keyword))
+						return false;
+				} catch (IntrospectionException | IllegalArgumentException
+						| IllegalAccessException | InvocationTargetException e) {
+					logger.error(
+							"failed to get properties of service description",
+							e);
+					return false;
+				}
+			}
+			return true;
+		}
+		for (String searchString : filterLowerCaseSplit)
+			if (!node.getUserObject().toString().toLowerCase().contains(
+					searchString))
+				return false;
+		return true;
+	}
+
+	/**
+	 * Determine whether a service description satisfies a search term.
+	 * 
+	 * @param serviceDescription
+	 *            The service description bean to look in.
+	 * @param type
+	 *            The name of the property to look in, or <tt>null</tt> to
+	 *            search in all public non-expert properties.
+	 * @param searchTerm
+	 *            The string to search for.
+	 * @return <tt>true</tt> if-and-only-if the description matches.
+	 */
+	private boolean doesPropertySatisfy(ServiceDescription serviceDescription,
+			String type, String searchTerm) throws IllegalAccessException,
+			IllegalArgumentException, InvocationTargetException,
+			IntrospectionException {
+		BeanInfo beanInfo = getBeanInfo(serviceDescription.getClass());
+		for (PropertyDescriptor property : beanInfo.getPropertyDescriptors()) {
+			if (superseded)
+				return false;
+			if ((type == null && !property.isHidden() && !property.isExpert())
+					|| property.getName().equalsIgnoreCase(type)) {
+				Method readMethod = property.getReadMethod();
+				if (readMethod == null)
+					continue;
+				Object readProperty = readMethod.invoke(serviceDescription,
+						new Object[0]);
+				if (readProperty == null)
+					continue;
+				if (readProperty.toString().toLowerCase().contains(searchTerm))
+					return true;
+				// Dig deeper?
+			}
+		}
+		return false;
+	}
+
+	@Override
+	public boolean pass(DefaultMutableTreeNode node) {
+		return basicFilter(node);
+	}
+
+	@Override
+	public String filterRepresentation(String original) {
+		return original;
+	}
+
+	/**
+	 * @return the superseded
+	 */
+	@Override
+	public boolean isSuperseded() {
+		return superseded;
+	}
+
+	/**
+	 * @param superseded
+	 *            the superseded to set
+	 */
+	@Override
+	public void setSuperseded(boolean superseded) {
+		this.superseded = superseded;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/a9a52bd5/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServiceFilterTreeNode.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServiceFilterTreeNode.java b/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServiceFilterTreeNode.java
new file mode 100644
index 0000000..ef7811e
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServiceFilterTreeNode.java
@@ -0,0 +1,39 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.ui.servicepanel;
+
+import org.apache.taverna.servicedescriptions.ServiceDescription;
+import org.apache.taverna.workbench.ui.servicepanel.tree.FilterTreeNode;
+
+/**
+ * @author alanrw
+ */
+public class ServiceFilterTreeNode extends FilterTreeNode {
+	private static final long serialVersionUID = 6066698619971305454L;
+	
+	public ServiceFilterTreeNode(ServiceDescription userObject) {
+		super(userObject);
+	}
+
+	@Override
+	public ServiceDescription getUserObject() {
+		return (ServiceDescription) super.getUserObject();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/a9a52bd5/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServicePanel.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServicePanel.java b/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServicePanel.java
new file mode 100644
index 0000000..e95869f
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServicePanel.java
@@ -0,0 +1,410 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.ui.servicepanel;
+
+import static javax.swing.JOptionPane.ERROR_MESSAGE;
+import static javax.swing.JOptionPane.showMessageDialog;
+import static javax.swing.SwingUtilities.invokeLater;
+import static org.apache.taverna.servicedescriptions.ServiceDescription.LOCAL_SERVICES;
+import static org.apache.taverna.servicedescriptions.ServiceDescription.SERVICE_TEMPLATES;
+
+import java.awt.BorderLayout;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import org.apache.taverna.lang.observer.Observable;
+import org.apache.taverna.lang.observer.Observer;
+import org.apache.taverna.servicedescriptions.ServiceDescription;
+import org.apache.taverna.servicedescriptions.ServiceDescriptionProvider;
+import org.apache.taverna.servicedescriptions.ServiceDescriptionRegistry;
+import org.apache.taverna.servicedescriptions.events.AbstractProviderEvent;
+import org.apache.taverna.servicedescriptions.events.AbstractProviderNotification;
+import org.apache.taverna.servicedescriptions.events.PartialServiceDescriptionsNotification;
+import org.apache.taverna.servicedescriptions.events.ProviderErrorNotification;
+import org.apache.taverna.servicedescriptions.events.RemovedProviderEvent;
+import org.apache.taverna.servicedescriptions.events.ServiceDescriptionProvidedEvent;
+import org.apache.taverna.servicedescriptions.events.ServiceDescriptionRegistryEvent;
+import org.apache.taverna.ui.menu.MenuManager;
+import org.apache.taverna.workbench.edits.EditManager;
+import org.apache.taverna.workbench.selection.SelectionManager;
+import org.apache.taverna.workbench.ui.servicepanel.tree.FilterTreeModel;
+import org.apache.taverna.workbench.ui.servicepanel.tree.FilterTreeNode;
+import org.apache.taverna.workbench.ui.zaria.UIComponentSPI;
+
+import org.apache.log4j.Logger;
+
+import org.apache.taverna.commons.services.ServiceRegistry;
+
+/**
+ * A panel of available services
+ *
+ * @author Stian Soiland-Reyes
+ */
+@SuppressWarnings("serial")
+public class ServicePanel extends JPanel implements UIComponentSPI {
+	private static Logger logger = Logger.getLogger(ServicePanel.class);
+	private static final int INITIAL_BLANK_OUT_COUNTER = 2;
+	public static final String AVAILABLE_SERVICES = "Available services";
+	public static final String MATCHING_SERVIES = "Matching services";
+	public static final String NO_MATCHING_SERVICES = "No matching services";
+	public static final String MOBY_OBJECTS = "MOBY Objects";
+	/**
+	 * A Comparable constant to be used with buildPathMap
+	 */
+	private static final String SERVICES = "4DA84170-7746-4817-8C2E-E29AF8B2984D";
+	private static final int STATUS_LINE_MESSAGE_MS = 600;
+	private static ServicePathElementComparator servicePathElementComparator = new ServicePathElementComparator();
+
+	public int blankOutCounter = 0;
+	private TreeUpdaterThread updaterThread;
+	private RootFilterTreeNode root = new RootFilterTreeNode(AVAILABLE_SERVICES);
+	private ServiceTreePanel serviceTreePanel;
+	private JLabel statusLine;
+	private FilterTreeModel treeModel;
+	protected Timer statusUpdateTimer;
+
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+	protected final ServiceDescriptionRegistryObserver serviceDescriptionRegistryObserver = new ServiceDescriptionRegistryObserver();
+	protected final Object updateLock = new Object();
+	private final EditManager editManager;
+	private final MenuManager menuManager;
+	private final SelectionManager selectionManager;
+	private final ServiceRegistry serviceRegistry;
+
+	public ServicePanel(ServiceDescriptionRegistry serviceDescriptionRegistry,
+			EditManager editManager, MenuManager menuManager,
+			SelectionManager selectionManager, ServiceRegistry serviceRegistry) {
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+		this.editManager = editManager;
+		this.menuManager = menuManager;
+		this.selectionManager = selectionManager;
+		this.serviceRegistry = serviceRegistry;
+		serviceDescriptionRegistry.addObserver(serviceDescriptionRegistryObserver);
+		initialise();
+	}
+
+	@Override
+	public ImageIcon getIcon() {
+		return null;
+	}
+
+	@Override
+	public String getName() {
+		return "Service panel";
+	}
+
+	@Override
+	public void onDisplay() {
+	}
+
+	@Override
+	public void onDispose() {
+	}
+
+	public void providerStatus(ServiceDescriptionProvider provider, String message) {
+		logger.info(message + " " + provider);
+		final String htmlMessage = "<small>" + message + " [" + provider + "]</small>";
+
+		invokeLater(new Runnable() {
+			@Override
+			public void run() {
+				blankOutCounter = INITIAL_BLANK_OUT_COUNTER;
+				statusLine.setText("<html>" + htmlMessage + "</html>");
+				statusLine.setVisible(true);
+			}
+		});
+	}
+
+	protected void initialise() {
+		removeAll();
+		setLayout(new BorderLayout());
+		treeModel = new FilterTreeModel(root);
+		serviceTreePanel = new ServiceTreePanel(treeModel, serviceDescriptionRegistry, editManager, menuManager, selectionManager, serviceRegistry);
+		serviceTreePanel.setAvailableObjectsString(AVAILABLE_SERVICES);
+		serviceTreePanel.setMatchingObjectsString(MATCHING_SERVIES);
+		serviceTreePanel.setNoMatchingObjectsString(NO_MATCHING_SERVICES);
+		add(serviceTreePanel);
+		statusLine = new JLabel();
+		add(statusLine, BorderLayout.SOUTH);
+		if (statusUpdateTimer != null)
+			statusUpdateTimer.cancel();
+		statusUpdateTimer = new Timer("Clear status line", true);
+		statusUpdateTimer
+				.scheduleAtFixedRate(new UpdateStatusLineTask(), 0, STATUS_LINE_MESSAGE_MS);
+		updateTree();
+	}
+
+	protected void updateTree() {
+		synchronized (updateLock) {
+			if (updaterThread != null && updaterThread.isAlive()) {
+				return;
+			}
+			updaterThread = new TreeUpdaterThread();
+			updaterThread.start();
+		}
+	}
+
+	protected static class ServicePathElementComparator implements Comparator<Object> {
+		@Override
+		public int compare(Object o1, Object o2) {
+			if ((o1 instanceof String) && (o2 instanceof String)) {
+				if (o1.equals(SERVICE_TEMPLATES))
+					return -1;
+				else if (o2.equals(SERVICE_TEMPLATES))
+					return 1;
+				if (o1.equals(LOCAL_SERVICES))
+					return -1;
+				else if (o2.equals(LOCAL_SERVICES))
+					return 1;
+				if (o1.equals(MOBY_OBJECTS))
+					return -1;
+				else if (o2.equals(MOBY_OBJECTS))
+					return 1;
+			}
+			return o1.toString().compareToIgnoreCase(o2.toString());
+		}
+	}
+
+	@SuppressWarnings({ "rawtypes", "unchecked" })
+	//FIXME this class is type-disastrous! Really bad.
+	public class TreeUpdaterThread extends Thread {
+		private boolean aborting = false;
+
+		private TreeUpdaterThread() {
+			super("Updating service panel");
+			setDaemon(true);
+		}
+
+		public void abort() {
+			aborting = true;
+			interrupt();
+		}
+
+		@Override
+		public void run() {
+			Map<Comparable, Map> pathMap = buildPathMap();
+			populateChildren(root, pathMap);
+			invokeLater(new Runnable() {
+				@Override
+				public void run() {
+					try {
+						serviceTreePanel.runFilter();
+					} catch (InterruptedException | InvocationTargetException e) {
+						logger.error("failed to filter", e);
+					}
+				}
+			});
+		}
+
+		protected Map<Comparable, Map> buildPathMap() {
+			Map<Comparable, Map> paths = new TreeMap<>();
+			for (ServiceDescription serviceDescription : serviceDescriptionRegistry
+					.getServiceDescriptions()) {
+				if (aborting)
+					return paths;
+				Map currentPath = paths;
+				for (Object pathElem : serviceDescription.getPath()) {
+					Map pathEntry = (Map) currentPath.get(pathElem);
+					if (pathEntry == null) {
+						pathEntry = new TreeMap();
+						currentPath.put(pathElem, pathEntry);
+					}
+					currentPath = pathEntry;
+				}
+				TreeMap<String, Set<ServiceDescription>> services = (TreeMap) currentPath
+						.get(SERVICES);
+				if (services == null) {
+					services = new TreeMap<>();
+					currentPath.put(SERVICES, services);
+				}
+				String serviceDescriptionName = serviceDescription.getName();
+				if (!services.containsKey(serviceDescriptionName)) {
+					Set<ServiceDescription> serviceSet = new HashSet<>();
+					services.put(serviceDescriptionName, serviceSet);
+				}
+				services.get(serviceDescriptionName).add(serviceDescription);
+			}
+			return paths;
+		}
+
+		protected void populateChildren(FilterTreeNode node, Map pathMap) {
+			if (aborting)
+				return;
+			if (node == root) {
+				// Clear top root
+				invokeLater(new Runnable() {
+					@Override
+					public void run() {
+						if (aborting)
+							return;
+						serviceTreePanel.setFilter(null);
+						root.removeAllChildren();
+					}
+				});
+			}
+
+			Set<Comparable> paths = new TreeSet<>(servicePathElementComparator);
+			Map<String, Set<ServiceDescription>> services = (Map) pathMap
+					.get(SERVICES);
+			if (services == null)
+				services = new TreeMap<>();
+			paths.addAll(pathMap.keySet());
+			paths.addAll(services.keySet());
+
+			for (Comparable pathElement : paths) {
+				if (aborting)
+					return;
+				if (pathElement.equals(SERVICES))
+					continue;
+				Set<FilterTreeNode> childNodes = new HashSet<>();
+				if (services.containsKey(pathElement)) {
+					for (ServiceDescription sd : services.get(pathElement))
+						childNodes.add(new ServiceFilterTreeNode(sd));
+				} else
+					childNodes.add(new PathElementFilterTreeNode((String) pathElement));
+				invokeLater(new AddNodeRunnable(node, childNodes));
+				if ((pathMap.containsKey(pathElement)) && !childNodes.isEmpty())
+					populateChildren(childNodes.iterator().next(), (Map) pathMap.get(pathElement));
+			}
+			// if (!services.isEmpty()) {
+			// Collections.sort(services, serviceComparator);
+			// for (String serviceName : services.keySet()) {
+			// if (aborting) {
+			// return;
+			// }
+			// if (pathMap.containsKey(serviceName)) {
+			// continue;
+			// }
+			// SwingUtilities.invokeLater(new AddNodeRunnable(node,
+			// new ServiceFilterTreeNode(services.get(serviceName))));
+			// }
+			// }
+		}
+
+		public class AddNodeRunnable implements Runnable {
+			private final Set<FilterTreeNode> nodes;
+			private final FilterTreeNode root;
+
+			public AddNodeRunnable(FilterTreeNode root, Set<FilterTreeNode> nodes) {
+				this.root = root;
+				this.nodes = nodes;
+			}
+
+			@Override
+			public void run() {
+				if (aborting)
+					return;
+				for (FilterTreeNode n : nodes)
+					root.add(n);
+			}
+		}
+	}
+
+	public static class RemoveNodeRunnable implements Runnable {
+		private final FilterTreeNode root;
+
+		public RemoveNodeRunnable(FilterTreeNode root) {
+			this.root = root;
+		}
+
+		@Override
+		public void run() {
+			root.removeFromParent();
+		}
+	}
+
+	private final class ServiceDescriptionRegistryObserver implements
+			Observer<ServiceDescriptionRegistryEvent> {
+		Set<ServiceDescriptionProvider> alreadyComplainedAbout = new HashSet<>();
+
+		@Override
+		public void notify(Observable<ServiceDescriptionRegistryEvent> sender,
+				ServiceDescriptionRegistryEvent message) throws Exception {
+			if (message instanceof ProviderErrorNotification)
+				reportServiceProviderError((ProviderErrorNotification) message);
+			else if (message instanceof ServiceDescriptionProvidedEvent
+					|| message instanceof RemovedProviderEvent) {
+				AbstractProviderEvent ape = (AbstractProviderEvent) message;
+				alreadyComplainedAbout.remove(ape.getProvider());
+			}
+
+			if (message instanceof AbstractProviderNotification) {
+				AbstractProviderNotification abstractProviderNotification = (AbstractProviderNotification) message;
+				providerStatus(abstractProviderNotification.getProvider(),
+						abstractProviderNotification.getMessage());
+			}
+			if (message instanceof PartialServiceDescriptionsNotification)
+				/*
+				 * TODO: Support other events and only update relevant parts of
+				 * tree, or at least select the recently added provider
+				 */
+				updateTree();
+			else if (message instanceof RemovedProviderEvent)
+				updateTree();
+		}
+
+		private void reportServiceProviderError(
+				final ProviderErrorNotification pen) {
+			ServiceDescriptionProvider provider = pen.getProvider();
+			if (serviceDescriptionRegistry
+					.getDefaultServiceDescriptionProviders().contains(provider))
+				return;
+			if (alreadyComplainedAbout.contains(provider))
+				return;
+
+			alreadyComplainedAbout.add(provider);
+			invokeLater(new Runnable() {
+				@Override
+				public void run() {
+					showMessageDialog(ServicePanel.this, pen.getMessage()
+							+ "\n" + pen.getProvider(), "Import service error",
+							ERROR_MESSAGE);
+				}
+			});
+		}
+	}
+
+	private final class UpdateStatusLineTask extends TimerTask {
+		@Override
+		public void run() {
+			if (blankOutCounter < 0 || blankOutCounter-- > 0)
+				// Only clear it once
+				return;
+			invokeLater(new Runnable() {
+				@Override
+				public void run() {
+					if (blankOutCounter < 0)
+						statusLine.setVisible(false);
+				}
+			});
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/a9a52bd5/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServicePanelComponentFactory.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServicePanelComponentFactory.java b/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServicePanelComponentFactory.java
new file mode 100644
index 0000000..1bd92a5
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServicePanelComponentFactory.java
@@ -0,0 +1,81 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.ui.servicepanel;
+
+import javax.swing.ImageIcon;
+
+import org.apache.taverna.commons.services.ServiceRegistry;
+
+import org.apache.taverna.servicedescriptions.ServiceDescriptionRegistry;
+import org.apache.taverna.ui.menu.MenuManager;
+import org.apache.taverna.workbench.edits.EditManager;
+import org.apache.taverna.workbench.selection.SelectionManager;
+import org.apache.taverna.workbench.ui.zaria.UIComponentFactorySPI;
+import org.apache.taverna.workbench.ui.zaria.UIComponentSPI;
+
+/**
+ * Service panel factory
+ * 
+ * @author Stian Soiland-Reyes
+ */
+public class ServicePanelComponentFactory implements UIComponentFactorySPI {
+	private ServiceDescriptionRegistry serviceDescriptionRegistry;
+	private EditManager editManager;
+	private MenuManager menuManager;
+	private SelectionManager selectionManager;
+	private ServiceRegistry serviceRegistry;
+
+	@Override
+	public UIComponentSPI getComponent() {
+		return new ServicePanel(serviceDescriptionRegistry, editManager,
+				menuManager, selectionManager, serviceRegistry);
+	}
+
+	@Override
+	public ImageIcon getIcon() {
+		return null;
+	}
+
+	@Override
+	public String getName() {
+		return "Service panel";
+	}
+
+	public void setServiceDescriptionRegistry(
+			ServiceDescriptionRegistry serviceDescriptionRegistry) {
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+	}
+
+	public void setEditManager(EditManager editManager) {
+		this.editManager = editManager;
+	}
+
+	public void setMenuManager(MenuManager menuManager) {
+		this.menuManager = menuManager;
+	}
+
+	public void setSelectionManager(SelectionManager selectionManager) {
+		this.selectionManager = selectionManager;
+	}
+
+	public void setServiceRegistry(ServiceRegistry serviceRegistry) {
+		this.serviceRegistry = serviceRegistry;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/a9a52bd5/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServiceTreeCellRenderer.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServiceTreeCellRenderer.java b/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServiceTreeCellRenderer.java
new file mode 100644
index 0000000..e5f65a9
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServiceTreeCellRenderer.java
@@ -0,0 +1,76 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.ui.servicepanel;
+
+import static org.apache.taverna.workbench.activityicons.DefaultActivityIcon.getDefaultIcon;
+
+import java.awt.Component;
+
+import javax.swing.Icon;
+import javax.swing.JTree;
+
+import org.apache.taverna.servicedescriptions.ServiceDescription;
+import org.apache.taverna.workbench.ui.servicepanel.tree.FilterTreeCellRenderer;
+import org.apache.taverna.workbench.ui.servicepanel.tree.FilterTreeNode;
+
+@SuppressWarnings("serial")
+public class ServiceTreeCellRenderer extends FilterTreeCellRenderer {
+	@Override
+	public Component getTreeCellRendererComponent(JTree tree, Object value,
+			boolean sel, boolean expanded, boolean leaf, int row,
+			boolean hasFocus) {
+		Component result = super.getTreeCellRendererComponent(tree, value, sel,
+				expanded, leaf, row, hasFocus);
+		if (result instanceof ServiceTreeCellRenderer
+				&& value instanceof FilterTreeNode
+				&& ((FilterTreeNode) value).getUserObject() instanceof ServiceDescription)
+			prettifyServiceTreeCell((ServiceTreeCellRenderer) result,
+					(ServiceDescription) ((FilterTreeNode) value)
+							.getUserObject());
+		else {
+			// Commented out - these are ugly, use the default folder icons instead
+			/*
+			 * if (expanded) { ((ServiceTreeCellRenderer) result)
+			 * .setIcon(WorkbenchIcons.folderOpenIcon); } else {
+			 * ((ServiceTreeCellRenderer) result)
+			 * .setIcon(WorkbenchIcons.folderClosedIcon); }
+			 */
+		}
+		return result;
+	}
+
+	private void prettifyServiceTreeCell(ServiceTreeCellRenderer renderer,
+			ServiceDescription item) {
+		String name = item.getName();
+		if (getFilter() != null)
+			name = getFilter().filterRepresentation(name);
+		// serviceTreeCellRenderer.setForeground(Color.red);
+		String displayName = name;
+
+		String textualDescription = item.getDescription();
+		if (textualDescription != null && !textualDescription.isEmpty())
+			displayName = displayName + " - " + textualDescription;
+		renderer.setText(displayName);
+
+		Icon activityIcon = item.getIcon();
+		renderer.setIcon(activityIcon != null ? activityIcon
+				: getDefaultIcon());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/a9a52bd5/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServiceTreeClickListener.java
----------------------------------------------------------------------
diff --git a/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServiceTreeClickListener.java b/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServiceTreeClickListener.java
new file mode 100644
index 0000000..8109cdc
--- /dev/null
+++ b/taverna-activity-palette-ui/src/main/java/org/apache/taverna/workbench/ui/servicepanel/ServiceTreeClickListener.java
@@ -0,0 +1,251 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+package org.apache.taverna.workbench.ui.servicepanel;
+
+import static java.awt.Color.RED;
+import static javax.swing.SwingUtilities.invokeLater;
+import static org.apache.taverna.lang.ui.ShadedLabel.BLUE;
+import static org.apache.taverna.lang.ui.ShadedLabel.GREEN;
+import static org.apache.taverna.lang.ui.ShadedLabel.ORANGE;
+import static org.apache.taverna.lang.ui.ShadedLabel.halfShade;
+import static org.apache.taverna.workbench.icons.WorkbenchIcons.minusIcon;
+import static org.apache.taverna.workbench.icons.WorkbenchIcons.plusIcon;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import javax.swing.AbstractAction;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.JTree;
+import javax.swing.tree.TreePath;
+
+import org.apache.taverna.lang.ui.ShadedLabel;
+import org.apache.taverna.servicedescriptions.ConfigurableServiceProvider;
+import org.apache.taverna.servicedescriptions.ServiceDescription;
+import org.apache.taverna.servicedescriptions.ServiceDescriptionProvider;
+import org.apache.taverna.servicedescriptions.ServiceDescriptionRegistry;
+import org.apache.taverna.ui.menu.MenuManager;
+import org.apache.taverna.workbench.edits.EditManager;
+import org.apache.taverna.workbench.selection.SelectionManager;
+import org.apache.taverna.workbench.ui.servicepanel.actions.ExportServiceDescriptionsAction;
+import org.apache.taverna.workbench.ui.servicepanel.actions.ImportServiceDescriptionsFromFileAction;
+import org.apache.taverna.workbench.ui.servicepanel.actions.ImportServiceDescriptionsFromURLAction;
+import org.apache.taverna.workbench.ui.servicepanel.actions.RemoveDefaultServicesAction;
+import org.apache.taverna.workbench.ui.servicepanel.actions.RemoveUserServicesAction;
+import org.apache.taverna.workbench.ui.servicepanel.actions.RestoreDefaultServicesAction;
+import org.apache.taverna.workbench.ui.servicepanel.tree.FilterTreeNode;
+import org.apache.taverna.workbench.ui.servicepanel.tree.FilterTreeSelectionModel;
+import org.apache.taverna.workbench.ui.servicepanel.tree.TreePanel;
+import org.apache.taverna.workbench.ui.workflowview.WorkflowView;
+
+import org.apache.log4j.Logger;
+
+import org.apache.taverna.commons.services.ServiceRegistry;
+
+/**
+ * @author alanrw
+ */
+public class ServiceTreeClickListener extends MouseAdapter {
+	private static Logger logger = Logger.getLogger(ServiceTreeClickListener.class);
+
+	private JTree tree;
+	private TreePanel panel;
+	private final ServiceDescriptionRegistry serviceDescriptionRegistry;
+	private final EditManager editManager;
+	private final MenuManager menuManager;
+	private final SelectionManager selectionManager;
+	private final ServiceRegistry serviceRegistry;
+
+	public ServiceTreeClickListener(JTree tree, TreePanel panel,
+			ServiceDescriptionRegistry serviceDescriptionRegistry, EditManager editManager,
+			MenuManager menuManager, SelectionManager selectionManager, ServiceRegistry serviceRegistry) {
+		this.tree = tree;
+		this.panel = panel;
+		this.serviceDescriptionRegistry = serviceDescriptionRegistry;
+		this.editManager = editManager;
+		this.menuManager = menuManager;
+		this.selectionManager = selectionManager;
+		this.serviceRegistry = serviceRegistry;
+	}
+
+	@SuppressWarnings("serial")
+	private void handleMouseEvent(MouseEvent evt) {
+		FilterTreeSelectionModel selectionModel = (FilterTreeSelectionModel) tree
+				.getSelectionModel();
+		// Discover the tree row that was clicked on
+		int selRow = tree.getRowForLocation(evt.getX(), evt.getY());
+		if (selRow == -1)
+			return;
+
+		// Get the selection path for the row
+		TreePath selectionPath = tree
+				.getPathForLocation(evt.getX(), evt.getY());
+		if (selectionPath == null)
+			return;
+
+		// Get the selected node
+		final FilterTreeNode selectedNode = (FilterTreeNode) selectionPath
+				.getLastPathComponent();
+
+		selectionModel.clearSelection();
+		selectionModel.mySetSelectionPath(selectionPath);
+
+		if (evt.isPopupTrigger()) {
+			JPopupMenu menu = new JPopupMenu();
+			Object selectedObject = selectedNode.getUserObject();
+			logger.info(selectedObject.getClass().getName());
+			if (!(selectedObject instanceof ServiceDescription)) {
+				menu.add(new ShadedLabel("Tree", BLUE));
+				menu.add(new JMenuItem(new AbstractAction("Expand all",
+						plusIcon) {
+					@Override
+					public void actionPerformed(ActionEvent evt) {
+						invokeLater(new Runnable() {
+							@Override
+							public void run() {
+								panel.expandAll(selectedNode, true);
+							}
+						});
+					}
+				}));
+				menu.add(new JMenuItem(new AbstractAction("Collapse all",
+						minusIcon) {
+					@Override
+					public void actionPerformed(ActionEvent evt) {
+						invokeLater(new Runnable() {
+							@Override
+							public void run() {
+								panel.expandAll(selectedNode, false);
+							}
+						});
+					}
+				}));
+			}
+
+			if (selectedObject instanceof ServiceDescription) {
+				final ServiceDescription sd = (ServiceDescription) selectedObject;
+				menu.add(new ShadedLabel(sd.getName(), ORANGE));
+				menu.add(new AbstractAction("Add to workflow") {
+					@Override
+					public void actionPerformed(ActionEvent e) {
+						WorkflowView.importServiceDescription(sd, false, editManager,
+								menuManager, selectionManager, serviceRegistry);
+					}
+				});
+				menu.add(new AbstractAction("Add to workflow with name...") {
+					@Override
+					public void actionPerformed(ActionEvent e) {
+						WorkflowView.importServiceDescription(sd, true, editManager,
+								menuManager, selectionManager, serviceRegistry);
+					}
+				});
+			}
+
+			Map<String, ServiceDescriptionProvider> nameMap = getServiceDescriptionProviderMap(selectedNode);
+
+			boolean first = true;
+			for (String name : nameMap.keySet()) {
+				final ServiceDescriptionProvider sdp = nameMap.get(name);
+				if (!(sdp instanceof ConfigurableServiceProvider))
+					continue;
+				if (first) {
+					menu.add(new ShadedLabel(
+							"Remove individual service provider", GREEN));
+					first = false;
+				}
+				menu.add(new AbstractAction(name) {
+					@Override
+					public void actionPerformed(ActionEvent e) {
+						serviceDescriptionRegistry
+								.removeServiceDescriptionProvider(sdp);
+					}
+				});
+			}
+
+			if (selectedNode.isRoot()) { // Root "Available services"
+				menu.add(new ShadedLabel("Default and added service providers",
+						ORANGE));
+				menu.add(new RemoveUserServicesAction(
+						serviceDescriptionRegistry));
+				menu.add(new RemoveDefaultServicesAction(
+						serviceDescriptionRegistry));
+				menu.add(new RestoreDefaultServicesAction(
+						serviceDescriptionRegistry));
+
+				menu.add(new ShadedLabel("Import/export services", halfShade(RED)));
+				menu.add(new ImportServiceDescriptionsFromFileAction(
+						serviceDescriptionRegistry));
+				menu.add(new ImportServiceDescriptionsFromURLAction(
+						serviceDescriptionRegistry));
+				menu.add(new ExportServiceDescriptionsAction(
+						serviceDescriptionRegistry));
+			}
+
+			menu.show(evt.getComponent(), evt.getX(), evt.getY());
+		}
+	}
+
+	private Map<String, ServiceDescriptionProvider> getServiceDescriptionProviderMap(
+			FilterTreeNode selectedNode) {
+		Set<ServiceDescriptionProvider> providers;
+
+		if (selectedNode.isRoot())
+			providers = serviceDescriptionRegistry
+					.getServiceDescriptionProviders();
+		else {
+			providers = new HashSet<>();
+			for (FilterTreeNode leaf : selectedNode.getLeaves()) {
+				if (!leaf.isLeaf())
+					logger.info("Not a leaf");
+				if (!(leaf.getUserObject() instanceof ServiceDescription)) {
+					logger.info(leaf.getUserObject().getClass()
+							.getCanonicalName());
+					logger.info(leaf.getUserObject().toString());
+					continue;
+				}
+				providers
+						.addAll(serviceDescriptionRegistry
+								.getServiceDescriptionProviders((ServiceDescription) leaf
+										.getUserObject()));
+			}
+		}
+
+		TreeMap<String, ServiceDescriptionProvider> nameMap = new TreeMap<>();
+		for (ServiceDescriptionProvider sdp : providers)
+			nameMap.put(sdp.toString(), sdp);
+		return nameMap;
+	}
+
+	@Override
+	public void mousePressed(MouseEvent evt) {
+		handleMouseEvent(evt);
+	}
+
+	@Override
+	public void mouseReleased(MouseEvent evt) {
+		handleMouseEvent(evt);
+	}
+}