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
