http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-partition/src/main/java/net/sf/taverna/t2/partition/ui/TableTreeNodeRenderer.java ---------------------------------------------------------------------- diff --git a/taverna-partition/src/main/java/net/sf/taverna/t2/partition/ui/TableTreeNodeRenderer.java b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/ui/TableTreeNodeRenderer.java new file mode 100644 index 0000000..12fd2a4 --- /dev/null +++ b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/ui/TableTreeNodeRenderer.java @@ -0,0 +1,554 @@ +/******************************************************************************* + * 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.partition.ui; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JTree; +import javax.swing.UIManager; +import javax.swing.plaf.TreeUI; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; + +public abstract class TableTreeNodeRenderer implements TreeCellRenderer { + + private static final long serialVersionUID = -7291631337751330696L; + + // The difference in indentation between a node and its child nodes, there + // isn't an easy way to get this other than constructing a JTree and + // measuring it - you'd think it would be a property of TreeUI but + // apparently not. + private static int perNodeOffset = -1; + + // Use this to rubber stamp the original node renderer in before rendering + // the table + private TreeCellRenderer nodeRenderer; + + // Determines the space allocated to leaf nodes and their parents when + // applying the stamp defined by the nodeRenderer + private int nodeWidth; + + // Number of pixels of space to leave between the node label and the table + // header or rows + private int labelToTablePad = 3; + + // Number of pixels to leave around the label rendered into the table cells + private int cellPadding = 4; + + // Drawing borders? + private boolean drawingBorders = true; + + // The number of pixels by which the height of the header is reduced + // compared to the row height, this leaves a small border above the header + // and separates it from the last row of the table above, if any. + private int headerTopPad = 4; + + // The proportion of colour : black or colour : white used to create the + // darker or lighter shades is blendFactor : 1 + private int blendFactor = 2; + + // Colour to use to draw the table borders when they're enabled + private Color borderColour = Color.black; + + /** + * Set the colour to be used to draw the borders if they are displayed at + * all. Defaults to black. + */ + public void setBorderColour(Color borderColour) { + this.borderColour = borderColour; + } + + /** + * The blend factor determines how strong the colour component is in the + * shadow and highlight colours used in the bevelled boxes, the ratio of + * black / white to colour is 1 : blendFactor + * + * @param blendFactor + */ + public void setBlendFactor(int blendFactor) { + this.blendFactor = blendFactor; + } + + /** + * Set whether the renderer will draw borders around the table cells - if + * this is false the table still has the bevelled edges of the cell painters + * so will still look semi-bordered. Defaults to true if not otherwise set. + * + * @param drawingBorders + */ + public void setDrawBorders(boolean drawingBorders) { + this.drawingBorders = drawingBorders; + } + + /** + * Override and implement to get the list of columns for a given partition + * node - currently assumes all partitions use the same column structure + * which I need to fix so it doesn't take a partition as argument (yet). + * + * @return an array of column specifications used to drive the renderer + */ + public abstract TableTreeNodeColumn[] getColumns(); + + /** + * Construct a new TableTreeNodeRenderer + * + * @param nodeRenderer + * The inner renderer used to render the node labels + * @param nodeWidth + * Width of the cell space into which the node label is rendered + * in the table header and row nodes + */ + public TableTreeNodeRenderer(TreeCellRenderer nodeRenderer, int nodeWidth) { + super(); + this.nodeRenderer = nodeRenderer; + this.nodeWidth = nodeWidth; + } + + /** + * Do the magic! + */ + public Component getTreeCellRendererComponent(final JTree tree, + final Object value, final boolean selected, final boolean expanded, + final boolean leaf, final int row, final boolean hasFocus) { + final Component nodeLabel = nodeRenderer.getTreeCellRendererComponent( + tree, value, false, expanded, leaf, row, false); + final int nodeLabelHeight = (int) nodeLabel.getPreferredSize() + .getHeight(); + if (leaf) { + // Rendering the leaf nodes, therefore use the table rendering + // strategy + getPerNodeIndentation(tree, row); + return new JComponent() { + private static final long serialVersionUID = 4993815558563895266L; + + @Override + public Dimension getPreferredSize() { + int width = nodeWidth + labelToTablePad; + for (TableTreeNodeColumn column : getColumns()) { + width += column.getColumnWidth(); + } + return new Dimension(width, nodeLabelHeight); + } + + @Override + protected void paintComponent(Graphics g) { + + Graphics2D g2d = (Graphics2D) g.create(); + AffineTransform originalTransform = g2d.getTransform(); + // Enable anti-aliasing for the curved lines + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + + // This method should paint a bevelled container for the + // original label but it doesn't really work terribly well + // as we can't ensure that the original label is actually + // honouring any opaque flags. + if (drawingBorders) { + paintRectangleWithBevel(g2d, nodeWidth + + labelToTablePad, getHeight(), Color.white); + } + + // Paint original node label + nodeLabel.setSize(new Dimension( + nodeWidth - cellPadding * 2, getHeight() + - (drawingBorders ? 2 : 1))); + g2d.translate(cellPadding, 0); + nodeLabel.paint(g2d); + g2d.translate(-cellPadding, 0); + + if (drawingBorders) { + paintRectangleBorder(g2d, nodeWidth + labelToTablePad, + getHeight(), 0, 0, 1, 1, borderColour); + } + + g2d.translate(nodeWidth + labelToTablePad, 0); + boolean first = true; + for (TableTreeNodeColumn column : getColumns()) { + + Color fillColour = column.getColour().brighter(); + Object parentNode = tree.getPathForRow(row) + .getParentPath().getLastPathComponent(); + int indexInParent = tree.getModel().getIndexOfChild( + parentNode, value); + if ((indexInParent & 1) == 1) { + fillColour = new Color( + (fillColour.getRed() + column.getColour() + .getRed()) / 2, (fillColour + .getGreen() + column.getColour() + .getGreen()) / 2, (fillColour + .getBlue() + column.getColour() + .getBlue()) / 2); + } + + // Paint background and bevel + paintRectangleWithBevel(g2d, column.getColumnWidth(), + getHeight(), fillColour); + + // Paint cell component + Component cellComponent = column.getCellRenderer(value); + cellComponent.setSize(new Dimension(column + .getColumnWidth() + - cellPadding * 2, getHeight())); + g2d.translate(cellPadding, 0); + cellComponent.paint(g2d); + g2d.translate(-cellPadding, 0); + + // Draw border + if (drawingBorders) { + paintRectangleBorder(g2d, column.getColumnWidth(), + getHeight(), 0, 1, 1, first ? 1 : 0, + borderColour); + } + first = false; + + g2d.translate(column.getColumnWidth(), 0); + + } + if (selected) { + g2d.setTransform(originalTransform); + g2d.translate(2, 0); + paintRectangleWithHighlightColour(g2d, getWidth() + - (drawingBorders ? 4 : 2), getHeight() + - (drawingBorders ? 2 : 0)); + } + } + }; + } else { + // If there are no child nodes, or there are child nodes but they + // aren't leaves then we render the cell as normal. If there are + // child nodes and the first one is a leaf (we assume this means + // they all are!) then we render the table header after the label. + if (!expanded) { + return getLabelWithHighlight(nodeLabel, selected); + } + // Expanded so do the model check... + TreeModel model = tree.getModel(); + int childCount = model.getChildCount(value); + if (childCount == 0) { + return getLabelWithHighlight(nodeLabel, selected); + } + Object childNode = model.getChild(value, 0); + if (!model.isLeaf(childNode)) { + return getLabelWithHighlight(nodeLabel, selected); + } + getPerNodeIndentation(tree, row); + // Got to here so we need to render a table header. + return new JComponent() { + private static final long serialVersionUID = -4923965850510357216L; + + @Override + public Dimension getPreferredSize() { + int width = nodeWidth + labelToTablePad + perNodeOffset; + for (TableTreeNodeColumn column : getColumns()) { + width += column.getColumnWidth(); + } + return new Dimension(width, nodeLabelHeight); + } + + @Override + protected void paintComponent(Graphics g) { + + Graphics2D g2d = (Graphics2D) g.create(); + AffineTransform originalTransform = g2d.getTransform(); + // Enable anti-aliasing for the curved lines + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + + // Paint original node label + nodeLabel.setSize(new Dimension(nodeWidth + perNodeOffset, + getHeight())); + nodeLabel.paint(g2d); + + // Draw line under label to act as line above table row + // below + if (drawingBorders) { + GeneralPath path = new GeneralPath(); + path.moveTo(perNodeOffset, getHeight() - 1); + path.lineTo( + perNodeOffset + nodeWidth + labelToTablePad, + getHeight() - 1); + g2d.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER)); + g2d.setPaint(borderColour); + g2d.draw(path); + } + + // Move painting origin to the start of the header row + g2d.translate(nodeWidth + perNodeOffset + labelToTablePad, + 0); + + // Paint columns + boolean first = true; + for (TableTreeNodeColumn column : getColumns()) { + + // Paint header cell background with bevel + g2d.translate(0, headerTopPad); + paintRectangleWithBevel(g2d, column.getColumnWidth(), + getHeight() - headerTopPad, column.getColour()); + + // Paint header label + JLabel columnLabel = new JLabel(column.getShortName()); + columnLabel.setSize(new Dimension(column + .getColumnWidth() + - cellPadding * 2, getHeight() - headerTopPad)); + g2d.translate(cellPadding, 0); + columnLabel.paint(g2d); + g2d.translate(-cellPadding, 0); + + // Paint header borders + if (drawingBorders) { + paintRectangleBorder(g2d, column.getColumnWidth(), + getHeight() - headerTopPad, 1, 1, 1, + first ? 1 : 0, borderColour); + } + g2d.translate(0, -headerTopPad); + first = false; + g2d.translate(column.getColumnWidth(), 0); + + } + if (selected) { + g2d.setTransform(originalTransform); + g2d.translate(1, headerTopPad); + paintRectangleWithHighlightColour(g2d, perNodeOffset + + nodeWidth + labelToTablePad + - (drawingBorders ? 2 : 0), getHeight() + - (headerTopPad + 2)); + } + } + }; + + } + + } + + private static Component getLabelWithHighlight(final Component c, + boolean selected) { + if (!selected) { + return c; + } else { + return new JComponent() { + private static final long serialVersionUID = -9175635475959046704L; + + @Override + public Dimension getPreferredSize() { + return c.getPreferredSize(); + } + + @Override + protected void paintComponent(Graphics g) { + Graphics2D g2d = (Graphics2D) g.create(); + c.setSize(new Dimension(getWidth(), getHeight())); + c.paint(g2d); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2d.translate(1, 1); + paintRectangleWithHighlightColour(g2d, getWidth() - 2, + getHeight() - 2); + } + }; + } + } + + private static void paintRectangleBorder(Graphics2D g2d, int width, + int height, int north, int east, int south, int west, Color c) { + Paint oldPaint = g2d.getPaint(); + Stroke oldStroke = g2d.getStroke(); + + g2d.setPaint(c); + + GeneralPath path; + + if (north > 0) { + g2d.setStroke(new BasicStroke(north, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER)); + path = new GeneralPath(); + path.moveTo(0, north - 1); + path.lineTo(width - 1, north - 1); + g2d.draw(path); + } + if (east > 0) { + g2d.setStroke(new BasicStroke(east, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER)); + path = new GeneralPath(); + path.moveTo(width - east, 0); + path.lineTo(width - east, height - 1); + g2d.draw(path); + } + if (south > 0) { + g2d.setStroke(new BasicStroke(south, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER)); + path = new GeneralPath(); + path.moveTo(0, height - south); + path.lineTo(width - 1, height - south); + g2d.draw(path); + } + if (west > 0) { + g2d.setStroke(new BasicStroke(west, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER)); + path = new GeneralPath(); + path.moveTo(west - 1, 0); + path.lineTo(west - 1, height - 1); + g2d.draw(path); + } + + g2d.setPaint(oldPaint); + g2d.setStroke(oldStroke); + } + + /** + * Paint a rectangle with the border colour set from the UIManager + * 'textHighlight' property and filled with the same colour at alpha 50/255. + * Paints from 0,0 to width-1,height-1 into the specified Graphics2D, + * preserving the existing paint and stroke properties on exit. + */ + private static void paintRectangleWithHighlightColour(Graphics2D g2d, + int width, int height) { + GeneralPath path = new GeneralPath(); + path.moveTo(0, 0); + path.lineTo(width - 1, 0); + path.lineTo(width - 1, height - 1); + path.lineTo(0, height - 1); + path.closePath(); + + Paint oldPaint = g2d.getPaint(); + Stroke oldStroke = g2d.getStroke(); + + Color c = UIManager.getColor("textHighlight"); + + g2d.setStroke(new BasicStroke(2, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER)); + g2d.setPaint(c); + g2d.draw(path); + + Color alpha = new Color(c.getRed(), c.getGreen(), c.getBlue(), 50); + g2d.setPaint(alpha); + g2d.fill(path); + + g2d.setPaint(oldPaint); + g2d.setStroke(oldStroke); + + } + + /** + * Paint a bevelled rectangle into the specified Graphics2D with shape from + * 0,0 to width-1,height-1 using the specified colour as a base and + * preserving colour and stroke in the Graphics2D + */ + private void paintRectangleWithBevel(Graphics2D g2d, int width, int height, + Color c) { + if (drawingBorders) { + width = width - 1; + height = height - 1; + } + + GeneralPath path = new GeneralPath(); + path.moveTo(0, 0); + path.lineTo(width - 1, 0); + path.lineTo(width - 1, height - 1); + path.lineTo(0, height - 1); + path.closePath(); + + Paint oldPaint = g2d.getPaint(); + Stroke oldStroke = g2d.getStroke(); + + g2d.setPaint(c); + g2d.fill(path); + + g2d.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER)); + + // Draw highlight (Northeast) + path = new GeneralPath(); + path.moveTo(0, 0); + path.lineTo(width - 1, 0); + path.lineTo(width - 1, height - 1); + Color highlightColour = new Color((c.getRed() * blendFactor + 255) + / (blendFactor + 1), (c.getGreen() * blendFactor + 255) + / (blendFactor + 1), (c.getBlue() * blendFactor + 255) + / (blendFactor + 1)); + g2d.setPaint(highlightColour); + g2d.draw(path); + + // Draw shadow (Southwest) + path = new GeneralPath(); + path.moveTo(0, 0); + path.lineTo(0, height - 1); + path.lineTo(width - 1, height - 1); + Color shadowColour = new Color((c.getRed() * blendFactor) + / (blendFactor + 1), (c.getGreen() * blendFactor) + / (blendFactor + 1), (c.getBlue() * blendFactor) + / (blendFactor + 1)); + g2d.setPaint(shadowColour); + g2d.draw(path); + + g2d.setPaint(oldPaint); + g2d.setStroke(oldStroke); + + } + + /** + * The TreeUI which was used to determine the per node indentation in the + * JTree for which this is a renderer. If this hasn't been set yet then this + * is null. + */ + private static TreeUI cachedTreeUI = null; + + /** + * Use the current TreeUI to determine the indentation per-node in the tree, + * this only works when the treeRow passed in is not the root as it has to + * traverse up to the parent to do anything sensible. Cached and associated + * with the TreeUI so in theory if the look and feel changes the UI changes + * and this is re-generated within the renderer code. + * + * @param tree + * @param treeRow + * @return + */ + private static int getPerNodeIndentation(JTree tree, int treeRow) { + if (perNodeOffset > 0 && tree.getUI() == cachedTreeUI) { + return perNodeOffset; + } + TreeUI uiModel = tree.getUI(); + cachedTreeUI = uiModel; + TreePath path = tree.getPathForRow(treeRow); + Rectangle nodeBounds = uiModel.getPathBounds(tree, path); + Rectangle parentNodeBounds = uiModel.getPathBounds(tree, path + .getParentPath()); + perNodeOffset = (int) nodeBounds.getMinX() + - (int) parentNodeBounds.getMinX(); + return perNodeOffset; + } + +}
http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-partition/src/main/java/net/sf/taverna/t2/partition/ui/UITest.java ---------------------------------------------------------------------- diff --git a/taverna-partition/src/main/java/net/sf/taverna/t2/partition/ui/UITest.java b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/ui/UITest.java new file mode 100644 index 0000000..05a6112 --- /dev/null +++ b/taverna-partition/src/main/java/net/sf/taverna/t2/partition/ui/UITest.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * 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.partition.ui; + +import java.awt.BorderLayout; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JFrame; +import javax.swing.JLabel; + +import net.sf.taverna.t2.partition.PartitionAlgorithm; +import net.sf.taverna.t2.partition.algorithms.LiteralValuePartitionAlgorithm; + +public class UITest extends JFrame { + + private static final long serialVersionUID = -734851883737477053L; + + public UITest() { + super(); + getContentPane().setLayout(new BorderLayout()); + List<PartitionAlgorithm<?>> paList = new ArrayList<PartitionAlgorithm<?>>(); + paList.add(new LiteralValuePartitionAlgorithm()); + paList.add(new LiteralValuePartitionAlgorithm()); + paList.add(new LiteralValuePartitionAlgorithm()); + PartitionAlgorithmListEditor pale = new PartitionAlgorithmListEditor(paList); + getContentPane().add(pale, BorderLayout.NORTH); + setVisible(true); + + } + + public static void main(String[] args) { + JLabel l = new JLabel("Foo"); + System.out.println(l.getPreferredSize()); + System.out.println(l.getWidth()); + + new UITest(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-partition/src/test/java/net/sf/taverna/t2/partition/PartitionTestApplication.java ---------------------------------------------------------------------- diff --git a/taverna-partition/src/test/java/net/sf/taverna/t2/partition/PartitionTestApplication.java b/taverna-partition/src/test/java/net/sf/taverna/t2/partition/PartitionTestApplication.java new file mode 100644 index 0000000..07364f1 --- /dev/null +++ b/taverna-partition/src/test/java/net/sf/taverna/t2/partition/PartitionTestApplication.java @@ -0,0 +1,280 @@ +/******************************************************************************* + * 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.partition; + +import java.awt.Color; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.BoxLayout; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JScrollPane; +import javax.swing.JTree; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.event.TreeModelEvent; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; + +import net.sf.taverna.t2.partition.algorithms.LiteralValuePartitionAlgorithm; +import net.sf.taverna.t2.partition.ui.TableTreeNodeColumn; +import net.sf.taverna.t2.partition.ui.TableTreeNodeRenderer; + +/** + * Exercise the partition algorithm codes + * + * @author Tom Oinn + * + */ +public class PartitionTestApplication { + + final static PropertyExtractorRegistry reg = new ExampleExtractorRegistry(); + + public static void main(String[] args) throws InterruptedException { + try { + // Set System L&F + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + // + } + + JFrame frame = new JFrame("Partition test"); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + System.exit(0); + } + + }); + + RootPartition<ExampleItem> partition = new RootPartition<ExampleItem>( + getAlgorithms(), reg); + JTree partitionTree = new AlwaysOpenJTree(partition); + partitionTree.setRowHeight(24); + TreeCellRenderer oldRenderer = partitionTree.getCellRenderer(); + TableTreeNodeRenderer ttnr = new TableTreeNodeRenderer(oldRenderer, 50) { + @Override + public TableTreeNodeColumn[] getColumns() { + return new TableTreeNodeColumn[] { + new TableTreeNodeColumnImpl("int", new Color(150, 150, + 210), 60,reg), + new TableTreeNodeColumnImpl("float", new Color(150, + 210, 150), 60,reg), + new TableTreeNodeColumnImpl("name", new Color(210, 150, + 150), 60,reg) , + new TableTreeNodeColumnImpl("Fish", new Color(210, 150, + 150), 60,reg) }; + } + }; + + ttnr.setBorderColour(new Color(150, 150, 150)); + + partitionTree.setCellRenderer(ttnr); + + frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(),BoxLayout.Y_AXIS)); + frame.getContentPane().add(new JScrollPane(partitionTree)); + frame.getContentPane().add(new JScrollPane(partitionTree)); + frame.setSize(400, 200); + frame.setVisible(true); + boolean showFrames = false; + //while (true) { + ttnr.setDrawBorders(showFrames); + showFrames = !showFrames; + for (ExampleItem item : exampleItems) { + Thread.sleep(200); + partition.addOrUpdateItem(item); + } +// Thread.sleep(1000); +// for (ExampleItem item : exampleItems) { +// Thread.sleep(400); +// partition.removeItem(item); +// } + //} + } + + static ExampleItem[] exampleItems = new ExampleItem[] { + new ExampleItem("foo", 1, 2.0f), new ExampleItem("bar", 1, 2.0f), + new ExampleItem("foo", 4, 3.7f), new ExampleItem("foo", 3, 2.0f), + new ExampleItem("bar", 1, 3.5f), new ExampleItem("bar", 1, 7.5f), + new ExampleItem("foo", 1, 2.1f), new ExampleItem("bar", 1, 2.3f), + new ExampleItem("foo", 4, 3.8f), new ExampleItem("foo", 3, 2.4f) }; + + static class TableTreeNodeColumnImpl implements TableTreeNodeColumn { + + private String propertyName; + private Color colour; + private int columnWidth; + private PropertyExtractorRegistry reg; + + public TableTreeNodeColumnImpl(String propertyName, Color colour, + int width,PropertyExtractorRegistry reg) { + this.propertyName = propertyName; + this.colour = colour; + this.columnWidth = width; + this.reg=reg; + } + + public Component getCellRenderer(Object value) { + Object propertyValue = reg.getAllPropertiesFor(value).get( + propertyName); + if (propertyValue == null) { + propertyValue = "Not defined"; + } + return new JLabel(propertyValue.toString()); + } + + public Color getColour() { + return this.colour; + } + + public int getColumnWidth() { + return columnWidth; + } + + public String getDescription() { + return "A description..."; + } + + public String getShortName() { + return propertyName; + } + + } + + static List<PartitionAlgorithm<?>> getAlgorithms() { + List<PartitionAlgorithm<?>> paList = new ArrayList<PartitionAlgorithm<?>>(); + LiteralValuePartitionAlgorithm lvpa = new LiteralValuePartitionAlgorithm(); + lvpa.setPropertyName("float"); + + LiteralValuePartitionAlgorithm lvpa2 = new LiteralValuePartitionAlgorithm(); + lvpa2.setPropertyName("int"); + LiteralValuePartitionAlgorithm lvpa3 = new LiteralValuePartitionAlgorithm(); + lvpa3.setPropertyName("name"); + + paList.add(lvpa2); + paList.add(lvpa); + paList.add(lvpa3); + + return paList; + } + + static class ExampleItem implements Comparable<Object>{ + private String name; + private int intValue; + private float floatValue; + + public String getName() { + return this.name; + } + + public int getIntValue() { + return this.intValue; + } + + public float getFloatValue() { + return this.floatValue; + } + + public ExampleItem(String name, int intValue, float floatValue) { + this.name = name; + this.intValue = intValue; + this.floatValue = floatValue; + } + + @Override + public String toString() { + return this.name; + } + + public int compareTo(Object o) { + // TODO Auto-generated method stub + return 0; + } + } + + static class ExampleExtractorRegistry implements PropertyExtractorRegistry { + public Map<String, Object> getAllPropertiesFor(Object target) { + Map<String, Object> properties = new HashMap<String, Object>(); + if (target instanceof ExampleItem) { + ExampleItem item = (ExampleItem) target; + properties.put("name", item.getName()); + properties.put("int", item.getIntValue()); + properties.put("float", item.getFloatValue()); + properties.put("Fish", "Soup"); + } + + return properties; + } + } + + static class AlwaysOpenJTree extends JTree { + + private static final long serialVersionUID = -3769998854485605447L; + + public AlwaysOpenJTree(TreeModel newModel) { + super(newModel); + setEditable(false); + setExpandsSelectedPaths(false); + setDragEnabled(false); + setScrollsOnExpand(false); + // setSelectionModel(SingleSelectionModel.sharedInstance()); + } + + @Override + public void setModel(TreeModel model) { + if (treeModel == model) + return; + if (treeModelListener == null) + treeModelListener = new TreeModelHandler() { + @Override + public void treeNodesInserted(final TreeModelEvent ev) { + if (ev.getChildren()[0] instanceof Partition == false) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + TreePath path = ev.getTreePath(); + setExpandedState(path, true); + fireTreeExpanded(path); + } + }); + } + + } + + }; + if (model != null) { + model.addTreeModelListener(treeModelListener); + } + TreeModel oldValue = treeModel; + treeModel = model; + firePropertyChange(TREE_MODEL_PROPERTY, oldValue, model); + } + + } + +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-partition/src/test/java/net/sf/taverna/t2/partition/algorithms/LiteralValuePartitionAlgorithmTest.java ---------------------------------------------------------------------- diff --git a/taverna-partition/src/test/java/net/sf/taverna/t2/partition/algorithms/LiteralValuePartitionAlgorithmTest.java b/taverna-partition/src/test/java/net/sf/taverna/t2/partition/algorithms/LiteralValuePartitionAlgorithmTest.java new file mode 100644 index 0000000..4e8f93c --- /dev/null +++ b/taverna-partition/src/test/java/net/sf/taverna/t2/partition/algorithms/LiteralValuePartitionAlgorithmTest.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * 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.partition.algorithms; + +import static org.junit.Assert.*; +import net.sf.taverna.t2.partition.algorithms.LiteralValuePartitionAlgorithm; + +import org.junit.Test; + +public class LiteralValuePartitionAlgorithmTest { + + @Test + public void testEquals() { + LiteralValuePartitionAlgorithm a = new LiteralValuePartitionAlgorithm(); + LiteralValuePartitionAlgorithm b = new LiteralValuePartitionAlgorithm(); + LiteralValuePartitionAlgorithm c = new LiteralValuePartitionAlgorithm(); + + a.setPropertyName("cheese"); + b.setPropertyName("cheese"); + c.setPropertyName("butter"); + + assertEquals("They should be equal",a,a); + assertEquals("They should be equal",a,b); + assertFalse("They should not be equal",a.equals(c)); + assertFalse("They should not be equal",a.equals("cheese")); + } + + @Test + public void testHashcode() { + LiteralValuePartitionAlgorithm a = new LiteralValuePartitionAlgorithm(); + LiteralValuePartitionAlgorithm b = new LiteralValuePartitionAlgorithm(); + LiteralValuePartitionAlgorithm c = new LiteralValuePartitionAlgorithm(); + + a.setPropertyName("cheese"); + b.setPropertyName("cheese"); + c.setPropertyName("Z"); + + assertEquals("They should have the same hashcode",a.hashCode(),b.hashCode()); + } + + @Test + public void testConstructor() { + LiteralValuePartitionAlgorithm p = new LiteralValuePartitionAlgorithm(); + assertNull("The property shoudl default to null",p.getPropertyName()); + + p=new LiteralValuePartitionAlgorithm("pea"); + assertEquals("The property name should default to 'pea'","pea",p.getPropertyName()); + } +} + http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/pom.xml ---------------------------------------------------------------------- diff --git a/taverna-ui/pom.xml b/taverna-ui/pom.xml new file mode 100644 index 0000000..6d42dc3 --- /dev/null +++ b/taverna-ui/pom.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>net.sf.taverna.t2</groupId> + <artifactId>lang</artifactId> + <version>2.0.1-SNAPSHOT</version> + </parent> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>ui</artifactId> + <packaging>bundle</packaging> + <name>User interface (Swing) utility classes</name> + <dependencies> + <dependency> + <groupId>net.sf.taverna.t2.lang</groupId> + <artifactId>observer</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>net.sf.taverna.jedit</groupId> + <artifactId>jedit-syntax</artifactId> + <version>${jedit.syntax.version}</version> + </dependency> + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>${commons.io.version}</version> + </dependency> + <dependency> + <groupId>org.apache.log4j</groupId> + <artifactId>com.springsource.org.apache.log4j</artifactId> + <version>${log4j.version}</version> + </dependency> + </dependencies> +</project> http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/CArrowImage.java ---------------------------------------------------------------------- diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/CArrowImage.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/CArrowImage.java new file mode 100644 index 0000000..6a258de --- /dev/null +++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/CArrowImage.java @@ -0,0 +1,198 @@ +/******************************************************************************* + * 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.Color; +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.SystemColor; +import java.awt.RenderingHints.Key; +import java.awt.geom.GeneralPath; +import java.awt.geom.Line2D; +import java.awt.image.BufferedImage; +import java.util.HashMap; +import java.util.Map; + +/** + * A BufferedImage of one of four types of arrow (up, down, left or right) drawn + * to the size specified on the constructor. + */ +public class CArrowImage extends BufferedImage { + // Constants... + public static final int ARROW_UP = 0; + + public static final int ARROW_DOWN = 1; + + public static final int ARROW_LEFT = 2; + + public static final int ARROW_RIGHT = 3; + + // Member variables... + private GeneralPath _pathArrow = new GeneralPath(); + + // Constructor... + public CArrowImage(int nArrowDirection) { + this(15, 9, nArrowDirection); + } + + public CArrowImage(int nWidth, int nHeight, int nArrowDirection) { + super(nWidth, nHeight, TYPE_INT_ARGB_PRE); // Set the width, height and + // image type + + Map<Key, Object> map = new HashMap<Key, Object>(); + map.put(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + map.put(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); + RenderingHints hints = new RenderingHints(map); + + Graphics2D g2 = this.createGraphics(); // Create a graphics context for + // this buffered image + g2.setRenderingHints(hints); + + float h = getHeight(); + float w = getWidth(); + float w13 = w / 3; + float w12 = w / 2; + float w23 = w * 2 / 3; + float h13 = h / 3; + float h12 = h / 2; + float h23 = h * 2 / 3; + + switch (nArrowDirection) { + case ARROW_UP: + _pathArrow.moveTo(w12, h12); + _pathArrow.lineTo(w12, 0); + _pathArrow.lineTo(w, h - 1); + _pathArrow.lineTo(0, h - 1); + _pathArrow.closePath(); + g2.setPaint(new GradientPaint(w13, h13, + SystemColor.controlLtHighlight, w, h - 1, + SystemColor.controlShadow)); + + g2.fill(_pathArrow); + + g2.setColor(SystemColor.controlDkShadow); + g2.draw(new Line2D.Float(0, h - 1, w, h - 1)); + g2.setColor(SystemColor.controlShadow); + g2.draw(new Line2D.Float(w12, 0, w, h - 1)); + g2.setColor(SystemColor.controlLtHighlight); + g2.draw(new Line2D.Float(0, h - 1, w12, 0)); + break; + + case ARROW_DOWN: + _pathArrow.moveTo(w12, h12); + _pathArrow.lineTo(w, 0); + _pathArrow.lineTo(w12, h - 1); + _pathArrow.closePath(); + g2.setPaint(new GradientPaint(0, 0, SystemColor.controlLtHighlight, + w23, h23, SystemColor.controlShadow)); + g2.fill(_pathArrow); + + g2.setColor(SystemColor.controlDkShadow); + g2.draw(new Line2D.Float(w, 0, w12, h - 1)); + g2.setColor(SystemColor.controlShadow); + g2.draw(new Line2D.Float(w12, h - 1, 0, 0)); + g2.setColor(SystemColor.controlLtHighlight); + g2.draw(new Line2D.Float(0, 0, w, 0)); + break; + + case ARROW_LEFT: + _pathArrow.moveTo(w - 1, h13); + _pathArrow.lineTo(w13, h13); + _pathArrow.lineTo(w13, 0); + _pathArrow.lineTo(0, h12); + _pathArrow.lineTo(w13, h - 1); + _pathArrow.lineTo(w13, h23); + _pathArrow.lineTo(w - 1, h23); + _pathArrow.closePath(); + g2.setPaint(new GradientPaint(0, 0, Color.white, // SystemColor. + // controlLtHighlight + // , + 0, h, SystemColor.controlShadow)); + g2.fill(_pathArrow); + + _pathArrow.reset(); + _pathArrow.moveTo(w13, 0); + _pathArrow.lineTo(w13, h13); + _pathArrow.moveTo(w - 1, h13); + _pathArrow.lineTo(w - 1, h23); + _pathArrow.lineTo(w13, h23); + _pathArrow.lineTo(w13, h - 1); + g2.setColor(SystemColor.controlDkShadow); + g2.draw(_pathArrow); + + g2.setColor(SystemColor.controlShadow); + g2.draw(new Line2D.Float(0, h12, w13, h - 1)); + + _pathArrow.reset(); + _pathArrow.moveTo(0, h12); + _pathArrow.lineTo(w13, 0); + _pathArrow.moveTo(w13, h13); + _pathArrow.lineTo(w - 1, h13); + g2.setColor(SystemColor.controlLtHighlight); + g2.draw(_pathArrow); + break; + + case ARROW_RIGHT: + default: { + _pathArrow.moveTo(0, h13); + _pathArrow.lineTo(w23, h13); + _pathArrow.lineTo(w23, 0); + _pathArrow.lineTo(w - 1, h12); + _pathArrow.lineTo(w23, h - 1); + _pathArrow.lineTo(w23, h23); + _pathArrow.lineTo(0, h23); + _pathArrow.closePath(); + g2.setPaint(new GradientPaint(0, 0, Color.white, // SystemColor. + // controlLtHighlight + // , + 0, h, SystemColor.controlShadow)); + g2.fill(_pathArrow); + + _pathArrow.reset(); + _pathArrow.moveTo(0, h23); + _pathArrow.lineTo(w23, h23); + _pathArrow.moveTo(w23, h - 1); + _pathArrow.lineTo(w - 1, h12); + g2.setColor(SystemColor.controlDkShadow); + g2.draw(_pathArrow); + + g2.setColor(SystemColor.controlShadow); + g2.draw(new Line2D.Float(w - 1, h12, w23, 0)); + + _pathArrow.reset(); + _pathArrow.moveTo(w23, 0); + _pathArrow.lineTo(w23, h13); + _pathArrow.lineTo(0, h13); + _pathArrow.lineTo(0, h23); + _pathArrow.moveTo(w23, h23); + _pathArrow.lineTo(w23, h - 1); + g2.setColor(SystemColor.controlLtHighlight); + g2.draw(_pathArrow); + break; + } + } + + } + +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/CTransferableTreePath.java ---------------------------------------------------------------------- diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/CTransferableTreePath.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/CTransferableTreePath.java new file mode 100644 index 0000000..2d85203 --- /dev/null +++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/CTransferableTreePath.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; + +import javax.swing.tree.TreePath; + +/** + * This represents a TreePath (a node in a JTree) that can be transferred + * between a drag source and a drop target. + */ +public class CTransferableTreePath implements Transferable { + // The type of DnD object being dragged... + public static final DataFlavor TREEPATH_FLAVOR = new DataFlavor( + DataFlavor.javaJVMLocalObjectMimeType, "TreePath"); + + private TreePath _path; + + private DataFlavor[] _flavors = { TREEPATH_FLAVOR }; + + /** + * Constructs a transferrable tree path object for the specified path. + */ + public CTransferableTreePath(TreePath path) { + _path = path; + } + + // Transferable interface methods... + public DataFlavor[] getTransferDataFlavors() { + return _flavors; + } + + public boolean isDataFlavorSupported(DataFlavor flavor) { + return java.util.Arrays.asList(_flavors).contains(flavor); + } + + public synchronized Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException { + if (flavor.isMimeTypeEqual(TREEPATH_FLAVOR.getMimeType())) // DataFlavor.javaJVMLocalObjectMimeType)) + return _path; + else + throw new UnsupportedFlavorException(flavor); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/DeselectingButton.java ---------------------------------------------------------------------- diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/DeselectingButton.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/DeselectingButton.java new file mode 100644 index 0000000..ea29adb --- /dev/null +++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/DeselectingButton.java @@ -0,0 +1,45 @@ +/** + * + */ +package net.sf.taverna.t2.lang.ui; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JButton; + +/** + * @author alanrw + * + */ +public class DeselectingButton extends JButton { + + public DeselectingButton(String name, final ActionListener action, String toolTip) { + super(); + this.setAction(new AbstractAction() { + + public void actionPerformed(ActionEvent e) { + Component parent = DeselectingButton.this.getParent(); + action.actionPerformed(e); + parent.requestFocusInWindow(); + } + }); + this.setText(name); + this.setToolTipText(toolTip); + } + + public DeselectingButton(String name, final ActionListener action) { + this(name, action, null); + } + + public DeselectingButton(final Action action, String toolTip) { + this((String) action.getValue(Action.NAME), action, toolTip); + } + + public DeselectingButton(final Action action) { + this(action, null); + } +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/DialogTextArea.java ---------------------------------------------------------------------- diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/DialogTextArea.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/DialogTextArea.java new file mode 100644 index 0000000..faf2643 --- /dev/null +++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/DialogTextArea.java @@ -0,0 +1,83 @@ +/** + * + */ +package net.sf.taverna.t2.lang.ui; + +import java.awt.Font; + +import javax.swing.JTextArea; +import javax.swing.text.Document; + +/** + * @author alanrw + * + */ +public class DialogTextArea extends JTextArea { + + private static Font newFont = Font.decode("Dialog"); + + /** + * + */ + private static final long serialVersionUID = 2329063139827993252L; + + /** + * + */ + public DialogTextArea() { + updateFont(); + } + + /** + * @param text + */ + public DialogTextArea(String text) { + super(text); + updateFont(); + } + + /** + * @param doc + */ + public DialogTextArea(Document doc) { + super(doc); + updateFont(); + } + + /** + * @param rows + * @param columns + */ + public DialogTextArea(int rows, int columns) { + super(rows, columns); + updateFont(); + } + + /** + * @param text + * @param rows + * @param columns + */ + public DialogTextArea(String text, int rows, int columns) { + super(text, rows, columns); + updateFont(); + } + + /** + * @param doc + * @param text + * @param rows + * @param columns + */ + public DialogTextArea(Document doc, String text, int rows, int columns) { + super(doc, text, rows, columns); + updateFont(); + } + + private void updateFont() { + if (newFont != null) { + this.setFont(newFont); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/EdgeLineBorder.java ---------------------------------------------------------------------- diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/EdgeLineBorder.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/EdgeLineBorder.java new file mode 100644 index 0000000..f49faa1 --- /dev/null +++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/EdgeLineBorder.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * 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; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Insets; + +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.LineBorder; + +/** + * + * + * @author David Withers + */ +public class EdgeLineBorder implements Border { + + public static final int TOP = 1; + public static final int BOTTOM = 2; + public static final int LEFT = 3; + public static final int RIGHT = 4; + private final int edge; + private final Color color; + + public EdgeLineBorder(int edge, Color color) { + this.edge = edge; + this.color = color; + } + + @Override + public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + Color oldColor = g.getColor(); + g.setColor(color); + switch (edge) { + case TOP: + g.drawLine(x, y, x+width, y); + break; + case BOTTOM: + g.drawLine(x, y+height-2, x+width, y+height-2); + break; + case LEFT: + g.drawLine(x, y, x+width, y+height); + break; + case RIGHT: + g.drawLine(x+width, y, x+width, y+height); + break; + } + g.setColor(oldColor); + } + + @Override + public Insets getBorderInsets(Component c) { + return new Insets(0, 0, 0, 0); + } + + @Override + public boolean isBorderOpaque() { + return false; + } + + public static void main(String[] args) { + JFrame frame = new JFrame(); + frame.setSize(500, 500); + JPanel panel = new JPanel(); + panel.setBorder(new EdgeLineBorder(TOP, Color.GRAY)); + frame.add(panel); + frame.setVisible(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/EditorKeySetUtil.java ---------------------------------------------------------------------- diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/EditorKeySetUtil.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/EditorKeySetUtil.java new file mode 100644 index 0000000..0e8d908 --- /dev/null +++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/EditorKeySetUtil.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (C) 2009 Ingo Wassink of University of Twente, Netherlands and + * 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 + ******************************************************************************/ + +/** + * @author Ingo Wassink + * @author Ian Dunlop + * @author Alan R Williams + */ +package net.sf.taverna.t2.lang.ui; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashSet; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.log4j.Logger; + +/** + * Manager for reading key set file + * + * @author WassinkI + * @author alanrw + * + */ +public class EditorKeySetUtil { + + private static Logger logger = Logger.getLogger(EditorKeySetUtil.class); + + + public static Set<String> loadKeySet(InputStream stream) { + Set<String> result = new TreeSet<String>(); + try { + BufferedReader reader = new BufferedReader( + new InputStreamReader(stream)); + + String line; + while ((line = reader.readLine()) != null) { + result.add(line.trim()); + } + reader.close(); + } catch (Exception e) { + logger.error(e); + } + 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/ExtensionFileFilter.java ---------------------------------------------------------------------- diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/ExtensionFileFilter.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/ExtensionFileFilter.java new file mode 100644 index 0000000..35e2417 --- /dev/null +++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/ExtensionFileFilter.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * 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.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.swing.filechooser.FileFilter; + +/** + * A FileFilter implementation that can be configured to show only specific file + * suffixes. + * + * @author Tom Oinn + * @author Stian Soiland-Reyes + */ +public class ExtensionFileFilter extends FileFilter { + List<String> allowedExtensions; + + public ExtensionFileFilter(List<String> extensions) { + setAllowedExtensions(extensions); + } + + public ExtensionFileFilter(String[] allowedExtensions) { + setAllowedExtensions(Arrays.asList(allowedExtensions)); + } + + private void setAllowedExtensions(List<String> extensions) { + this.allowedExtensions = new ArrayList<String>(); + for (String e : extensions) { + if (e.startsWith(".")) { + if (e.length() > 1) { + allowedExtensions.add(e.substring(1)); + } + } + else { + allowedExtensions.add(e); + } + } + } + + @Override + public boolean accept(File f) { + if (f.isDirectory()) { + return true; + } + String extension = getExtension(f); + if (extension != null) { + for (String allowedExtension : allowedExtensions) { + if (extension.equalsIgnoreCase(allowedExtension)) { + return true; + } + } + } + return false; + } + + String getExtension(File f) { + String ext = null; + String s = f.getName(); + int i = s.lastIndexOf('.'); + if (i > 0 && i < s.length() - 1) { + ext = s.substring(i + 1).toLowerCase(); + } + return ext; + } + + @Override + public String getDescription() { + StringBuffer sb = new StringBuffer(); + sb.append("Filter for extensions : " ); + for (int i = 0; i < allowedExtensions.size(); i++) { + sb.append(allowedExtensions.get(i)); + if (i < allowedExtensions.size() - 1) { + sb.append(", "); + } + } + return sb.toString(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/FileTools.java ---------------------------------------------------------------------- diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/FileTools.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/FileTools.java new file mode 100644 index 0000000..4aa5bb2 --- /dev/null +++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/FileTools.java @@ -0,0 +1,117 @@ +/** + * + */ +package net.sf.taverna.t2.lang.ui; + +import java.awt.Component; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.prefs.Preferences; + +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; + +import org.apache.commons.io.FileUtils; +import org.apache.log4j.Logger; + +/** + * @author alanrw + * + */ +public class FileTools { + + private static Logger logger = Logger.getLogger(FileTools.class); + + + + public static boolean saveStringToFile(Component parent, String dialogTitle, String extension, String content) { + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setDialogTitle(dialogTitle); + + fileChooser.resetChoosableFileFilters(); + fileChooser.setAcceptAllFileFilterUsed(true); + + fileChooser.setFileFilter(new ExtensionFileFilter(new String[] { extension })); + + Preferences prefs = Preferences.userNodeForPackage(FileTools.class); + String curDir = prefs + .get("currentDir", System.getProperty("user.home")); + fileChooser.setCurrentDirectory(new File(curDir)); + + boolean tryAgain = true; + while (tryAgain) { + tryAgain = false; + int returnVal = fileChooser.showSaveDialog(parent); + if (returnVal == JFileChooser.APPROVE_OPTION) { + prefs.put("currentDir", fileChooser.getCurrentDirectory() + .toString()); + File file = fileChooser.getSelectedFile(); + if (!file.getName().contains(".")) { + String newName = file.getName() + extension; + file = new File(file.getParentFile(), newName); + } + + // TODO: Open in separate thread to avoid hanging UI + try { + if (file.exists()) { + logger.info("File already exists: " + file); + String msg = "Are you sure you want to overwrite existing file " + + file + "?"; + int ret = JOptionPane.showConfirmDialog( + parent, msg, "File already exists", + JOptionPane.YES_NO_CANCEL_OPTION); + if (ret == JOptionPane.YES_OPTION) { + + } else if (ret == JOptionPane.NO_OPTION) { + tryAgain = true; + continue; + } else { + logger.info("Aborted overwrite of " + file); + return false; + } + } + FileUtils.writeStringToFile(file, content, StandardCharsets.UTF_8.name()); + logger.info("Saved content by overwriting " + file); + return true; + } catch (IOException ex) { + logger.warn("Could not save content to " + file, ex); + JOptionPane.showMessageDialog(parent, + "Could not save to " + file + ": \n\n" + + ex.getMessage(), "Warning", + JOptionPane.WARNING_MESSAGE); + return false; + } + } + } + return false; + } + + public static String readStringFromFile(Component parent, String dialogTitle, String extension) { + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setDialogTitle(dialogTitle); + fileChooser.resetChoosableFileFilters(); + fileChooser.setAcceptAllFileFilterUsed(true); + + fileChooser.setFileFilter(new ExtensionFileFilter(new String[] { extension })); + + Preferences prefs = Preferences.userNodeForPackage(FileTools.class); + String curDir = prefs + .get("currentDir", System.getProperty("user.home")); + fileChooser.setCurrentDirectory(new File(curDir)); + + if (fileChooser.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION) { + File selectedFile = fileChooser.getSelectedFile(); + + try { + return FileUtils.readFileToString(selectedFile, StandardCharsets.UTF_8.name()); + } catch (IOException ioe) { + JOptionPane.showMessageDialog(parent, "Can not read file '" + + selectedFile.getName() + "'", "Can not read file", + JOptionPane.ERROR_MESSAGE); + } + + } + return null; + } +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/HtmlUtils.java ---------------------------------------------------------------------- diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/HtmlUtils.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/HtmlUtils.java new file mode 100644 index 0000000..a30d36f --- /dev/null +++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/HtmlUtils.java @@ -0,0 +1,87 @@ +/** + * + */ +package net.sf.taverna.t2.lang.ui; + +import static org.apache.log4j.Logger.getLogger; + +import java.awt.BorderLayout; +import java.awt.Desktop; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.JEditorPane; +import javax.swing.JPanel; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; + +import org.apache.log4j.Logger; + +/** + * @author alanrw + * + */ +public class HtmlUtils { + + private static Logger logger = getLogger(HtmlUtils.class); + + + + public static JEditorPane createEditorPane(String html) { + JEditorPane result = new JEditorPane("text/html", html); + result.addHyperlinkListener(new HyperlinkListener() { + + @Override + public void hyperlinkUpdate(HyperlinkEvent arg0) { + if (HyperlinkEvent.EventType.ACTIVATED == arg0.getEventType()) { + try { + Desktop.getDesktop().browse(arg0.getURL().toURI()); + } catch (IOException | URISyntaxException e1) { + logger.error(e1); + } + } + }}); + result.setEditable(false); + return result; + } + + public static JPanel panelForHtml(JEditorPane editorPane) { + JPanel result = new JPanel(); + + result.setLayout(new BorderLayout()); + + result.add(editorPane, BorderLayout.CENTER); + return result; + } + + public static String getStyle(String backgroundColour) { + String style = "<style type='text/css'>"; + style += "table {align:center; border:solid black 1px; background-color:" + + backgroundColour + + ";width:100%; height:100%; overflow:auto;}"; + style += "</style>"; + return style; + } + + public static String buildTableOpeningTag() { + String result = "<table "; + Map<String, String> props = getTableProperties(); + for (String key : props.keySet()) { + result += key + "=\"" + props.get(key) + "\" "; + } + result += ">"; + return result; + } + + public static Map<String, String> getTableProperties() { + Map<String, String> result = new HashMap<String, String>(); + result.put("border", "1"); + return result; + } + + public static String getHtmlHead(String backgroundColour) { + return "<html><head>" + getStyle(backgroundColour) + "</head><body>"; + } +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/JSplitPaneExt.java ---------------------------------------------------------------------- diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/JSplitPaneExt.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/JSplitPaneExt.java new file mode 100644 index 0000000..e656c36 --- /dev/null +++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/JSplitPaneExt.java @@ -0,0 +1,56 @@ +/** + * + */ +package net.sf.taverna.t2.lang.ui; + +import java.awt.Graphics; + +import javax.swing.JSplitPane; + +/** + * Copied from code found on http://www.jguru.com + * + */ +public class JSplitPaneExt extends JSplitPane { + + protected boolean m_fIsPainted = false; + protected double m_dProportionalLocation = -1; + + public JSplitPaneExt() { + super(); + } + + public JSplitPaneExt(int iOrientation) { + super(iOrientation); + } + + protected boolean hasProportionalLocation() { + return (m_dProportionalLocation != -1); + } + + public void cancelDividerProportionalLocation() { + m_dProportionalLocation = -1; + } + + public void setDividerLocation(double dProportionalLocation) { + if (dProportionalLocation < 0 || dProportionalLocation > 1) { + throw new IllegalArgumentException( + "Illegal value for divider location: " + + dProportionalLocation); + } + m_dProportionalLocation = dProportionalLocation; + if (m_fIsPainted) { + super.setDividerLocation(m_dProportionalLocation); + } + } + + public void paint(Graphics g) { + super.paint(g); + if (hasProportionalLocation()) { + super.setDividerLocation(m_dProportionalLocation); + } + m_fIsPainted=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/KeywordDocument.java ---------------------------------------------------------------------- diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/KeywordDocument.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/KeywordDocument.java new file mode 100644 index 0000000..e8fae14 --- /dev/null +++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/KeywordDocument.java @@ -0,0 +1,568 @@ +/******************************************************************************* + * Copyright (C) 2009 Ingo Wassink of University of Twente, Netherlands and + * 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 + ******************************************************************************/ + +/** + * @author Ingo Wassink + * @author Ian Dunlop + * @author Alan R Williams + */ +package net.sf.taverna.t2.lang.ui; + +import java.awt.Color; +import java.util.Set; + +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.DefaultEditorKit; +import javax.swing.text.DefaultStyledDocument; +import javax.swing.text.Element; +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.StyleConstants; + +import org.apache.log4j.Logger; + +public class KeywordDocument extends DefaultStyledDocument { + + private static Logger logger = Logger + .getLogger(KeywordDocument.class); + + private DefaultStyledDocument doc; + private Element rootElement; + + private boolean multiLineComment; + private MutableAttributeSet normal; + private MutableAttributeSet keyword; + private MutableAttributeSet comment; + private MutableAttributeSet quote; + private MutableAttributeSet port; + + private Set<String> keywords; + + private Set<String> ports; + + + + public KeywordDocument(Set<String> keywords, Set<String> ports) { + doc = this; + rootElement = doc.getDefaultRootElement(); + putProperty(DefaultEditorKit.EndOfLineStringProperty, "\n"); + + normal = new SimpleAttributeSet(); + StyleConstants.setForeground(normal, Color.black); + + comment = new SimpleAttributeSet(); + StyleConstants.setForeground(comment, new Color(0, 139, 69, 255)); + StyleConstants.setItalic(comment, true); + + keyword = new SimpleAttributeSet(); + StyleConstants.setForeground(keyword, Color.blue); + StyleConstants.setBold(keyword, true); + + + port = new SimpleAttributeSet(); + StyleConstants.setForeground(port, Color.magenta); + + quote = new SimpleAttributeSet(); + StyleConstants.setForeground(quote, Color.red); + + this.keywords = keywords; + this.ports = ports; + } + + /** + * Method for adding an port + * @param name the name of the port + */ + public void addPort(String name){ + ports.add(name); + updateText(); + } + + /** + * Method for removing an port + * @param name the name of the port + */ + public void removePort(String name){ + ports.remove(name); + updateText(); + } + + /** + * Method for checking whether the name represents an input port + * @param name the name of the port + * @return true if true + */ + private boolean isPort(String name){ + return ports.contains(name); + } + + + /** + * Method for updating the whole text + */ + private void updateText(){ + try{ + processChangedLines(0, getLength() ); + } catch(Exception e){ + logger.error("Unable to update text", e); + } + } + + /* + * Override to apply syntax highlighting after the document has been updated + */ + public void insertString(int offset, String str, AttributeSet a) + throws BadLocationException { + if (str.equals("{")) + str = addMatchingBrace(offset); + + super.insertString(offset, str, a); + processChangedLines(offset, str.length()); + } + + /* + * Override to apply syntax highlighting after the document has been updated + */ + public void remove(int offset, int length) throws BadLocationException { + super.remove(offset, length); + processChangedLines(offset, 0); + } + + /* + * Determine how many lines have been changed, then apply highlighting to + * each line + */ + public void processChangedLines(int offset, int length) + throws BadLocationException { + String content = doc.getText(0, doc.getLength()); + + // The lines affected by the latest document update + + int startLine = rootElement.getElementIndex(offset); + int endLine = rootElement.getElementIndex(offset + length); + + // Make sure all comment lines prior to the start line are commented + // and determine if the start line is still in a multi line comment + + setMultiLineComment(commentLinesBefore(content, startLine)); + + // Do the actual highlighting + + for (int i = startLine; i <= endLine; i++) { + applyHighlighting(content, i); + } + + // Resolve highlighting to the next end multi line delimiter + + if (isMultiLineComment()) + commentLinesAfter(content, endLine); + else + highlightLinesAfter(content, endLine); + } + + /* + * Highlight lines when a multi line comment is still 'open' (ie. matching + * end delimiter has not yet been encountered) + */ + private boolean commentLinesBefore(String content, int line) { + int offset = rootElement.getElement(line).getStartOffset(); + + // Start of comment not found, nothing to do + + int startDelimiter = lastIndexOf(content, getStartDelimiter(), + offset - 2); + + if (startDelimiter < 0) + return false; + + // Matching start/end of comment found, nothing to do + + int endDelimiter = indexOf(content, getEndDelimiter(), startDelimiter); + + if (endDelimiter < offset & endDelimiter != -1) + return false; + + // End of comment not found, highlight the lines + + doc.setCharacterAttributes(startDelimiter, offset - startDelimiter + 1, + comment, false); + return true; + } + + /* + * Highlight comment lines to matching end delimiter + */ + private void commentLinesAfter(String content, int line) { + int offset = rootElement.getElement(line).getEndOffset(); + + // End of comment not found, nothing to do + + int endDelimiter = indexOf(content, getEndDelimiter(), offset); + + if (endDelimiter < 0) + return; + + // Matching start/end of comment found, comment the lines + + int startDelimiter = lastIndexOf(content, getStartDelimiter(), + endDelimiter); + + if (startDelimiter < 0 || startDelimiter <= offset) { + doc.setCharacterAttributes(offset, endDelimiter - offset + 1, + comment, false); + } + } + + /* + * Highlight lines to start or end delimiter + */ + private void highlightLinesAfter(String content, int line) + throws BadLocationException { + int offset = rootElement.getElement(line).getEndOffset(); + + // Start/End delimiter not found, nothing to do + + int startDelimiter = indexOf(content, getStartDelimiter(), offset); + int endDelimiter = indexOf(content, getEndDelimiter(), offset); + + if (startDelimiter < 0) + startDelimiter = content.length(); + + if (endDelimiter < 0) + endDelimiter = content.length(); + + int delimiter = Math.min(startDelimiter, endDelimiter); + + if (delimiter < offset) + return; + + // Start/End delimiter found, reapply highlighting + + int endLine = rootElement.getElementIndex(delimiter); + + for (int i = line + 1; i < endLine; i++) { + Element branch = rootElement.getElement(i); + Element leaf = doc.getCharacterElement(branch.getStartOffset()); + AttributeSet as = leaf.getAttributes(); + + if (as.isEqual(comment)) + applyHighlighting(content, i); + } + } + + /* + * Parse the line to determine the appropriate highlighting + */ + private void applyHighlighting(String content, int line) + throws BadLocationException { + int startOffset = rootElement.getElement(line).getStartOffset(); + int endOffset = rootElement.getElement(line).getEndOffset() - 1; + + int lineLength = endOffset - startOffset; + int contentLength = content.length(); + + if (endOffset >= contentLength) + endOffset = contentLength - 1; + + // check for multi line comments + // (always set the comment attribute for the entire line) + + if (endingMultiLineComment(content, startOffset, endOffset) + || isMultiLineComment() + || startingMultiLineComment(content, startOffset, endOffset)) { + doc.setCharacterAttributes(startOffset, + endOffset - startOffset + 1, comment, false); + return; + } + + // set normal attributes for the line + + doc.setCharacterAttributes(startOffset, lineLength, normal, true); + + // check for single line comment + + int index = content.indexOf(getSingleLineDelimiter(), startOffset); + + if ((index > -1) && (index < endOffset)) { + doc.setCharacterAttributes(index, endOffset - index + 1, comment, + false); + endOffset = index - 1; + } + + // check for tokens + + checkForTokens(content, startOffset, endOffset); + } + + /* + * Does this line contain the start delimiter + */ + private boolean startingMultiLineComment(String content, int startOffset, + int endOffset) throws BadLocationException { + int index = indexOf(content, getStartDelimiter(), startOffset); + + if ((index < 0) || (index > endOffset)) + return false; + else { + setMultiLineComment(true); + return true; + } + } + + /* + * Does this line contain the end delimiter + */ + private boolean endingMultiLineComment(String content, int startOffset, + int endOffset) throws BadLocationException { + int index = indexOf(content, getEndDelimiter(), startOffset); + + if ((index < 0) || (index > endOffset)) + return false; + else { + setMultiLineComment(false); + return true; + } + } + + /* + * We have found a start delimiter and are still searching for the end + * delimiter + */ + private boolean isMultiLineComment() { + return false;//multiLineComment; + } + + private void setMultiLineComment(boolean value) { + multiLineComment = value; + } + + /* + * Parse the line for tokens to highlight + */ + private void checkForTokens(String content, int startOffset, int endOffset) { + while (startOffset <= endOffset) { + // skip the delimiters to find the start of a new token + + while (isDelimiter(content.substring(startOffset, startOffset + 1))) { + if (startOffset < endOffset) + startOffset++; + else + return; + } + + // Extract and process the entire token + + if (isQuoteDelimiter(content + .substring(startOffset, startOffset + 1))) + startOffset = getQuoteToken(content, startOffset, endOffset); + else + startOffset = getOtherToken(content, startOffset, endOffset); + } + } + + /* + * + */ + private int getQuoteToken(String content, int startOffset, int endOffset) { + String quoteDelimiter = content.substring(startOffset, startOffset + 1); + String escapeString = getEscapeString(quoteDelimiter); + + int index; + int endOfQuote = startOffset; + + // skip over the escape quotes in this quote + + index = content.indexOf(escapeString, endOfQuote + 1); + + while ((index > -1) && (index < endOffset)) { + endOfQuote = index + 1; + index = content.indexOf(escapeString, endOfQuote); + } + + // now find the matching delimiter + + index = content.indexOf(quoteDelimiter, endOfQuote + 1); + + if ((index < 0) || (index > endOffset)) + endOfQuote = endOffset; + else + endOfQuote = index; + + doc.setCharacterAttributes(startOffset, endOfQuote - startOffset + 1, + quote, false); + + return endOfQuote + 1; + } + + /* + * + */ + private int getOtherToken(String content, int startOffset, int endOffset) { + int endOfToken = startOffset + 1; + + while (endOfToken <= endOffset) { + if (isDelimiter(content.substring(endOfToken, endOfToken + 1))) + break; + + endOfToken++; + } + + String token = content.substring(startOffset, endOfToken); + + if (isKeyword(token)) { + doc.setCharacterAttributes(startOffset, endOfToken - startOffset, + keyword, false); + } else if(isPort(token)){ + doc.setCharacterAttributes(startOffset, endOfToken - startOffset, + port, false); + } + + return endOfToken + 1; + } + + /* + * Assume the needle will the found at the start/end of the line + */ + private int indexOf(String content, String needle, int offset) { + int index; + + while ((index = content.indexOf(needle, offset)) != -1) { + String text = getLine(content, index).trim(); + + if (text.startsWith(needle) || text.endsWith(needle)) + break; + else + offset = index + 1; + } + + return index; + } + + /* + * Assume the needle will the found at the start/end of the line + */ + private int lastIndexOf(String content, String needle, int offset) { + int index; + + while ((index = content.lastIndexOf(needle, offset)) != -1) { + String text = getLine(content, index).trim(); + + if (text.startsWith(needle) || text.endsWith(needle)) + break; + else + offset = index - 1; + } + + return index; + } + + private String getLine(String content, int offset) { + int line = rootElement.getElementIndex(offset); + Element lineElement = rootElement.getElement(line); + int start = lineElement.getStartOffset(); + int end = lineElement.getEndOffset(); + return content.substring(start, end - 1); + } + + /* + * Override for other languages + */ + protected boolean isDelimiter(String character) { + String operands = ";:{}()[]+-/%<=>!&|^~*,."; + + if (Character.isWhitespace(character.charAt(0)) + || operands.indexOf(character) != -1) + return true; + else + return false; + } + + /* + * Override for other languages + */ + protected boolean isQuoteDelimiter(String character) { + String quoteDelimiters = "\"'"; + + if (quoteDelimiters.indexOf(character) < 0) + return false; + else + return true; + } + + /* + * Override for other languages + */ + protected boolean isKeyword(String token) { + return keywords.contains(token); + } + + /* + * Override for other languages + */ + protected String getStartDelimiter() { + return "/*"; + } + + /* + * Override for other languages + */ + protected String getEndDelimiter() { + return "*/"; + } + + /* + * Override for other languages + */ + protected String getSingleLineDelimiter() { + return "#"; + } + + /* + * Override for other languages + */ + protected String getEscapeString(String quoteDelimiter) { + return "\\" + quoteDelimiter; + } + + /* + * + */ + protected String addMatchingBrace(int offset) throws BadLocationException { + StringBuffer whiteSpace = new StringBuffer(); + int line = rootElement.getElementIndex(offset); + int i = rootElement.getElement(line).getStartOffset(); + + while (true) { + String temp = doc.getText(i, 1); + + if (temp.equals(" ") || temp.equals("\t")) { + whiteSpace.append(temp); + i++; + } else + break; + } + + return "{\n" + whiteSpace.toString() + "\t\n" + whiteSpace.toString() + + "}"; + } +} http://git-wip-us.apache.org/repos/asf/incubator-taverna-workbench/blob/f676ef35/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/LineEnabledTextPanel.java ---------------------------------------------------------------------- diff --git a/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/LineEnabledTextPanel.java b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/LineEnabledTextPanel.java new file mode 100644 index 0000000..389b9b3 --- /dev/null +++ b/taverna-ui/src/main/java/net/sf/taverna/t2/lang/ui/LineEnabledTextPanel.java @@ -0,0 +1,106 @@ +/** + * + */ +package net.sf.taverna.t2.lang.ui; + +import java.awt.BorderLayout; +import java.awt.Event; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +import javax.swing.AbstractAction; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.KeyStroke; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; +import javax.swing.text.Document; +import javax.swing.text.Element; +import javax.swing.text.JTextComponent; + + +/** + * @author alanrw + * + */ +public class LineEnabledTextPanel extends JPanel { + + private JTextComponent textComponent = null; + private Document document; + private GotoLineAction gotoLineAction = null; + + public LineEnabledTextPanel(final JTextComponent component) { + + this.setLayout(new BorderLayout()); + textComponent = component; + updateDocument(); + + JScrollPane scrollPane = new JScrollPane(textComponent ); + scrollPane.setPreferredSize(textComponent.getPreferredSize() ); + + this.add(scrollPane, BorderLayout.CENTER);; + + final JLabel caretLabel = new JLabel("Line: 1 Column: 0"); + + setCaretListener(new CaretListener() { + + public void caretUpdate(CaretEvent e) { + int caretPosition = getCaretPosition(); + Element root = document.getRootElements()[0]; + int elementIndex = root.getElementIndex(caretPosition); + int relativeOffset = caretPosition - root.getElement(elementIndex).getStartOffset(); + caretLabel.setText("Line: " + (elementIndex + 1) + " Column: " + relativeOffset); + + }}); + this.add(caretLabel, BorderLayout.SOUTH); + + KeyStroke gotoLineKeystroke = KeyStroke.getKeyStroke(KeyEvent.VK_L, Event.META_MASK); + + gotoLineAction = new GotoLineAction(); + textComponent.getInputMap().put(gotoLineKeystroke, "gotoLineKeystroke"); + textComponent.getActionMap().put("gotoLineKeystroke", gotoLineAction); + + } + + private void updateDocument() { + document = ((JTextComponent) textComponent).getDocument(); + } + + private void setCaretListener(CaretListener listener) { + ((JTextComponent) textComponent).addCaretListener(listener); + } + + private int getCaretPosition() { + return ((JTextComponent) textComponent).getCaretPosition(); + } + + private void setCaretPosition(int position) { + ((JTextComponent) textComponent).setCaretPosition(position); + textComponent.requestFocus(); + } + + class GotoLineAction extends AbstractAction + { + + public GotoLineAction() { + } + + public void actionPerformed(ActionEvent e) { + String inputString = JOptionPane.showInputDialog(null, "Enter line number", "Line number", JOptionPane.QUESTION_MESSAGE); + if (inputString != null) { + try { + int lineNumber = Integer.parseInt(inputString); + Element root = document.getDefaultRootElement(); + lineNumber = Math.max(lineNumber, 1); + lineNumber = Math.min(lineNumber, root.getElementCount()); + setCaretPosition( root.getElement( lineNumber - 1 ).getStartOffset() ); + + } catch (NumberFormatException e1){ + // do nothing + } + } + } + } +}
