You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@taverna.apache.org by st...@apache.org on 2015/03/06 23:34:37 UTC

[41/50] [abbrv] incubator-taverna-workbench git commit: taverna-*

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/LinePainter.java
----------------------------------------------------------------------
diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/LinePainter.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/LinePainter.java
new file mode 100644
index 0000000..0fb2926
--- /dev/null
+++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/LinePainter.java
@@ -0,0 +1,163 @@
+package net.sf.taverna.t2.lang.ui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.text.*;
+
+/*
+ *  Track the movement of the Caret by painting a background line at the
+ *  current caret position.
+ *  
+ *  Copied from http://www.camick.com/java/source/LinePainter.java
+ *  
+ *  Written by Rob Camick
+ */
+public class LinePainter
+	implements Highlighter.HighlightPainter, CaretListener, MouseListener, MouseMotionListener
+{
+	private JTextComponent component;
+
+	private Color color;
+
+	private Rectangle lastView;
+
+	/*
+	 *  The line color will be calculated automatically by attempting
+	 *  to make the current selection lighter by a factor of 1.2.
+	 *
+	 *  @param component  text component that requires background line painting
+	 */
+	public LinePainter(JTextComponent component)
+	{
+		this(component, null);
+		setLighter(component.getSelectionColor());
+	}
+
+	/*
+	 *  Manually control the line color
+	 *
+	 *  @param component  text component that requires background line painting
+	 *  @param color      the color of the background line
+	 */
+	public LinePainter(JTextComponent component, Color color)
+	{
+		this.component = component;
+		setColor( color );
+
+		//  Add listeners so we know when to change highlighting
+
+		component.addCaretListener( this );
+		component.addMouseListener( this );
+		component.addMouseMotionListener( this );
+
+		//  Turn highlighting on by adding a dummy highlight
+
+		try
+		{
+			component.getHighlighter().addHighlight(0, 0, this);
+		}
+		catch(BadLocationException ble) {}
+	}
+
+	/*
+	 *	You can reset the line color at any time
+	 *
+	 *  @param color  the color of the background line
+	 */
+	public void setColor(Color color)
+	{
+		this.color = color;
+	}
+
+	/*
+	 *  Calculate the line color by making the selection color lighter
+	 *
+	 *  @return the color of the background line
+	 */
+	public void setLighter(Color color)
+	{
+		int red   = Math.min(255, (int)(color.getRed() * 1.2));
+		int green = Math.min(255, (int)(color.getGreen() * 1.2));
+		int blue  = Math.min(255, (int)(color.getBlue() * 1.2));
+		setColor(new Color(red, green, blue));
+	}
+
+	//  Paint the background highlight
+
+	public void paint(Graphics g, int p0, int p1, Shape bounds, JTextComponent c)
+	{
+		try
+		{
+			Rectangle r = c.modelToView(c.getCaretPosition());
+			g.setColor( color );
+			g.fillRect(0, r.y, c.getWidth(), r.height);
+
+			if (lastView == null)
+				lastView = r;
+		}
+		catch(BadLocationException ble) {System.out.println(ble);}
+	}
+
+	/*
+	*   Caret position has changed, remove the highlight
+	*/
+	private void resetHighlight()
+	{
+		//  Use invokeLater to make sure updates to the Document are completed,
+		//  otherwise Undo processing causes the modelToView method to loop.
+
+		SwingUtilities.invokeLater(new Runnable()
+		{
+			public void run()
+			{
+				try
+				{
+					int offset =  component.getCaretPosition();
+					Rectangle currentView = component.modelToView(offset);
+
+					if (lastView == null) {
+						lastView = currentView;
+					} else {
+						//  Remove the highlighting from the previously highlighted line
+						if (lastView.y != currentView.y)
+						{
+							component.repaint(0, lastView.y, component.getWidth(), lastView.height);
+							lastView = currentView;
+						}
+					}
+				}
+				catch(BadLocationException ble) {}
+			}
+		});
+	}
+
+	//  Implement CaretListener
+
+	public void caretUpdate(CaretEvent e)
+	{
+		resetHighlight();
+	}
+
+	//  Implement MouseListener
+
+	public void mousePressed(MouseEvent e)
+	{
+		resetHighlight();
+	}
+
+	public void mouseClicked(MouseEvent e) {}
+	public void mouseEntered(MouseEvent e) {}
+	public void mouseExited(MouseEvent e) {}
+	public void mouseReleased(MouseEvent e) {}
+
+	//  Implement MouseMotionListener
+
+	public void mouseDragged(MouseEvent e)
+	{
+		resetHighlight();
+	}
+
+	public void mouseMoved(MouseEvent e) {}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/LineWrappingTextArea.java
----------------------------------------------------------------------
diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/LineWrappingTextArea.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/LineWrappingTextArea.java
new file mode 100644
index 0000000..dde1566
--- /dev/null
+++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/LineWrappingTextArea.java
@@ -0,0 +1,217 @@
+/*******************************************************************************
+ * 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.lang.ui;
+
+import java.awt.Component;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.text.BreakIterator;
+import java.util.ArrayList;
+
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+
+/**
+ * A JTextArea whose text can be line wrapped either
+ * based on the size of the scroll pane that holds the text area or by a given
+ * line width in characters.
+ * 
+ * @author Alex Nenadic
+ *
+ */
+@SuppressWarnings("serial")
+public class LineWrappingTextArea extends JTextArea{
+		
+	String originalText;
+	String[] wrappedLines;
+	int lineWidth;
+	
+	final FontMetrics fontMetrics;
+	
+	public LineWrappingTextArea(String text){
+		super(text);
+		setFont(new Font("Monospaced", Font.PLAIN,12));
+		fontMetrics  = this.getFontMetrics(this.getFont());
+		setCaretPosition(0);
+		originalText = text; 
+	}
+	
+	/**
+	 * Based on:
+	 * @author Robert Hanson
+	 * http://progcookbook.blogspot.com/2006/02/text-wrapping-function-for-java.html
+	 * 
+	 * This function takes a string value and a line length, and returns an array of 
+	 * lines. Lines are cut on word boundaries, where the word boundary is a space 
+	 * character. Spaces are included as the last character of a word, so most lines 
+	 * will actually end with a space. This isn't too problematic, but will cause a 
+	 * word to wrap if that space pushes it past the max line length.
+	 * 
+	 * This is a modified version - added various word boundaries based on Java's word's 
+	 * BreakIterator in addition to simply space character as in the original function. 
+	 *
+	 * 
+	 */
+	private String [] wrapTextIntoLines (String text, int len)
+	{		
+		// BreakIterator will take care of word boundary characters for us
+		BreakIterator wordIterator = BreakIterator.getWordInstance();
+		wordIterator.setText(text);
+        
+		// return empty array for null text
+		if (text == null)
+			return new String[] {};
+
+		// return text if len is zero or less
+		if (len <= 0)
+			return new String[] { text };
+
+		// return text if less than length
+		if (text.length() <= len)
+			return new String[] { text };
+
+		//char[] chars = text.toCharArray(); // no need to copy the text once again
+		ArrayList<String> lines = new ArrayList<String>();
+		StringBuffer line = new StringBuffer();
+		StringBuffer word = new StringBuffer();
+
+		for (int i = 0; i < text.length(); i++) {
+		//for (int i = 0; i < chars.length; i++) {
+			word.append(text.charAt(i));
+			//word.append(chars[i]);
+
+			if (wordIterator.isBoundary(i)){ // is this character a word boundary?
+			//if (chars[i] == ' ') {
+				if ((line.length() + word.length()) > len) {
+					lines.add(line.toString());
+					line.delete(0, line.length());
+				}
+
+				line.append(word);
+				word.delete(0, word.length());
+			}
+		}
+
+		// handle any extra chars in current word
+		if (word.length() > 0) {
+			if ((line.length() + word.length()) > len) {
+				lines.add(line.toString());
+				line.delete(0, line.length());
+			}
+			line.append(word);
+		}
+
+		// handle extra line
+		if (line.length() > 0) {
+			lines.add(line.toString());
+		}
+
+		String[] ret = new String[lines.size()];
+		int c = 0; // counter
+		for (String line2 : lines) {
+			ret[c++] = line2;
+		}
+
+		return ret;
+	}
+	
+	public void wrapText() {
+		// Figure out how many characters to leave in one line
+		// Based on the size of JTextArea on the screen and font
+		// size - modified from
+		// http://www.coderanch.com/t/517006/GUI/java/Visible-column-row-count-JTextArea
+
+		// Get width of any char (font is monospaced)
+		final int charWidth = fontMetrics.charWidth('M');
+		// Get the width of the visible viewport of the scroll pane
+		// that contains the lot - loop until such a scroll pane is found
+		JScrollPane scrollPane = null;
+		Component currentComponent = getParent();
+		while (true) {
+			if (currentComponent == null) {
+				break;
+			}
+			if (currentComponent instanceof JScrollPane) {
+				scrollPane = (JScrollPane) currentComponent;
+				break;
+			} else {
+				currentComponent = currentComponent.getParent();
+			}
+		}
+		int prefWidth;
+		int maxChars;
+		if (scrollPane == null) { // We did not find the parent scroll pane
+			maxChars = 80; // wrap into lines of 80 characters
+		} else {
+			prefWidth = scrollPane.getVisibleRect().width;
+//			if (scrollPane.getVerticalScrollBar().isVisible()){
+//				prefWidth = prefWidth -	scrollPane.getVerticalScrollBar().getWidth();
+//			}
+			maxChars = prefWidth / charWidth - 3; // -3 because there is some
+													// space between the text
+													// area and the edge of
+													// scroll pane so just to be sure
+		}
+
+		// If we have not wrapped lines before or the
+		// width of the textarea has changed
+		if (wrappedLines == null || lineWidth != maxChars) {
+			lineWidth = maxChars;
+			wrappedLines = wrapTextIntoLines(getText(), lineWidth);
+		}
+
+		if (wrappedLines.length >= 1) {
+			setText(""); // clear the text area
+			StringBuffer buff = new StringBuffer();
+			for (int i = 0; i < wrappedLines.length; i++) {
+				buff.append(wrappedLines[i] + "\n");
+			}
+			// Remove the last \n
+			setText(buff.substring(0, buff.length()-1));
+			repaint();
+		}
+	}
+	
+	public void wrapText(int numCharactersPerLine){
+		// If we have not wrapped lines before or the
+		// number of characters per line has changed since last 
+		// we wrapped
+		if (wrappedLines == null || lineWidth != numCharactersPerLine) {
+			lineWidth = numCharactersPerLine;
+			wrappedLines = wrapTextIntoLines(getText(), lineWidth);
+		}
+
+		if (wrappedLines.length >= 1) {
+			setText(""); // clear the text area
+			for (int i = 0; i < wrappedLines.length; i++) {
+				append(wrappedLines[i] + "\n");
+			}
+			repaint();
+		}
+
+	}
+
+	public void unwrapText(){
+        setText(originalText);	
+		repaint();
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/NoWrapEditorKit.java
----------------------------------------------------------------------
diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/NoWrapEditorKit.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/NoWrapEditorKit.java
new file mode 100644
index 0000000..f032d67
--- /dev/null
+++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/NoWrapEditorKit.java
@@ -0,0 +1,77 @@
+/**
+ * 
+ */
+package net.sf.taverna.t2.lang.ui;
+
+import javax.swing.text.AbstractDocument;
+import javax.swing.text.BoxView;
+import javax.swing.text.ComponentView;
+import javax.swing.text.Element;
+import javax.swing.text.IconView;
+import javax.swing.text.LabelView;
+import javax.swing.text.ParagraphView;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyledEditorKit;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+
+
+/**
+ * 
+ * The following class is copied from http://forums.sun.com/thread.jspa?threadID=622683
+ *
+ */
+public class NoWrapEditorKit extends StyledEditorKit
+{
+	public ViewFactory getViewFactory()
+	{
+			return new StyledViewFactory();
+	} 
+ 
+	static class StyledViewFactory implements ViewFactory
+	{
+		public View create(Element elem)
+		{
+			String kind = elem.getName();
+ 
+			if (kind != null)
+			{
+				if (kind.equals(AbstractDocument.ContentElementName))
+				{
+					return new LabelView(elem);
+				}
+				else if (kind.equals(AbstractDocument.ParagraphElementName))
+				{
+					return new ParagraphView(elem);
+				}
+				else if (kind.equals(AbstractDocument.SectionElementName))
+				{
+					return new NoWrapBoxView(elem, View.Y_AXIS);
+				}
+				else if (kind.equals(StyleConstants.ComponentElementName))
+				{
+					return new ComponentView(elem);
+				}
+				else if (kind.equals(StyleConstants.IconElementName))
+				{
+					return new IconView(elem);
+				}
+			}
+ 
+	 		return new LabelView(elem);
+		}
+	}
+
+	static class NoWrapBoxView extends BoxView {
+        public NoWrapBoxView(Element elem, int axis) {
+            super(elem, axis);
+        }
+ 
+        public void layout(int width, int height) {
+            super.layout(32768, height);
+        }
+        public float getMinimumSpan(int axis) {
+            return super.getPreferredSpan(axis);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/ReadOnlyTextArea.java
----------------------------------------------------------------------
diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/ReadOnlyTextArea.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/ReadOnlyTextArea.java
new file mode 100644
index 0000000..0759605
--- /dev/null
+++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/ReadOnlyTextArea.java
@@ -0,0 +1,50 @@
+/**
+ * 
+ */
+package net.sf.taverna.t2.lang.ui;
+
+import javax.swing.JTextArea;
+import javax.swing.text.Document;
+
+/**
+ * @author alanrw
+ *
+ */
+public class ReadOnlyTextArea extends JTextArea {
+	
+	public ReadOnlyTextArea () {
+		super();
+		setFields();
+	}
+	
+	public ReadOnlyTextArea(Document doc) {
+		super(doc);
+		setFields();
+	}
+	
+	public ReadOnlyTextArea (Document doc, String text, int rows, int columns) {
+		super(doc,text,rows,columns);
+		setFields();
+	}
+	
+	public ReadOnlyTextArea(int rows, int columns) {
+		super(rows, columns);
+		setFields();
+	}
+	
+	public ReadOnlyTextArea(String text) {
+		super(text);
+		setFields();
+	}
+	
+	public ReadOnlyTextArea(String text, int rows, int columns) {
+		super(text, rows, columns);
+		setFields();
+	}
+
+	private void setFields() {
+		super.setEditable(false);
+		super.setLineWrap(true);
+		super.setWrapStyleWord(true);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/SanitisingDocumentFilter.java
----------------------------------------------------------------------
diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/SanitisingDocumentFilter.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/SanitisingDocumentFilter.java
new file mode 100644
index 0000000..2a91e18
--- /dev/null
+++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/SanitisingDocumentFilter.java
@@ -0,0 +1,60 @@
+/**
+ * 
+ */
+package net.sf.taverna.t2.lang.ui;
+
+import java.util.regex.Pattern;
+
+import javax.swing.text.AbstractDocument;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.DocumentFilter;
+import javax.swing.text.JTextComponent;
+
+/**
+ * @author alanrw
+ *
+ */
+public class SanitisingDocumentFilter extends DocumentFilter {
+	
+	private static SanitisingDocumentFilter INSTANCE = new SanitisingDocumentFilter();
+	
+	private SanitisingDocumentFilter () {
+		super();
+	}
+	
+	public static void addFilterToComponent(JTextComponent c) {
+		Document d = c.getDocument();
+		if (d instanceof AbstractDocument) {
+			((AbstractDocument) d).setDocumentFilter(INSTANCE);
+		} 		
+	}
+	
+	public void insertString(DocumentFilter.FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
+		
+		fb.insertString(offset, sanitiseString(string), attr);
+	}
+	
+	public void replace(DocumentFilter.FilterBypass fb, int offset, int length,
+		      String text, javax.swing.text.AttributeSet attr)
+
+		      throws BadLocationException {
+		           fb.replace(offset, length, sanitiseString(text), attr);   
+		 }
+	
+	private static String sanitiseString(String text) {
+		String result = text;
+		if (Pattern.matches("\\w++", text) == false) {
+			result = "";
+			for (char c : text.toCharArray()) {
+				if (Character.isLetterOrDigit(c) || c == '_') {
+					result += c;
+				} else {
+					result += "_";
+				}
+			}
+		}
+		return result;		
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/ShadedLabel.java
----------------------------------------------------------------------
diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/ShadedLabel.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/ShadedLabel.java
new file mode 100644
index 0000000..27ebe33
--- /dev/null
+++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/ShadedLabel.java
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+/**
+ * This file is a component of the Taverna project,
+ * and is licensed under the GNU LGPL.
+ * Copyright Tom Oinn, EMBL-EBI
+ */
+package net.sf.taverna.t2.lang.ui;
+
+import java.awt.Color;
+import java.awt.FlowLayout;
+import java.awt.GradientPaint;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+
+import javax.swing.Box;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.SwingConstants;
+
+/**
+ * A JLabel like component with a shaded background
+ * 
+ * @author Tom Oinn
+ */
+public class ShadedLabel extends JPanel {
+
+	// If you change these, please make sure BLUE is really blue, etc.. :-)
+
+	public static Color ORANGE = new Color(238, 206, 143);
+
+	public static Color BLUE = new Color(213, 229, 246);
+
+	public static Color GREEN = new Color(161, 198, 157);
+
+	final JLabel label;
+
+	Color colour;
+
+	Color toColour = Color.WHITE;
+
+	private String text;
+
+	/**
+	 * Create a ShadedLabel blending from the specified colour to white (left to
+	 * right) and with the specified text.
+	 */
+	public ShadedLabel(String text, Color colour) {
+		this(text, colour, false);
+	}
+
+	/**
+	 * Create a ShadedLabel blending from the specified colour to either white
+	 * if halfShade is false or to a colour halfway between the specified colour
+	 * and white if true and with the specified text
+	 */
+	public ShadedLabel(String text, Color colour, boolean halfShade) {
+		super(new FlowLayout(FlowLayout.LEFT, 0, 3));
+
+		if (halfShade) {
+			toColour = halfShade(colour);
+		}
+		label = new JLabel("",
+				SwingConstants.LEFT);
+		setText(text);
+		label.setOpaque(false);
+		add(Box.createHorizontalStrut(5));
+		add(label);
+		add(Box.createHorizontalStrut(5));
+		setOpaque(false);
+		this.colour = colour;
+	}
+
+	public void setText(String text) {
+		this.text = text;
+		label.setText("<html><body><b>" + text + "</b></body></html>");
+		invalidate();
+	}
+	
+	public String getText() {
+		return text;
+	}
+
+	@Override
+	protected void paintComponent(Graphics g) {
+		final int width = getWidth();
+		final int height = getHeight();
+		Graphics2D g2d = (Graphics2D) g;
+		Paint oldPaint = g2d.getPaint();
+		g2d.setPaint(new GradientPaint(0, 0, this.colour, width, height,
+				toColour));
+		g2d.fillRect(0, 0, width, height);
+		g2d.setPaint(oldPaint);
+		super.paintComponent(g);
+	}
+
+	public static Color halfShade(Color colour) {
+		return new Color((colour.getRed() + 510) / 3,
+				(colour.getGreen() + 510) / 3, (colour.getBlue() + 510) / 3);
+	}
+
+	@Override
+	public boolean isFocusable() {
+		return false;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/TableMap.java
----------------------------------------------------------------------
diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/TableMap.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/TableMap.java
new file mode 100644
index 0000000..dbef4c0
--- /dev/null
+++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/TableMap.java
@@ -0,0 +1,69 @@
+/**
+ * 
+ */
+package net.sf.taverna.t2.lang.ui;
+
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.TableModel;
+import javax.swing.event.TableModelListener;
+import javax.swing.event.TableModelEvent;
+
+/**
+ * Copied from code found at http://www.informit.com/guides/content.aspx?g=java&seqNum=57
+ *
+ */
+public class TableMap extends AbstractTableModel implements TableModelListener {
+	protected TableModel model;
+
+	public TableModel getModel() {
+		return model;
+	}
+
+	public void setModel(TableModel model) {
+		this.model = model;
+		model.addTableModelListener(this);
+	}
+
+	// By default, implement TableModel by forwarding all messages
+	// to the model.
+
+	public Object getValueAt(int aRow, int aColumn) {
+		return model.getValueAt(aRow, aColumn);
+	}
+
+	public void setValueAt(Object aValue, int aRow, int aColumn) {
+		model.setValueAt(aValue, aRow, aColumn);
+	}
+
+	public int getRowCount() {
+		return (model == null) ? 0 : model.getRowCount();
+	}
+
+	public int getColumnCount() {
+		return (model == null) ? 0 : model.getColumnCount();
+	}
+
+	public String getColumnName(int aColumn) {
+		return model.getColumnName(aColumn);
+	}
+
+	public Class getColumnClass(int aColumn) {
+		return model.getColumnClass(aColumn);
+	}
+
+	public boolean isCellEditable(int row, int column) {
+		return model.isCellEditable(row, column);
+	}
+	
+	public int transposeRow(int row) {
+		return row;
+	}
+
+	//
+	// Implementation of the TableModelListener interface,
+	//
+	// By default forward all events to all the listeners.
+	public void tableChanged(TableModelEvent e) {
+		fireTableChanged(e);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/TableSorter.java
----------------------------------------------------------------------
diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/TableSorter.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/TableSorter.java
new file mode 100644
index 0000000..7c7083d
--- /dev/null
+++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/TableSorter.java
@@ -0,0 +1,341 @@
+/**
+ * 
+ */
+package net.sf.taverna.t2.lang.ui;
+
+/**
+ * Copied from code found at http://www.informit.com/guides/content.aspx?g=java&seqNum=57
+ */
+
+import java.util.Date;
+import java.util.Vector;
+
+import javax.swing.table.TableModel;
+import javax.swing.event.TableModelEvent;
+
+//Imports for picking up mouse events from the JTable.
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.InputEvent;
+import javax.swing.JTable;
+import javax.swing.table.JTableHeader;
+import javax.swing.table.TableColumnModel;
+
+public class TableSorter extends TableMap {
+	int indexes[];
+	Vector sortingColumns = new Vector();
+	boolean ascending = true;
+	int compares;
+
+	public TableSorter() {
+		indexes = new int[0]; // for consistency
+	}
+
+	public TableSorter(TableModel model) {
+		setModel(model);
+	}
+
+	public void setModel(TableModel model) {
+		super.setModel(model);
+		reallocateIndexes();
+	}
+
+	public int compareRowsByColumn(int row1, int row2, int column) {
+		Class type = model.getColumnClass(column);
+		TableModel data = model;
+
+		// Check for nulls.
+
+		Object o1 = data.getValueAt(row1, column);
+		Object o2 = data.getValueAt(row2, column);
+
+		// If both values are null, return 0.
+		if (o1 == null && o2 == null) {
+			return 0;
+		} else if (o1 == null) { // Define null less than everything.
+			return -1;
+		} else if (o2 == null) {
+			return 1;
+		}
+
+		if (o1 instanceof Comparable) {
+			return ((Comparable) o1).compareTo(o2);
+		}
+		/*
+		 * We copy all returned values from the getValue call in case an
+		 * optimised model is reusing one object to return many values. The
+		 * Number subclasses in the JDK are immutable and so will not be used in
+		 * this way but other subclasses of Number might want to do this to save
+		 * space and avoid unnecessary heap allocation.
+		 */
+
+		if (type.getSuperclass() == java.lang.Number.class) {
+			Number n1 = (Number) data.getValueAt(row1, column);
+			double d1 = n1.doubleValue();
+			Number n2 = (Number) data.getValueAt(row2, column);
+			double d2 = n2.doubleValue();
+
+			if (d1 < d2) {
+				return -1;
+			} else if (d1 > d2) {
+				return 1;
+			} else {
+				return 0;
+			}
+		} else if (type == java.util.Date.class) {
+			Date d1 = (Date) data.getValueAt(row1, column);
+			long n1 = d1.getTime();
+			Date d2 = (Date) data.getValueAt(row2, column);
+			long n2 = d2.getTime();
+
+			if (n1 < n2) {
+				return -1;
+			} else if (n1 > n2) {
+				return 1;
+			} else {
+				return 0;
+			}
+		} else if (type == String.class) {
+			String s1 = (String) data.getValueAt(row1, column);
+			String s2 = (String) data.getValueAt(row2, column);
+			int result = s1.compareTo(s2);
+
+			if (result < 0) {
+				return -1;
+			} else if (result > 0) {
+				return 1;
+			} else {
+				return 0;
+			}
+		} else if (type == Boolean.class) {
+			Boolean bool1 = (Boolean) data.getValueAt(row1, column);
+			boolean b1 = bool1.booleanValue();
+			Boolean bool2 = (Boolean) data.getValueAt(row2, column);
+			boolean b2 = bool2.booleanValue();
+
+			if (b1 == b2) {
+				return 0;
+			} else if (b1) { // Define false < true
+				return 1;
+			} else {
+				return -1;
+			}
+		} else {
+			Object v1 = data.getValueAt(row1, column);
+			String s1 = v1.toString();
+			Object v2 = data.getValueAt(row2, column);
+			String s2 = v2.toString();
+			int result = s1.compareTo(s2);
+
+			if (result < 0) {
+				return -1;
+			} else if (result > 0) {
+				return 1;
+			} else {
+				return 0;
+			}
+		}
+	}
+
+	public int compare(int row1, int row2) {
+		compares++;
+		for (int level = 0; level < sortingColumns.size(); level++) {
+			Integer column = (Integer) sortingColumns.elementAt(level);
+			int result = compareRowsByColumn(row1, row2, column.intValue());
+			if (result != 0) {
+				return ascending ? result : -result;
+			}
+		}
+		return 0;
+	}
+
+	public void reallocateIndexes() {
+		int rowCount = model.getRowCount();
+
+		// Set up a new array of indexes with the right number of elements
+		// for the new data model.
+		indexes = new int[rowCount];
+
+		// Initialise with the identity mapping.
+		for (int row = 0; row < rowCount; row++) {
+			indexes[row] = row;
+		}
+	}
+
+	public void tableChanged(TableModelEvent e) {
+		// System.out.println("Sorter: tableChanged");
+		reallocateIndexes();
+
+		super.tableChanged(e);
+	}
+
+	public void checkModel() {
+		if (indexes.length != model.getRowCount()) {
+			System.err.println("Sorter not informed of a change in model.");
+		}
+	}
+
+	public void sort(Object sender) {
+		checkModel();
+
+		compares = 0;
+		// n2sort();
+		// qsort(0, indexes.length-1);
+		shuttlesort((int[]) indexes.clone(), indexes, 0, indexes.length);
+		// System.out.println("Compares: "+compares);
+	}
+
+	public void n2sort() {
+		for (int i = 0; i < getRowCount(); i++) {
+			for (int j = i + 1; j < getRowCount(); j++) {
+				if (compare(indexes[i], indexes[j]) == -1) {
+					swap(i, j);
+				}
+			}
+		}
+	}
+
+	// This is a home-grown implementation which we have not had time
+	// to research - it may perform poorly in some circumstances. It
+	// requires twice the space of an in-place algorithm and makes
+	// NlogN assigments shuttling the values between the two
+	// arrays. The number of compares appears to vary between N-1 and
+	// NlogN depending on the initial order but the main reason for
+	// using it here is that, unlike qsort, it is stable.
+	public void shuttlesort(int from[], int to[], int low, int high) {
+		if (high - low < 2) {
+			return;
+		}
+		int middle = (low + high) / 2;
+		shuttlesort(to, from, low, middle);
+		shuttlesort(to, from, middle, high);
+
+		int p = low;
+		int q = middle;
+
+		/*
+		 * This is an optional short-cut; at each recursive call, check to see
+		 * if the elements in this subset are already ordered. If so, no further
+		 * comparisons are needed; the sub-array can just be copied. The array
+		 * must be copied rather than assigned otherwise sister calls in the
+		 * recursion might get out of sinc. When the number of elements is three
+		 * they are partitioned so that the first set, [low, mid), has one
+		 * element and and the second, [mid, high), has two. We skip the
+		 * optimisation when the number of elements is three or less as the
+		 * first compare in the normal merge will produce the same sequence of
+		 * steps. This optimisation seems to be worthwhile for partially ordered
+		 * lists but some analysis is needed to find out how the performance
+		 * drops to Nlog(N) as the initial order diminishes - it may drop very
+		 * quickly.
+		 */
+
+		if (high - low >= 4 && compare(from[middle - 1], from[middle]) <= 0) {
+			for (int i = low; i < high; i++) {
+				to[i] = from[i];
+			}
+			return;
+		}
+
+		// A normal merge.
+
+		for (int i = low; i < high; i++) {
+			if (q >= high || (p < middle && compare(from[p], from[q]) <= 0)) {
+				to[i] = from[p++];
+			} else {
+				to[i] = from[q++];
+			}
+		}
+	}
+
+	public void swap(int i, int j) {
+		int tmp = indexes[i];
+		indexes[i] = indexes[j];
+		indexes[j] = tmp;
+	}
+
+	// The mapping only affects the contents of the data rows.
+	// Pass all requests to these rows through the mapping array: "indexes".
+
+	public Object getValueAt(int aRow, int aColumn) {
+		checkModel();
+		return model.getValueAt(indexes[aRow], aColumn);
+	}
+
+	public void setValueAt(Object aValue, int aRow, int aColumn) {
+		checkModel();
+		model.setValueAt(aValue, indexes[aRow], aColumn);
+	}
+
+	public void sortByColumn(int column) {
+		sortByColumn(column, true);
+	}
+
+	public void sortByColumn(int column, boolean ascending) {
+		this.ascending = ascending;
+		sortingColumns.removeAllElements();
+		sortingColumns.addElement(new Integer(column));
+		sort(this);
+		super.tableChanged(new TableModelEvent(this));
+	}
+	
+	private int lastSortedColumn = -1;
+	private boolean lastAscending = false;
+	
+	public void sortColumn(JTable tableView, int column) {
+		int currentlySelectedRow = tableView.getSelectedRow();
+		int underlyingSelectedRow = -1;
+		if (currentlySelectedRow != -1) {
+			underlyingSelectedRow = transposeRow(currentlySelectedRow);
+		}
+		// System.out.println("Sorting ...");
+		boolean ascendingColumn = true;
+		if (lastSortedColumn == column) {
+			ascendingColumn = !lastAscending;
+		}
+		lastSortedColumn = column;
+		lastAscending = ascendingColumn;
+		this.sortByColumn(column, ascendingColumn);
+		if (underlyingSelectedRow != -1) {
+			for (int row = 0; row < indexes.length; row++) {
+				if (transposeRow(row) == underlyingSelectedRow) {
+					tableView.setRowSelectionInterval(row, row);
+				}
+			}
+		}		
+	}
+	
+	public void resort(JTable tableView) {
+		if (lastSortedColumn != -1) {
+			lastAscending = !lastAscending;
+			sortColumn(tableView, lastSortedColumn);
+		}
+	}
+
+	// There is no-where else to put this.
+	// Add a mouse listener to the Table to trigger a table sort
+	// when a column heading is clicked in the JTable.
+	public void addMouseListenerToHeaderInTable(JTable table) {
+		final TableSorter sorter = this;
+		final JTable tableView = table;
+		tableView.setColumnSelectionAllowed(false);
+		MouseAdapter listMouseListener = new MouseAdapter() {
+			
+			private int lastClickedColumn = -1;
+			private boolean lastAscending = false;
+			public void mouseClicked(MouseEvent e) {
+				TableColumnModel columnModel = tableView.getColumnModel();
+				int viewColumn = columnModel.getColumnIndexAtX(e.getX());
+				int column = tableView.convertColumnIndexToModel(viewColumn);
+				if (e.getClickCount() == 1 && column != -1) {
+					sortColumn(tableView, column);
+				}
+			}
+		};
+		JTableHeader th = tableView.getTableHeader();
+		th.addMouseListener(listMouseListener);
+	}
+	
+	public int transposeRow(int row) {
+		return indexes[row];
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/ValidatingUserInputDialog.java
----------------------------------------------------------------------
diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/ValidatingUserInputDialog.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/ValidatingUserInputDialog.java
new file mode 100644
index 0000000..005b4b4
--- /dev/null
+++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/ValidatingUserInputDialog.java
@@ -0,0 +1,274 @@
+/*******************************************************************************
+ * 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.lang.ui;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.util.Set;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextArea;
+import javax.swing.border.EmptyBorder;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.text.JTextComponent;
+
+import net.sf.taverna.t2.lang.ui.icons.Icons;
+
+/**
+ * A user input dialog that validates the input as the user is entering the
+ * input and gives feedback on why the input is invalid.
+ * 
+ * @author David Withers
+ */
+public class ValidatingUserInputDialog extends JDialog {
+
+	private static final long serialVersionUID = 1L;
+
+	private String inputTitle;
+
+	private JButton okButton;
+
+	private JButton cancelButton;
+
+	private JTextArea inputText;
+
+	private JPanel inputPanel;
+
+	private JLabel iconLabel;
+
+	private boolean valid = true;
+
+	private boolean result = false;
+
+	/**
+	 * Constructs a new instance of ValidatingUserInputDialog.
+	 * 
+	 * @param inputTitle
+	 *            the title for the dialog.
+	 * @param inputMessage
+	 *            the message describing what the user should input.
+	 */
+	public ValidatingUserInputDialog(String inputTitle, JPanel inputPanel) {
+		this.inputTitle = inputTitle;
+		this.inputPanel = inputPanel;
+		initialize();
+	}
+
+	/**
+	 * Adds a text component and the rules for a valid user entry.
+	 * 
+	 * @param textComponent
+	 *            the text component to validate
+	 * @param invalidInputs
+	 *            a set of inputs that are not valid. This is typically a set of
+	 *            already used identifiers to avoid name clashes. Can be an
+	 *            empty set or null.
+	 * @param invalidInputsMessage
+	 *            the message to display if the user enters a value that is in
+	 *            invalidInputs.
+	 * @param inputRegularExpression
+	 *            a regular expression that specifies a valid user input. Can be
+	 *            null.
+	 * @param inputRegularExpressionMessage
+	 *            the message to display if the user enters a value that doesn't
+	 *            match the inputRegularExpression.
+	 */
+	public void addTextComponentValidation(final JTextComponent textComponent,
+			final String inputMessage, final Set<String> invalidInputs,
+			final String invalidInputsMessage,
+			final String inputRegularExpression,
+			final String inputRegularExpressionMessage) {
+		textComponent.getDocument().addDocumentListener(new DocumentListener() {
+			public void changedUpdate(DocumentEvent e) {
+			}
+
+			public void insertUpdate(DocumentEvent e) {
+				verify(textComponent.getText(), inputMessage, invalidInputs,
+						invalidInputsMessage, inputRegularExpression,
+						inputRegularExpressionMessage);
+			}
+
+			public void removeUpdate(DocumentEvent e) {
+				verify(textComponent.getText(), inputMessage, invalidInputs,
+						invalidInputsMessage, inputRegularExpression,
+						inputRegularExpressionMessage);
+			}
+		});
+		textComponent.addKeyListener(new KeyAdapter() {
+			boolean okDown = false;
+			
+			public void keyPressed(KeyEvent e) {
+				if (okButton.isEnabled() && e.getKeyCode() == KeyEvent.VK_ENTER) {
+					okDown = true;
+				}
+			}
+			public void keyReleased(KeyEvent e) {
+				if (okDown && okButton.isEnabled() && e.getKeyCode() == KeyEvent.VK_ENTER) {
+					okButton.doClick();
+				}
+			}
+		});
+		textComponent.addFocusListener(new FocusAdapter() {
+			public void focusGained(FocusEvent e) {
+				if (valid) {
+					setMessage(inputMessage);
+				}
+			}
+		});
+	}
+
+	/**
+	 * Adds a component and a message to display when the component is in focus.
+	 * 
+	 * @param component
+	 *            the component to add
+	 * @param message
+	 *            the message to display when the component is in focus
+	 */
+	public void addMessageComponent(Component component, final String message) {
+		component.addFocusListener(new FocusAdapter() {
+			public void focusGained(FocusEvent e) {
+				if (valid) {
+					setMessage(message);
+				}
+			}
+		});
+	}
+
+	private void initialize() {
+		setLayout(new BorderLayout());
+
+		JPanel messagePanel = new JPanel(new BorderLayout());
+		messagePanel.setBorder(new EmptyBorder(5, 5, 0, 0));
+		messagePanel.setBackground(Color.WHITE);
+
+		add(messagePanel, BorderLayout.NORTH);
+
+		JLabel inputLabel = new JLabel(inputTitle);
+		inputLabel.setBackground(Color.WHITE);
+		Font baseFont = inputLabel.getFont();
+		inputLabel.setFont(baseFont.deriveFont(Font.BOLD));
+		messagePanel.add(inputLabel, BorderLayout.NORTH);
+
+		inputText = new JTextArea();
+		inputText.setMargin(new Insets(5, 10, 10, 10));
+		inputText.setMinimumSize(new Dimension(0, 30));
+		inputText.setFont(baseFont.deriveFont(11f));
+		inputText.setEditable(false);
+		inputText.setFocusable(false);
+		messagePanel.add(inputText, BorderLayout.CENTER);
+
+		iconLabel = new JLabel();
+		messagePanel.add(iconLabel, BorderLayout.WEST);
+
+		add(inputPanel, BorderLayout.CENTER);
+
+		JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING));
+		add(buttonPanel, BorderLayout.SOUTH);
+
+		okButton = new JButton("OK");
+		okButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				result = valid;
+				setVisible(false);
+			}
+		});
+		okButton.setEnabled(false);
+		buttonPanel.add(okButton);
+
+		cancelButton = new JButton("Cancel");
+		cancelButton.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent e) {
+				setVisible(false);
+			}
+		});
+		buttonPanel.add(cancelButton);
+
+		setModal(true);
+		setDefaultCloseOperation(HIDE_ON_CLOSE);
+	}
+
+	public void setMessage(String message) {
+		iconLabel.setIcon(null);
+		inputText.setText(message);
+	}
+
+	public void setWarningMessage(String message) {
+		iconLabel.setIcon(Icons.warningIcon);
+		inputText.setText(message);
+	}
+
+	public void setErrorMessage(String message) {
+		iconLabel.setIcon(Icons.severeIcon);
+		inputText.setText(message);
+	}
+
+	private void verify(String text, String inputMessage,
+			Set<String> invalidInputs, String invalidInputsMessage,
+			String inputRegularExpression, String inputRegularExpressionMessage) {
+		if (invalidInputs != null && invalidInputs.contains(text)) {
+			setErrorMessage(invalidInputsMessage);
+			valid = false;
+		} else if (inputRegularExpression != null
+				&& !text.matches(inputRegularExpression)) {
+			setErrorMessage(inputRegularExpressionMessage);
+			valid = false;
+		} else {
+			setMessage(inputMessage);
+			valid = true;
+		}
+		okButton.setEnabled(valid);
+//		okButton.setSelected(valid);
+	}
+
+	/**
+	 * Show the dialog relative to the component. If the component is null then
+	 * the dialog is shown in the centre of the screen.
+	 * 
+	 * Returns true if the user input is valid.
+	 * 
+	 * @param component
+	 *            the component that the dialog is shown relative to
+	 * @return true if the user input is valid
+	 */
+	public boolean show(Component component) {
+		setLocationRelativeTo(component);
+		setVisible(true);
+		dispose();
+		return result;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/icons/Icons.java
----------------------------------------------------------------------
diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/icons/Icons.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/icons/Icons.java
new file mode 100644
index 0000000..398b3f4
--- /dev/null
+++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/icons/Icons.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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.lang.ui.icons;
+
+import javax.swing.ImageIcon;
+
+import org.apache.log4j.Logger;
+
+public class Icons {
+	
+	private static Logger logger = Logger.getLogger(Icons.class);
+
+
+
+	public static ImageIcon okIcon;
+	public static ImageIcon severeIcon;
+	public static ImageIcon warningIcon;
+
+	static {
+		try {
+			Class c = Icons.class;
+			okIcon = new ImageIcon(c.getResource("ok.png"));
+			severeIcon = new ImageIcon(c.getResource("severe.png"));
+			warningIcon = new ImageIcon(c.getResource("warning.png"));
+
+		} catch (Exception ex) {
+			logger.error("Unable to load standard icons", ex);
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/tabselector/ScrollController.java
----------------------------------------------------------------------
diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/tabselector/ScrollController.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/tabselector/ScrollController.java
new file mode 100644
index 0000000..f7218a6
--- /dev/null
+++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/tabselector/ScrollController.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (C) 2013 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.lang.ui.tabselector;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JButton;
+import javax.swing.JComponent;
+
+/**
+ * Controls tab scrolling when there is not enough space to show all the tabs.
+ *
+ * @author David Withers
+ */
+public class ScrollController {
+
+	private int position;
+	private final JButton scrollLeft;
+	private final JButton scrollRight;
+
+	public ScrollController(final JComponent component) {
+		scrollLeft = new JButton("<");
+		scrollRight = new JButton(">");
+		scrollLeft.setOpaque(true);
+		scrollRight.setOpaque(true);
+		scrollLeft.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				increment();
+				component.doLayout();
+			}
+		});
+		scrollRight.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				decrement();
+				component.doLayout();
+			}
+		});
+	}
+
+	public JButton getScrollLeft() {
+		return scrollLeft;
+	}
+
+	public JButton getScrollRight() {
+		return scrollRight;
+	}
+
+	public int getPosition() {
+		return position;
+	}
+
+	public void reset() {
+		position = 0;
+	}
+
+	public void increment() {
+		position++;
+	}
+
+	public void decrement() {
+		position--;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/tabselector/Tab.java
----------------------------------------------------------------------
diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/tabselector/Tab.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/tabselector/Tab.java
new file mode 100644
index 0000000..3c40b42
--- /dev/null
+++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/tabselector/Tab.java
@@ -0,0 +1,200 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.lang.ui.tabselector;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.RenderingHints;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JToggleButton;
+import javax.swing.plaf.basic.BasicButtonUI;
+
+/**
+ * Tab button that includes a label and a close button.
+ *
+ * @author David Withers
+ */
+public abstract class Tab<T> extends JToggleButton {
+
+	private static final long serialVersionUID = 1L;
+
+	public final static Color midGrey = new Color(160,160,160);
+	public final static Color lightGrey = new Color(200,200,200);
+
+	protected final T selection;
+	private String name;
+	private Icon icon;
+	private JLabel label;
+
+	public Tab(String name, T selection) {
+		this(name, null, selection);
+	}
+
+	public Tab(String name, Icon icon, T selection) {
+		this.name = name;
+		this.icon = icon;
+		this.selection = selection;
+		initialise();
+	}
+
+	private void initialise() {
+		setUI(new BasicButtonUI());
+		setLayout(new GridBagLayout());
+		setOpaque(false);
+		setBackground(Color.red);
+		setBorder(null);
+
+		GridBagConstraints c = new GridBagConstraints();
+
+		label = new JLabel(this.name);
+		label.setIcon(icon);
+		c.anchor = GridBagConstraints.WEST;
+		c.fill = GridBagConstraints.BOTH;
+		c.insets = new Insets(0, 5, 0, 5);
+		c.weightx = 1;
+		add(label, c);
+
+		JButton button = new CloseWorkflowButton();
+		c.fill = GridBagConstraints.NONE;
+		c.insets = new Insets(0, 0, 0, 5);
+		c.weightx = 0;
+		add(button, c);
+
+		addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				clickTabAction();
+			}
+		});
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		if (!this.name.equals(name)) {
+			this.name = name;
+			label.setText(name);
+			repaint();
+		}
+	}
+
+	public void setIcon(Icon icon) {
+		label.setIcon(icon);
+		repaint();
+	}
+
+	@Override
+	public void updateUI() {
+		// override to ignore UI update
+	}
+
+	@Override
+	protected void paintComponent(Graphics g) {
+		super.paintComponent(g);
+		Graphics2D g2 = (Graphics2D) g.create();
+		if (getModel().isPressed()) {
+			g2.translate(1, 1);
+		}
+		if (!getModel().isSelected()) {
+			g2.setColor(lightGrey);
+			g2.fillRoundRect(1, 0, getWidth() - 3, getHeight() - 1, 4, 10);
+		}
+		g2.setColor(midGrey);
+		g2.drawRoundRect(1, 0, getWidth() - 3, getHeight(), 4, 10);
+		if (getModel().isSelected()) {
+			g2.setColor(getParent().getBackground());
+			g2.drawLine(1, getHeight() - 1, getWidth() - 2, getHeight() - 1);
+		}
+		g2.dispose();
+	}
+
+	protected abstract void clickTabAction();
+
+	protected abstract void closeTabAction();
+
+	@SuppressWarnings("serial")
+	private class CloseWorkflowButton extends JButton {
+
+		private final int size = 15;
+		private final int border = 4;
+
+		public CloseWorkflowButton() {
+			setUI(new BasicButtonUI());
+			setPreferredSize(new Dimension(size, size));
+			setMinimumSize(new Dimension(size, size));
+			setContentAreaFilled(false);
+			setFocusable(false);
+			setToolTipText("Close workflow");
+
+			setRolloverEnabled(true);
+			addActionListener(new ActionListener() {
+				@Override
+				public void actionPerformed(ActionEvent e) {
+					closeTabAction();
+				}
+			});
+		}
+
+		@Override
+		public void updateUI() {
+			// override to ignore UI update
+		}
+
+		@Override
+		protected void paintComponent(Graphics g) {
+			super.paintComponent(g);
+			Graphics2D g2 = (Graphics2D) g.create();
+			// animate button press
+			if (getModel().isPressed()) {
+				g2.translate(1, 1);
+			}
+			g2.setColor(midGrey);
+			if (getModel().isRollover()) {
+				// draw armed button
+				g2.fillRoundRect(0, 0, size - 1, size - 1, 4, 4);
+				g2.setColor(Color.GRAY);
+				g2.drawRoundRect(0, 0, size - 1, size - 1, 4, 4);
+				g2.setColor(Color.RED);
+			}
+			// draw 'x'
+			g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
+			g2.setStroke(new BasicStroke(2.5f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
+			g2.drawLine(border, border, size - border, size - border);
+			g2.drawLine(border, size - border, size - border, border);
+			g2.dispose();
+		}
+
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/tabselector/TabLayout.java
----------------------------------------------------------------------
diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/tabselector/TabLayout.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/tabselector/TabLayout.java
new file mode 100644
index 0000000..2a12f0e
--- /dev/null
+++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/tabselector/TabLayout.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.lang.ui.tabselector;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+
+/**
+ * LayoutManager for laying out tabs.
+ * <p>
+ * Tabs are made all the same width and prefer to be maximumTabWidth. If there is more than
+ * preferred width available extra space is left blank. If there is not enough width for tabs to be
+ * maximumTabWidth tab width is reduced until it reaches minimumTabWidth. If there is not enough
+ * width to show tabs at minimumTabWidth scroll buttons are shows and tabs can be scrolled within
+ * the available width.
+ *
+ * @author David Withers
+ */
+public class TabLayout implements LayoutManager {
+
+	public static final int DEFAULT_MINIMUM_TAB_WIDTH = 100;
+	public static final int DEFAULT_MAXIMUM_TAB_WIDTH = 250;
+	public static final int DEFAULT_TAB_HEIGHT = 22;
+	public static final int DEFAULT_SCROLL_BUTTON_WIDTH = 22;
+
+	private final int minimumTabWidth, maximumTabWidth, tabHeight, scrollButtonWidth;
+	private final ScrollController scrollController;
+
+	public TabLayout(ScrollController scrollController) {
+		this(scrollController, DEFAULT_MINIMUM_TAB_WIDTH, DEFAULT_MAXIMUM_TAB_WIDTH,
+				DEFAULT_TAB_HEIGHT, DEFAULT_SCROLL_BUTTON_WIDTH);
+	}
+
+	public TabLayout(ScrollController scrollController, int minimumTabWidth, int maximumTabWidth,
+			int tabHeight, int scrollButtonWidth) {
+		this.scrollController = scrollController;
+		this.minimumTabWidth = minimumTabWidth;
+		this.maximumTabWidth = maximumTabWidth;
+		this.tabHeight = tabHeight;
+		this.scrollButtonWidth = scrollButtonWidth;
+	}
+
+	@Override
+	public void removeLayoutComponent(Component comp) {
+	}
+
+	@Override
+	public void layoutContainer(Container parent) {
+		Component[] components = parent.getComponents();
+		int tabs = components.length - 2;
+		if (tabs > 0) {
+			Insets insets = parent.getInsets();
+			int x = insets.left;
+			int y = insets.top;
+			int availableWidth = parent.getWidth() - insets.left - insets.right;
+			int tabWidth = availableWidth / tabs;
+			boolean showScrollButtons = false;
+			if (tabWidth < minimumTabWidth) {
+				tabWidth = minimumTabWidth;
+				showScrollButtons = true;
+			} else if (tabWidth > maximumTabWidth) {
+				tabWidth = maximumTabWidth;
+			}
+			if (showScrollButtons) {
+				scrollController.getScrollLeft().setLocation(x, y);
+				scrollController.getScrollLeft().setSize(
+						new Dimension(scrollButtonWidth, tabHeight));
+				scrollController.getScrollLeft().setVisible(true);
+				scrollController.getScrollLeft().setEnabled(shouldScrollLeft(tabs, availableWidth));
+				x = x + scrollButtonWidth - (scrollController.getPosition() * minimumTabWidth);
+				scrollController.getScrollRight()
+						.setLocation(availableWidth - scrollButtonWidth, y);
+				scrollController.getScrollRight().setSize(
+						new Dimension(scrollButtonWidth, tabHeight));
+				scrollController.getScrollRight().setVisible(true);
+				scrollController.getScrollRight().setEnabled(scrollController.getPosition() > 0);
+			} else {
+				scrollController.getScrollLeft().setVisible(false);
+				scrollController.getScrollRight().setVisible(false);
+				scrollController.reset();
+			}
+			for (int i = 2; i < components.length; i++) {
+				components[i].setLocation(x, y);
+				components[i].setSize(new Dimension(tabWidth, tabHeight));
+				x = x + tabWidth;
+			}
+		}
+	}
+
+	@Override
+	public void addLayoutComponent(String name, Component comp) {
+	}
+
+	@Override
+	public Dimension minimumLayoutSize(Container parent) {
+		return calculateSize(parent.getInsets(), 2, minimumTabWidth);
+	}
+
+	@Override
+	public Dimension preferredLayoutSize(Container parent) {
+		return calculateSize(parent.getInsets(), parent.getComponents().length, maximumTabWidth);
+	}
+
+	private Dimension calculateSize(Insets insets, int tabs, int tabWidth) {
+		int width = insets.left + insets.right + tabs * tabWidth;
+		int height = insets.top + insets.bottom + tabHeight;
+		return new Dimension(width, height);
+	}
+
+	private boolean shouldScrollLeft(int tabs, int availableWidth) {
+		int tabsToShow = tabs - scrollController.getPosition();
+		return (tabsToShow * minimumTabWidth) > (availableWidth - (scrollButtonWidth * 2));
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/tabselector/TabSelectorComponent.java
----------------------------------------------------------------------
diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/tabselector/TabSelectorComponent.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/tabselector/TabSelectorComponent.java
new file mode 100644
index 0000000..d09b6ec
--- /dev/null
+++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/tabselector/TabSelectorComponent.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (C) 2012 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.lang.ui.tabselector;
+
+import java.awt.BorderLayout;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JPanel;
+
+/**
+ * Component for selecting objects using tabs.
+ *
+ * @author David Withers
+ */
+public abstract class TabSelectorComponent<T> extends JPanel {
+
+	private static final long serialVersionUID = 1L;
+
+	private Map<T, Tab<T>> tabMap;
+	private ButtonGroup tabGroup;
+	private ScrollController scrollController;
+
+	public TabSelectorComponent() {
+		tabMap = new HashMap<T, Tab<T>>();
+		tabGroup = new ButtonGroup();
+		setLayout(new BorderLayout());
+		scrollController = new ScrollController(this);
+		add(scrollController.getScrollLeft());
+		add(scrollController.getScrollRight());
+		setLayout(new TabLayout(scrollController));
+	}
+
+	@Override
+	protected void paintComponent(Graphics g) {
+		super.paintComponent(g);
+		Graphics2D g2 = (Graphics2D) g.create();
+		g2.setColor(Tab.midGrey);
+		g2.drawLine(0, getHeight() - 1, getWidth(), getHeight() - 1);
+		g2.dispose();
+	}
+
+	protected abstract Tab<T> createTab(T object);
+
+	public Tab<T> getTab(T object) {
+		return tabMap.get(object);
+	}
+
+	public void addObject(T object) {
+		Tab<T> button = createTab(object);
+		tabMap.put(object, button);
+		tabGroup.add(button);
+		add(button);
+		revalidate();
+		repaint();
+		button.setSelected(true);
+	}
+
+	public void removeObject(T object) {
+		Tab<T> button = tabMap.remove(object);
+		if (button != null) {
+			tabGroup.remove(button);
+			remove(button);
+			revalidate();
+			repaint();
+		}
+	}
+
+	public void selectObject(T object) {
+		Tab<T> button = tabMap.get(object);
+		if (button != null) {
+			button.setSelected(true);
+		} else {
+			addObject(object);
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/treetable/AbstractCellEditor.java
----------------------------------------------------------------------
diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/treetable/AbstractCellEditor.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/treetable/AbstractCellEditor.java
new file mode 100644
index 0000000..63827bb
--- /dev/null
+++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/treetable/AbstractCellEditor.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * 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.lang.ui.treetable;
+
+import java.util.EventObject;
+
+import javax.swing.CellEditor;
+import javax.swing.event.CellEditorListener;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.EventListenerList;
+
+public class AbstractCellEditor implements CellEditor {
+
+    protected EventListenerList listenerList = new EventListenerList();
+
+    public Object getCellEditorValue() { return null; }
+    public boolean isCellEditable(EventObject e) { return true; }
+    public boolean shouldSelectCell(EventObject anEvent) { return false; }
+    public boolean stopCellEditing() { return true; }
+    public void cancelCellEditing() {}
+
+    public void addCellEditorListener(CellEditorListener l) {
+	listenerList.add(CellEditorListener.class, l);
+    }
+
+    public void removeCellEditorListener(CellEditorListener l) {
+	listenerList.remove(CellEditorListener.class, l);
+    }
+
+    /*
+     * Notify all listeners that have registered interest for
+     * notification on this event type.  
+     * @see EventListenerList
+     */
+    protected void fireEditingStopped() {
+	// Guaranteed to return a non-null array
+	Object[] listeners = listenerList.getListenerList();
+	// Process the listeners last to first, notifying
+	// those that are interested in this event
+	for (int i = listeners.length-2; i>=0; i-=2) {
+	    if (listeners[i]==CellEditorListener.class) {
+		((CellEditorListener)listeners[i+1]).editingStopped(new ChangeEvent(this));
+	    }	       
+	}
+    }
+
+    /*
+     * Notify all listeners that have registered interest for
+     * notification on this event type.  
+     * @see EventListenerList
+     */
+    protected void fireEditingCanceled() {
+	// Guaranteed to return a non-null array
+	Object[] listeners = listenerList.getListenerList();
+	// Process the listeners last to first, notifying
+	// those that are interested in this event
+	for (int i = listeners.length-2; i>=0; i-=2) {
+	    if (listeners[i]==CellEditorListener.class) {
+		((CellEditorListener)listeners[i+1]).editingCanceled(new ChangeEvent(this));
+	    }	       
+	}
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/treetable/AbstractTreeTableModel.java
----------------------------------------------------------------------
diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/treetable/AbstractTreeTableModel.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/treetable/AbstractTreeTableModel.java
new file mode 100644
index 0000000..afec8f3
--- /dev/null
+++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/treetable/AbstractTreeTableModel.java
@@ -0,0 +1,198 @@
+/*
+ * @(#)AbstractTreeTableModel.java	1.2 98/10/27
+ *
+ * Copyright 1997, 1998 by Sun Microsystems, Inc.,
+ * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
+ * All rights reserved.
+ *
+ * This software is the confidential and proprietary information
+ * of Sun Microsystems, Inc. ("Confidential Information").  You
+ * shall not disclose such Confidential Information and shall use
+ * it only in accordance with the terms of the license agreement
+ * you entered into with Sun.
+ */
+package net.sf.taverna.t2.lang.ui.treetable;
+
+import javax.swing.event.EventListenerList;
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.tree.TreePath;
+ 
+/**
+ * @version 1.2 10/27/98
+ * An abstract implementation of the TreeTableModel interface, handling the list 
+ * of listeners. 
+ * @author Philip Milne
+ */
+
+public abstract class AbstractTreeTableModel implements TreeTableModel {
+    protected Object root;     
+    protected EventListenerList listenerList = new EventListenerList();
+  
+    public AbstractTreeTableModel(Object root) {
+        this.root = root; 
+    }
+
+    //
+    // Default implementations for methods in the TreeModel interface. 
+    //
+
+    public Object getRoot() {
+        return root;
+    }
+
+    public boolean isLeaf(Object node) {
+        return getChildCount(node) == 0; 
+    }
+
+    public void valueForPathChanged(TreePath path, Object newValue) {}
+
+    // This is not called in the JTree's default mode: use a naive implementation. 
+    public int getIndexOfChild(Object parent, Object child) {
+        for (int i = 0; i < getChildCount(parent); i++) {
+	    if (getChild(parent, i).equals(child)) { 
+	        return i; 
+	    }
+        }
+	return -1; 
+    }
+
+    public void addTreeModelListener(TreeModelListener l) {
+        listenerList.add(TreeModelListener.class, l);
+    }
+
+    public void removeTreeModelListener(TreeModelListener l) {
+        listenerList.remove(TreeModelListener.class, l);
+    }
+
+    /*
+     * Notify all listeners that have registered interest for
+     * notification on this event type.  The event instance 
+     * is lazily created using the parameters passed into 
+     * the fire method.
+     * @see EventListenerList
+     */
+    protected void fireTreeNodesChanged(Object source, Object[] path, 
+                                        int[] childIndices, 
+                                        Object[] children) {
+        // Guaranteed to return a non-null array
+        Object[] listeners = listenerList.getListenerList();
+        TreeModelEvent e = null;
+        // Process the listeners last to first, notifying
+        // those that are interested in this event
+        for (int i = listeners.length-2; i>=0; i-=2) {
+            if (listeners[i]==TreeModelListener.class) {
+                // Lazily create the event:
+                if (e == null)
+                    e = new TreeModelEvent(source, path, 
+                                           childIndices, children);
+                ((TreeModelListener)listeners[i+1]).treeNodesChanged(e);
+            }          
+        }
+    }
+
+    /*
+     * Notify all listeners that have registered interest for
+     * notification on this event type.  The event instance 
+     * is lazily created using the parameters passed into 
+     * the fire method.
+     * @see EventListenerList
+     */
+    protected void fireTreeNodesInserted(Object source, Object[] path, 
+                                        int[] childIndices, 
+                                        Object[] children) {
+        // Guaranteed to return a non-null array
+        Object[] listeners = listenerList.getListenerList();
+        TreeModelEvent e = null;
+        // Process the listeners last to first, notifying
+        // those that are interested in this event
+        for (int i = listeners.length-2; i>=0; i-=2) {
+            if (listeners[i]==TreeModelListener.class) {
+                // Lazily create the event:
+                if (e == null)
+                    e = new TreeModelEvent(source, path, 
+                                           childIndices, children);
+                ((TreeModelListener)listeners[i+1]).treeNodesInserted(e);
+            }          
+        }
+    }
+
+    /*
+     * Notify all listeners that have registered interest for
+     * notification on this event type.  The event instance 
+     * is lazily created using the parameters passed into 
+     * the fire method.
+     * @see EventListenerList
+     */
+    protected void fireTreeNodesRemoved(Object source, Object[] path, 
+                                        int[] childIndices, 
+                                        Object[] children) {
+        // Guaranteed to return a non-null array
+        Object[] listeners = listenerList.getListenerList();
+        TreeModelEvent e = null;
+        // Process the listeners last to first, notifying
+        // those that are interested in this event
+        for (int i = listeners.length-2; i>=0; i-=2) {
+            if (listeners[i]==TreeModelListener.class) {
+                // Lazily create the event:
+                if (e == null)
+                    e = new TreeModelEvent(source, path, 
+                                           childIndices, children);
+                ((TreeModelListener)listeners[i+1]).treeNodesRemoved(e);
+            }          
+        }
+    }
+
+    /*
+     * Notify all listeners that have registered interest for
+     * notification on this event type.  The event instance 
+     * is lazily created using the parameters passed into 
+     * the fire method.
+     * @see EventListenerList
+     */
+    protected void fireTreeStructureChanged(Object source, Object[] path, 
+                                        int[] childIndices, 
+                                        Object[] children) {
+        // Guaranteed to return a non-null array
+        Object[] listeners = listenerList.getListenerList();
+        TreeModelEvent e = null;
+        // Process the listeners last to first, notifying
+        // those that are interested in this event
+        for (int i = listeners.length-2; i>=0; i-=2) {
+            if (listeners[i]==TreeModelListener.class) {
+                // Lazily create the event:
+                if (e == null)
+                    e = new TreeModelEvent(source, path, 
+                                           childIndices, children);
+                ((TreeModelListener)listeners[i+1]).treeStructureChanged(e);
+            }          
+        }
+    }
+
+    //
+    // Default impelmentations for methods in the TreeTableModel interface. 
+    //
+
+    public Class getColumnClass(int column) { return Object.class; }
+
+   /** By default, make the column with the Tree in it the only editable one. 
+    *  Making this column editable causes the JTable to forward mouse 
+    *  and keyboard events in the Tree column to the underlying JTree. 
+    */ 
+    public boolean isCellEditable(Object node, int column) { 
+         return getColumnClass(column) == TreeTableModel.class; 
+    }
+
+    public void setValueAt(Object aValue, Object node, int column) {}
+
+
+    // Left to be implemented in the subclass:
+
+    /* 
+     *   public Object getChild(Object parent, int index)
+     *   public int getChildCount(Object parent) 
+     *   public int getColumnCount() 
+     *   public String getColumnName(Object node, int column)  
+     *   public Object getValueAt(Object node, int column) 
+     */
+}

http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/treetable/JTreeTable.LICENSE
----------------------------------------------------------------------
diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/treetable/JTreeTable.LICENSE b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/treetable/JTreeTable.LICENSE
new file mode 100644
index 0000000..7500c7a
--- /dev/null
+++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/treetable/JTreeTable.LICENSE
@@ -0,0 +1,59 @@
+The license below applies to the classes contained within the 
+package net.sf.taverna.t2.lang.ui.treetable package. These classes have been taken 
+from the Sun Developer Network tutorial on using JTree components within JTables. The URLs for the tutorials are as follows :
+
+http://java.sun.com/products/jfc/tsc/articles/treetable1/index.html
+http://java.sun.com/products/jfc/tsc/articles/treetable2/index.html
+http://java.sun.com/products/jfc/tsc/articles/bookmarks/index.html#editableJTreeTable
+
+The original web page form of this license is available at :
+
+http://developers.sun.com/license/berkeley_license.html?uid=6910008
+
+Tom Oinn, tmo@ebi.ac.uk, 23rd Feb 2004
+
+-------------------------------------------------------------------------------------
+
+Code sample 
+
+License
+
+      Copyright 1994-2004 Sun Microsystems, Inc. All Rights Reserved. 
+      Redistribution and use in source and binary forms, with or without 
+      modification, are permitted provided that the following conditions are 
+      met: 
+       
+
+      1 Redistribution of source code must retain the above copyright notice, 
+        this list of conditions and the following disclaimer.
+
+
+      2 Redistribution in binary form must reproduce the above copyright notice, 
+        this list of conditions and the following disclaimer in the 
+        documentation and/or other materials provided with the distribution. 
+       
+
+      Neither the name of Sun Microsystems, Inc. or the names of contributors 
+      may be used to endorse or promote products derived from this software 
+      without specific prior written permission. 
+       
+
+      This software is provided "AS IS," without a warranty of any kind. ALL 
+      EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING 
+      ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE 
+      OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") 
+      AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE 
+      AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS 
+      DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST 
+      REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, 
+      INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE 
+      THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS 
+      SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 
+
+       
+
+      You acknowledge that this software is not designed, licensed or intended 
+      for use in the design, construction, operation or maintenance of any 
+      nuclear facility.
+
+-------------------------------------------------------------------------------------
\ No newline at end of file