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, [email protected], 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

Reply via email to