| /******************************************************************************* |
| * Copyright (C) 2007 The University of Manchester |
| * |
| * Modifications to the initial code base are copyright of their |
| * respective authors, or their employers as appropriate. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public License |
| * as published by the Free Software Foundation; either version 2.1 of |
| * the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
| ******************************************************************************/ |
| package net.sf.taverna.t2.workbench.views.results.workflow; |
| |
| import static java.awt.BorderLayout.CENTER; |
| import static java.awt.BorderLayout.NORTH; |
| import static java.awt.BorderLayout.WEST; |
| import static java.awt.Font.BOLD; |
| import static java.lang.Math.round; |
| import static javax.swing.JSplitPane.HORIZONTAL_SPLIT; |
| import static javax.swing.SwingUtilities.invokeLater; |
| import static javax.swing.tree.TreeSelectionModel.SINGLE_TREE_SELECTION; |
| |
| import java.awt.BorderLayout; |
| import java.awt.Font; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.nio.file.Path; |
| import java.util.ArrayList; |
| import java.util.Enumeration; |
| import java.util.List; |
| |
| import javax.swing.JComboBox; |
| import javax.swing.JLabel; |
| import javax.swing.JPanel; |
| import javax.swing.JScrollPane; |
| import javax.swing.JSplitPane; |
| import javax.swing.JTree; |
| import javax.swing.event.TreeModelEvent; |
| import javax.swing.event.TreeModelListener; |
| import javax.swing.event.TreeSelectionEvent; |
| import javax.swing.event.TreeSelectionListener; |
| import javax.swing.tree.DefaultMutableTreeNode; |
| import javax.swing.tree.TreePath; |
| |
| import net.sf.taverna.t2.renderers.RendererRegistry; |
| import net.sf.taverna.t2.workbench.ui.Updatable; |
| import net.sf.taverna.t2.workbench.views.results.saveactions.SaveIndividualResultSPI; |
| import net.sf.taverna.t2.workbench.views.results.workflow.FilteredDataBundleTreeModel.FilterType; |
| import uk.org.taverna.databundle.DataBundles; |
| import uk.org.taverna.scufl2.api.port.Port; |
| |
| /** |
| * A tab containing result tree for an output port and a panel with rendered result |
| * of the currently selected node in the tree. |
| * |
| * @author Alex Nenadic |
| */ |
| @SuppressWarnings("serial") |
| public class PortResultsViewTab extends JPanel implements Updatable { |
| private enum State {NO_DATA, SINGLE_VALUE, LIST} |
| |
| private State state; |
| /** Tree model of results */ |
| private DataBundleTreeModel resultModel; |
| private FilteredDataBundleTreeModel filteredTreeModel; |
| /** Rendered result component */ |
| private RenderedResultComponent renderedResultComponent; |
| private JTree tree; |
| private JComboBox<FilterType> filterChoiceBox; |
| private final RendererRegistry rendererRegistry; |
| private final List<SaveIndividualResultSPI> saveActions; |
| private final Port port; |
| private Path value; |
| |
| public PortResultsViewTab(Port port, Path value, RendererRegistry rendererRegistry, |
| List<SaveIndividualResultSPI> saveActions) { |
| super(new BorderLayout()); |
| this.port = port; |
| this.value = value; |
| this.rendererRegistry = rendererRegistry; |
| this.saveActions = saveActions; |
| |
| initComponents(); |
| } |
| |
| private void initComponents() { |
| removeAll(); |
| if (value == null || DataBundles.isMissing(value)) { |
| state = State.NO_DATA; |
| JLabel noDataMessage = new JLabel("No data available", JLabel.CENTER); |
| Font font = noDataMessage.getFont(); |
| if (font != null) { |
| font = font.deriveFont(round(font.getSize() * 1.5)).deriveFont(BOLD); |
| noDataMessage.setFont(font); |
| } |
| add(noDataMessage, CENTER); |
| } else if (DataBundles.isList(value)) { |
| state = State.LIST; |
| initListComponents(); |
| } else { |
| state = State.SINGLE_VALUE; |
| // Component for rendering individual results |
| renderedResultComponent = new RenderedResultComponent(rendererRegistry, saveActions); |
| renderedResultComponent.setPath(value); |
| add(renderedResultComponent, CENTER); |
| } |
| revalidate(); |
| } |
| |
| private void initListComponents() { |
| // Results tree (containing DataBundle Paths to all individual results for this port) |
| resultModel = new DataBundleTreeModel(value); |
| filteredTreeModel = new FilteredDataBundleTreeModel(resultModel); |
| tree = new JTree(filteredTreeModel); |
| tree.getSelectionModel().setSelectionMode(SINGLE_TREE_SELECTION); |
| tree.setExpandsSelectedPaths(true); |
| tree.setRootVisible(true); |
| tree.setShowsRootHandles(true); |
| tree.setCellRenderer(new PortResultCellRenderer()); |
| |
| tree.addTreeSelectionListener(new TreeSelectionListener() { |
| @Override |
| public void valueChanged(TreeSelectionEvent e) { |
| TreePath selectionPath = e.getNewLeadSelectionPath(); |
| if (selectionPath != null) { |
| // Get the selected node |
| DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) selectionPath |
| .getLastPathComponent(); |
| renderedResultComponent.setPath((Path) selectedNode.getUserObject()); |
| } |
| } |
| }); |
| |
| filteredTreeModel.addTreeModelListener(new TreeModelListener() { |
| @Override |
| public void treeNodesChanged(TreeModelEvent e) { |
| TreePath treePath = e.getTreePath(); |
| tree.expandPath(treePath); |
| // If nothing is currently selected in the tree - select the first item in the result list |
| if (tree.getSelectionRows() == null |
| || tree.getSelectionRows().length == 0) { |
| DefaultMutableTreeNode firstLeaf = ((DefaultMutableTreeNode) filteredTreeModel |
| .getRoot()).getFirstLeaf(); |
| tree.setSelectionPath(new TreePath(firstLeaf.getPath())); |
| } |
| } |
| |
| @Override |
| public void treeNodesInserted(TreeModelEvent e) { |
| } |
| |
| @Override |
| public void treeNodesRemoved(TreeModelEvent e) { |
| } |
| |
| @Override |
| public void treeStructureChanged(TreeModelEvent e) { |
| } |
| }); |
| |
| // Component for rendering individual results |
| renderedResultComponent = new RenderedResultComponent(rendererRegistry, saveActions); |
| |
| /* |
| * Split pane containing a tree with all results from an output port and |
| * rendered result component for individual result rendered currently |
| * selected from the tree |
| */ |
| JSplitPane splitPanel = new JSplitPane(HORIZONTAL_SPLIT); |
| |
| JPanel leftPanel = new JPanel(new BorderLayout()); |
| |
| JPanel treeSubPanel = new JPanel(new BorderLayout()); |
| treeSubPanel.add(new JLabel("Click in tree to"), WEST); |
| filterChoiceBox = new JComboBox<>(new FilterType[] { FilterType.ALL, |
| FilterType.RESULTS, FilterType.ERRORS }); |
| filterChoiceBox.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| updateTree(); |
| } |
| }); |
| treeSubPanel.add(filterChoiceBox); |
| leftPanel.add(treeSubPanel, NORTH); |
| leftPanel.add(new JScrollPane(tree), CENTER); |
| splitPanel.setTopComponent(leftPanel); |
| splitPanel.setBottomComponent(renderedResultComponent); |
| splitPanel.setDividerLocation(400); |
| |
| // Add all to main panel |
| add(splitPanel, CENTER); |
| |
| DefaultMutableTreeNode firstLeaf = ((DefaultMutableTreeNode) filteredTreeModel |
| .getRoot()).getFirstLeaf(); |
| tree.setSelectionPath(new TreePath(firstLeaf.getPath())); |
| } |
| |
| @Override |
| public void update() { |
| if (value == null || DataBundles.isMissing(value)) { |
| if (state != State.NO_DATA) |
| scheduleInitComponents(); |
| } else if (DataBundles.isList(value)) { |
| if (state != State.LIST) |
| scheduleInitComponents(); |
| else if (resultModel != null) |
| resultModel.update(); |
| } else { |
| if (state != State.SINGLE_VALUE) |
| scheduleInitComponents(); |
| } |
| } |
| |
| private void scheduleInitComponents() { |
| invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| initComponents(); |
| } |
| }); |
| } |
| |
| public Port getPort() { |
| return port; |
| } |
| |
| public FilteredDataBundleTreeModel getModel() { |
| return filteredTreeModel; |
| } |
| |
| private List<TreePath> expandedPaths = new ArrayList<>(); |
| private TreePath selectionPath = null; |
| |
| private void rememberPaths() { |
| expandedPaths.clear(); |
| for (Enumeration<TreePath> e = tree |
| .getExpandedDescendants(new TreePath(filteredTreeModel |
| .getRoot())); (e != null) && e.hasMoreElements();) |
| expandedPaths.add(e.nextElement()); |
| selectionPath = tree.getSelectionPath(); |
| } |
| |
| private void reinstatePaths() { |
| for (TreePath path : expandedPaths) |
| if (filteredTreeModel.isShown((DefaultMutableTreeNode) path |
| .getLastPathComponent())) |
| tree.expandPath(path); |
| if (selectionPath != null) { |
| if (filteredTreeModel |
| .isShown((DefaultMutableTreeNode) selectionPath |
| .getLastPathComponent())) |
| tree.setSelectionPath(selectionPath); |
| else { |
| tree.clearSelection(); |
| renderedResultComponent.clearResult(); |
| } |
| } |
| } |
| |
| private void updateTree() { |
| filteredTreeModel.setFilter((FilterType) filterChoiceBox |
| .getSelectedItem()); |
| rememberPaths(); |
| filteredTreeModel.reload(); |
| tree.setModel(filteredTreeModel); |
| reinstatePaths(); |
| } |
| |
| public void expandTree() { |
| if (tree != null) |
| for (int row = 0; row < tree.getRowCount(); row++) |
| tree.expandRow(row); |
| } |
| } |