Java tutorial
/* This file is part of BioNet. BioNet is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. BioNet 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 General Public License for more details. You should have received a copy of the GNU General Public License along with BioNet. If not, see <http://www.gnu.org/licenses/>. */ package edu.purdue.cc.bionet.ui; import edu.purdue.bbc.util.Language; import edu.purdue.bbc.util.MutableNumber; import edu.purdue.bbc.util.Pair; import edu.purdue.bbc.util.Range; import edu.purdue.bbc.util.Settings; import edu.purdue.bbc.util.SimplePair; import edu.purdue.cc.bionet.io.DataReader; import edu.purdue.cc.bionet.io.SaveImageAction; import edu.purdue.cc.bionet.ui.layout.LayoutAnimator; import edu.purdue.cc.bionet.ui.layout.CenterLayout; import edu.purdue.cc.bionet.ui.layout.MultipleCirclesLayout; import edu.purdue.cc.bionet.ui.layout.RandomLayout; import edu.purdue.cc.bionet.ui.renderer.FastEdgeRenderer; import edu.purdue.cc.bionet.util.Correlation; import edu.purdue.cc.bionet.util.CorrelationSet; import edu.purdue.cc.bionet.util.Experiment; import edu.purdue.cc.bionet.util.Molecule; import edu.purdue.cc.bionet.util.MonitorableRange; import edu.purdue.cc.bionet.util.Sample; import edu.purdue.cc.bionet.util.SampleGroup; import edu.purdue.cc.bionet.util.SampleGroupChangeEvent; import edu.purdue.cc.bionet.util.SampleGroupChangeListener; import edu.purdue.cc.bionet.util.Spectrum; import edu.purdue.cc.bionet.util.SplitSpectrum; import java.io.IOException; import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Frame; import java.awt.Graphics2D; import java.awt.Graphics; import java.awt.GridLayout; import java.awt.Paint; import java.awt.Rectangle; import java.awt.Stroke; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.InputEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.geom.Rectangle2D; import java.awt.geom.RectangularShape; import java.awt.image.BufferedImage; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.TreeSet; import java.util.Vector; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.AbstractButton; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JCheckBoxMenuItem; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JRadioButtonMenuItem; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.SpinnerNumberModel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.border.TitledBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumn; import edu.uci.ics.jung.algorithms.layout.AbstractLayout; import edu.uci.ics.jung.algorithms.layout.CircleLayout; import edu.uci.ics.jung.algorithms.layout.FRLayout; import edu.uci.ics.jung.algorithms.layout.KKLayout; import edu.uci.ics.jung.algorithms.layout.Layout; import edu.uci.ics.jung.algorithms.layout.LayoutDecorator; import edu.uci.ics.jung.algorithms.layout.SpringLayout2; import edu.uci.ics.jung.graph.Graph; import edu.uci.ics.jung.graph.util.EdgeType; import edu.uci.ics.jung.visualization.control.GraphMouseListener; import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; import edu.uci.ics.jung.visualization.picking.PickedState; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.data.statistics.SimpleHistogramBin; import org.jfree.data.statistics.SimpleHistogramDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.jfree.ui.RectangleEdge; import org.jfree.chart.renderer.category.BarRenderer; import org.jfree.chart.renderer.category.StandardBarPainter; import org.jfree.chart.renderer.xy.StandardXYBarPainter; import org.jfree.chart.renderer.xy.XYBarRenderer; import org.apache.commons.collections15.Transformer; import org.apache.log4j.Logger; /** * A class for displaying and interacting with Correlation data for a set of * molecules. */ public class CorrelationDisplayPanel extends AbstractDisplayPanel implements ActionListener, ChangeListener, ItemListener { private JMenuBar menuBar; // correlationMethod menu items private JMenu correlationMethodMenu; private ButtonGroup correlationMethodMenuButtonGroup; private JRadioButtonMenuItem pearsonCalculationMenuItem; private JRadioButtonMenuItem spearmanCalculationMenuItem; private JRadioButtonMenuItem kendallCalculationMenuItem; // layout menu itmes private JMenu layoutMenu; private ButtonGroup layoutMenuButtonGroup; private JRadioButtonMenuItem multipleCirclesLayoutMenuItem; private JRadioButtonMenuItem singleCircleLayoutMenuItem; private JRadioButtonMenuItem randomLayoutMenuItem; private JRadioButtonMenuItem heatMapLayoutMenuItem; private JRadioButtonMenuItem kkLayoutMenuItem; // private JRadioButtonMenuItem frLayoutMenuItem; // private JRadioButtonMenuItem springLayoutMenuItem; private JRadioButtonMenuItem frSpringLayoutMenuItem; // private JCheckBoxMenuItem animatedLayoutMenuItem; // view menu items private JMenu viewMenu; private JMenuItem zoomInViewMenuItem; private JMenuItem zoomOutViewMenuItem; private JMenuItem fitToWindowViewMenuItem; private JMenuItem selectAllViewMenuItem; private JMenuItem clearSelectionViewMenuItem; private JMenuItem invertSelectionViewMenuItem; private JMenuItem selectCorrelatedViewMenuItem; private JMenuItem hideSelectedViewMenuItem; private JMenuItem hideUnselectedViewMenuItem; private JMenuItem hideUncorrelatedViewMenuItem; private JMenuItem hideOrphansViewMenuItem; private JMenuItem showCorrelatedViewMenuItem; private SaveImageAction saveImageAction; // groups menu items private JMenu groupsMenu; private JMenuItem resetSampleGroupsMenuItem; private JMenuItem chooseSampleGroupsMenuItem; // color menu items private JMenu colorMenu; private ButtonGroup colorMenuButtonGroup; private JRadioButtonMenuItem normalColorMenuItem; private JRadioButtonMenuItem highContrastColorMenuItem; private MoleculeFilterPanel moleculeFilterPanel; private CorrelationFilterPanel correlationFilterPanel; private CorrelationGraphVisualizer graph; private Layout<Molecule, Correlation> layout; //Graph Layout private InfoPanel infoPanel; private JSplitPane graphSplitPane; private HeatMap heatMapPanel; private Collection<Experiment> experiments; private Collection<Molecule> molecules; private Collection<Sample> samples; private CorrelationSet correlations; private String title; private Scalable visibleGraph; private MutableNumber correlationMethod; /** * Creates a new CorrelationDisplayPanel. */ public CorrelationDisplayPanel() { super(new BorderLayout()); this.menuBar = new JMenuBar(); this.graphSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); this.correlationMethod = new MutableNumber(Correlation.PEARSON); this.buildPanel(); } /** * Gets the title of this CorrelationDisplayPanel, which should be the * description field of the experiment. * * @return the title of this CorrelationDisplayPanel. */ public String getTitle() { return this.title; } /** * Sets the title of this CorrelationDisplayPanel. * * @param title The new title for this Panel */ public void setTitle(String title) { this.title = title; } /** * Adds all of the necessary Components to this Component. */ private void buildPanel() { Language language = Settings.getLanguage(); this.correlationMethodMenu = new JMenu(language.get("Correlation Method")); this.correlationMethodMenuButtonGroup = new ButtonGroup(); this.pearsonCalculationMenuItem = new JRadioButtonMenuItem(language.get("Pearson"), true); this.spearmanCalculationMenuItem = new JRadioButtonMenuItem(language.get("Spearman")); this.kendallCalculationMenuItem = new JRadioButtonMenuItem(language.get("Kendall")); // layout menu itmes this.layoutMenu = new JMenu(language.get("Layout")); this.layoutMenuButtonGroup = new ButtonGroup(); this.multipleCirclesLayoutMenuItem = new JRadioButtonMenuItem(language.get("Multiple Circles")); this.singleCircleLayoutMenuItem = new JRadioButtonMenuItem(language.get("Single Circle"), true); this.randomLayoutMenuItem = new JRadioButtonMenuItem(language.get("Random")); this.heatMapLayoutMenuItem = new JRadioButtonMenuItem(language.get("Heat Map")); this.kkLayoutMenuItem = new JRadioButtonMenuItem(language.get("Kamada-Kawai")); // this.frLayoutMenuItem = // new JRadioButtonMenuItem( language.get( "Fruchterman-Reingold" )); // this.springLayoutMenuItem = // new JRadioButtonMenuItem( language.get( "Spring Layout" )); this.frSpringLayoutMenuItem = new JRadioButtonMenuItem(language.get("Spring Layout")); // this.animatedLayoutMenuItem = new JCheckBoxMenuItem( // language.get( "Fruchterman-Reingold Spring Embedding" )); // view menu items this.viewMenu = new JMenu(language.get("View")); this.zoomInViewMenuItem = new JMenuItem(language.get("Zoom In"), KeyEvent.VK_I); this.zoomOutViewMenuItem = new JMenuItem(language.get("Zoom Out"), KeyEvent.VK_O); this.fitToWindowViewMenuItem = new JMenuItem(language.get("Fit to Window"), KeyEvent.VK_F); this.selectAllViewMenuItem = new JMenuItem(language.get("Select All"), KeyEvent.VK_A); this.clearSelectionViewMenuItem = new JMenuItem(language.get("Clear Selection"), KeyEvent.VK_C); this.invertSelectionViewMenuItem = new JMenuItem(language.get("Invert Selection"), KeyEvent.VK_I); this.selectCorrelatedViewMenuItem = new JMenuItem(language.get("Select Correlated to Selection"), KeyEvent.VK_R); this.hideSelectedViewMenuItem = new JMenuItem(language.get("Hide Selected"), KeyEvent.VK_H); this.hideUnselectedViewMenuItem = new JMenuItem(language.get("Hide Unselected"), KeyEvent.VK_U); this.hideUncorrelatedViewMenuItem = new JMenuItem(language.get("Hide Uncorrelated to Selection"), KeyEvent.VK_L); this.hideOrphansViewMenuItem = new JMenuItem(language.get("Hide Orphans"), KeyEvent.VK_P); this.showCorrelatedViewMenuItem = new JMenuItem(language.get("Show All Correlated to Visible"), KeyEvent.VK_S); this.saveImageAction = new SaveImageAction(language.get("Save Main Graph Image") + "...", null); // groups menu items this.groupsMenu = new JMenu(language.get("Groups")); this.resetSampleGroupsMenuItem = new JMenuItem(language.get("Reset Sample Groups"), KeyEvent.VK_R); this.chooseSampleGroupsMenuItem = new JMenuItem(language.get("Choose Sample Groups") + "...", KeyEvent.VK_C); // color menu items this.colorMenu = new JMenu(language.get("Color")); this.colorMenuButtonGroup = new ButtonGroup(); this.normalColorMenuItem = new JRadioButtonMenuItem(language.get("Normal Color"), true); this.highContrastColorMenuItem = new JRadioButtonMenuItem(language.get("High Contrast Color")); // CORRELATION FILTER ELEMENTS JPanel leftPanel = new JPanel(new BorderLayout()); this.moleculeFilterPanel = new MoleculeFilterPanel(); leftPanel.add(moleculeFilterPanel, BorderLayout.CENTER); this.correlationFilterPanel = new CorrelationFilterPanel(); leftPanel.add(this.correlationFilterPanel, BorderLayout.SOUTH); //CALCULATION MENU this.correlationMethodMenu.setMnemonic(KeyEvent.VK_C); this.correlationMethodMenu.getAccessibleContext() .setAccessibleDescription(language.get("Perform Data Calculations")); this.correlationMethodMenuButtonGroup.add(this.pearsonCalculationMenuItem); this.correlationMethodMenuButtonGroup.add(this.spearmanCalculationMenuItem); this.correlationMethodMenuButtonGroup.add(this.kendallCalculationMenuItem); this.pearsonCalculationMenuItem.setMnemonic(KeyEvent.VK_P); this.spearmanCalculationMenuItem.setMnemonic(KeyEvent.VK_S); this.kendallCalculationMenuItem.setMnemonic(KeyEvent.VK_K); this.correlationMethodMenu.add(this.pearsonCalculationMenuItem); this.correlationMethodMenu.add(this.spearmanCalculationMenuItem); this.correlationMethodMenu.add(this.kendallCalculationMenuItem); this.pearsonCalculationMenuItem.addItemListener(this); this.spearmanCalculationMenuItem.addItemListener(this); this.kendallCalculationMenuItem.addItemListener(this); //LAYOUT MENU LayoutChangeListener lcl = new LayoutChangeListener(); this.layoutMenu.setMnemonic(KeyEvent.VK_L); this.layoutMenu.getAccessibleContext() .setAccessibleDescription(language.get("Change the layout of the graph")); this.layoutMenuButtonGroup.add(this.multipleCirclesLayoutMenuItem); this.layoutMenuButtonGroup.add(this.singleCircleLayoutMenuItem); this.layoutMenuButtonGroup.add(this.randomLayoutMenuItem); this.layoutMenuButtonGroup.add(this.kkLayoutMenuItem); // this.layoutMenuButtonGroup.add( this.frLayoutMenuItem ); // this.layoutMenuButtonGroup.add( this.springLayoutMenuItem ); this.layoutMenuButtonGroup.add(this.frSpringLayoutMenuItem); this.layoutMenuButtonGroup.add(this.heatMapLayoutMenuItem); Enumeration<AbstractButton> e = this.layoutMenuButtonGroup.getElements(); this.layoutMenu.add(this.multipleCirclesLayoutMenuItem); this.layoutMenu.add(this.singleCircleLayoutMenuItem); this.layoutMenu.add(this.randomLayoutMenuItem); this.layoutMenu.add(this.kkLayoutMenuItem); // this.layoutMenu.add( this.frLayoutMenuItem ); // this.layoutMenu.add( this.springLayoutMenuItem ); this.layoutMenu.add(this.frSpringLayoutMenuItem); this.layoutMenu.add(this.heatMapLayoutMenuItem); // this.layoutMenu.addSeparator( ); // this.layoutMenu.add( this.animatedLayoutMenuItem ); this.multipleCirclesLayoutMenuItem.addActionListener(lcl); this.multipleCirclesLayoutMenuItem.setEnabled(false); this.singleCircleLayoutMenuItem.addActionListener(lcl); this.randomLayoutMenuItem.addActionListener(lcl); this.kkLayoutMenuItem.addActionListener(lcl); // this.frLayoutMenuItem.addActionListener( lcl ); this.frSpringLayoutMenuItem.addActionListener(lcl); this.heatMapLayoutMenuItem.addActionListener(lcl); // this.animatedLayoutMenuItem.addActionListener( lcl ); //VIEW MENU this.viewMenu.add(this.colorMenu); this.viewMenu.addSeparator(); this.viewMenu.setMnemonic(KeyEvent.VK_V); this.viewMenu.getAccessibleContext() .setAccessibleDescription(language.get("Change the data view settings")); this.viewMenu.add(this.zoomOutViewMenuItem); this.viewMenu.add(this.zoomInViewMenuItem); this.viewMenu.add(this.fitToWindowViewMenuItem); this.viewMenu.addSeparator(); this.viewMenu.add(this.selectAllViewMenuItem); this.viewMenu.add(this.clearSelectionViewMenuItem); this.viewMenu.add(this.invertSelectionViewMenuItem); this.viewMenu.add(this.selectCorrelatedViewMenuItem); this.viewMenu.addSeparator(); this.viewMenu.add(this.hideSelectedViewMenuItem); this.viewMenu.add(this.hideUnselectedViewMenuItem); this.viewMenu.add(this.hideUncorrelatedViewMenuItem); this.viewMenu.add(this.hideOrphansViewMenuItem); this.viewMenu.add(this.showCorrelatedViewMenuItem); this.viewMenu.addSeparator(); this.viewMenu.add(this.saveImageAction); this.resetSampleGroupsMenuItem.addActionListener(this); this.chooseSampleGroupsMenuItem.addActionListener(this); this.zoomOutViewMenuItem.addActionListener(this); this.zoomInViewMenuItem.addActionListener(this); this.fitToWindowViewMenuItem.addActionListener(this); this.selectAllViewMenuItem.addActionListener(this); this.clearSelectionViewMenuItem.addActionListener(this); this.invertSelectionViewMenuItem.addActionListener(this); this.selectCorrelatedViewMenuItem.addActionListener(this); this.hideSelectedViewMenuItem.addActionListener(this); this.hideUnselectedViewMenuItem.addActionListener(this); this.hideUncorrelatedViewMenuItem.addActionListener(this); this.hideOrphansViewMenuItem.addActionListener(this); this.showCorrelatedViewMenuItem.addActionListener(this); this.selectAllViewMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK)); this.clearSelectionViewMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0)); // GROUPS MENU this.groupsMenu.setMnemonic(KeyEvent.VK_G); this.groupsMenu.add(this.resetSampleGroupsMenuItem); this.groupsMenu.add(this.chooseSampleGroupsMenuItem); this.zoomOutViewMenuItem .setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, InputEvent.CTRL_DOWN_MASK)); this.zoomInViewMenuItem .setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, InputEvent.CTRL_DOWN_MASK)); this.fitToWindowViewMenuItem .setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_0, InputEvent.CTRL_DOWN_MASK)); this.hideSelectedViewMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0)); //COLOR MENU this.colorMenu.setMnemonic(KeyEvent.VK_R); this.colorMenu.getAccessibleContext() .setAccessibleDescription(language.get("Change the color of the graph")); this.colorMenuButtonGroup.add(this.normalColorMenuItem); this.colorMenuButtonGroup.add(this.highContrastColorMenuItem); this.colorMenu.add(this.normalColorMenuItem); this.colorMenu.add(this.highContrastColorMenuItem); this.normalColorMenuItem.addItemListener(this); this.highContrastColorMenuItem.addItemListener(this); this.menuBar.add(this.correlationMethodMenu); this.menuBar.add(this.layoutMenu); this.menuBar.add(this.viewMenu); this.menuBar.add(this.groupsMenu); // Add the panels to the main panel this.add(menuBar, BorderLayout.NORTH); // this.add( this.correlationViewPanel, BorderLayout.CENTER ); this.add(leftPanel, BorderLayout.WEST); } /** * Creates a Correlation Graph. * * @param experiments An Experiment Object containing the data to be used. */ public boolean createView(Collection<Experiment> experiments) { if (experiments.size() == 0) { return false; } this.setBackground(Color.WHITE); this.setVisible(true); this.title = Settings.getLanguage().get("Correlation Network"); this.molecules = new TreeSet<Molecule>(); for (Experiment experiment : experiments) { this.molecules.addAll(experiment.getMolecules()); experiment.updateCorrelations(); } this.samples = new TreeSet<Sample>(); this.experiments = experiments; this.correlations = new CorrelationSet(molecules, samples); for (Experiment experiment : this.experiments) { for (Sample sample : experiment.getSamples()) { this.samples.add(sample); } for (Molecule molecule : experiment.getMolecules()) { this.moleculeFilterPanel.add(molecule); this.molecules.add(molecule); this.correlations.add(molecule); } } this.graph = new CorrelationGraphVisualizer(this.correlations, this.correlationFilterPanel.getMonitorableRange()); this.addSampleGroupChangeListener(this.graph); this.saveImageAction.setComponent(this.graph); this.graph.setBackground(this.getBackground()); this.graph.setIndicateCommonNeighbors(true); this.infoPanel = new InfoPanel(); this.addSampleGroupChangeListener(this.infoPanel); this.graph.addGraphMouseListener(new CorrelationGraphMouseListener()); this.graph.addGraphMouseEdgeListener(new GraphMouseListener<Correlation>() { public void graphClicked(Correlation c, MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() >= 2) { new DetailWindow(correlations, c, correlationFilterPanel.getRange(), correlationMethod.intValue()); } } public void graphPressed(Correlation c, MouseEvent e) { } public void graphReleased(Correlation c, MouseEvent e) { } }); Collection<SampleGroup> sampleGroups = new ArrayList<SampleGroup>(); sampleGroups.add(new SampleGroup("All Samples", this.samples)); this.setSampleGroups(sampleGroups); this.add(this.graphSplitPane, BorderLayout.CENTER); this.graphSplitPane.setBottomComponent(this.infoPanel); this.setGraphVisualizer(this.graph); this.heatMapPanel = new HeatMap(this.getTitle(), this.correlations, this.getCorrelationRange(), this.correlationMethod); this.graph.addVertexChangeListener(this.heatMapPanel); this.pearsonCalculationMenuItem.addChangeListener(this.heatMapPanel); this.spearmanCalculationMenuItem.addChangeListener(this.heatMapPanel); this.kendallCalculationMenuItem.addChangeListener(this.heatMapPanel); this.addComponentListener(new InitialSetup()); return true; } // ============================ InitialSetup ================================= /** * A class for handling the sizng of panels and such in * ClusteringDisplayPanel when it is initially shown. */ private class InitialSetup extends ComponentAdapter { public InitialSetup() { super(); } /** * Fired when the component is shown. Sets up a few things * based on the overall size of the panel when it is made visible, then * unregisters itself as a listener so it is only fired once. * * @param e The event which triggered this action. */ public void componentShown(ComponentEvent e) { Component source = e.getComponent(); graphSplitPane.setDividerLocation(source.getHeight() - 300); setGraphLayout(CircleLayout.class); // we only need to perform this action once. source.removeComponentListener(this); } } /** * Resets the Graph Layout back to its original state. */ public void resetGraphLayout() { this.graph.resetLayout(); } /** * Sets the graph layout for this CorrelationDisplayPanel. * * @param layout The Class of the layout to use for the graph. A new * instance will be created. */ public void setGraphLayout(Class<? extends AbstractLayout> layout) { if (this.heatMapPanel != null && this.visibleGraph == this.heatMapPanel) { this.remove(this.heatMapPanel.getScrollPane()); this.add(this.graphSplitPane, BorderLayout.CENTER); this.setVisibleGraph(this.graph); this.selectAllViewMenuItem.setEnabled(true); this.clearSelectionViewMenuItem.setEnabled(true); this.invertSelectionViewMenuItem.setEnabled(true); this.selectCorrelatedViewMenuItem.setEnabled(true); this.hideSelectedViewMenuItem.setEnabled(true); this.hideUnselectedViewMenuItem.setEnabled(true); this.hideUncorrelatedViewMenuItem.setEnabled(true); // this.animatedLayoutMenuItem.setEnabled( true ); this.validate(); this.graphSplitPane.repaint(); } this.graph.setGraphLayout(layout); } /** * Switches the view layout to heat map. */ private void heatMap() { this.selectAllViewMenuItem.setEnabled(false); this.clearSelectionViewMenuItem.setEnabled(false); this.invertSelectionViewMenuItem.setEnabled(false); this.selectCorrelatedViewMenuItem.setEnabled(false); this.hideSelectedViewMenuItem.setEnabled(false); this.hideUnselectedViewMenuItem.setEnabled(false); this.hideUncorrelatedViewMenuItem.setEnabled(false); // this.animatedLayoutMenuItem.setEnabled( false ); this.remove(this.graphSplitPane); this.add(this.heatMapPanel.getScrollPane(), BorderLayout.CENTER); this.setVisibleGraph(this.heatMapPanel); this.validate(); this.heatMapPanel.getScrollPane().repaint(); } /** * Sets the CorrelationGraphVisualizer component for this panel. * * @param v The CorrelationGraphVisualizer to use. */ private void setGraphVisualizer(CorrelationGraphVisualizer v) { if (this.graph != null) this.remove(this.graph); this.graph = v; this.correlationFilterPanel.getMonitorableRange().addChangeListener(v); // add labels to the graph this.graphSplitPane.setTopComponent(this.graph.getScrollPane()); this.setVisibleGraph(this.graph); this.graph.addAnimationListener(this); this.graph.addVertexChangeListener(this.moleculeFilterPanel); v.addPickedVertexStateChangeListener(new PickedStateChangeListener<Molecule>() { public void stateChanged(PickedStateChangeEvent<Molecule> event) { if (event.getStateChange()) infoPanel.add(event.getItem()); else infoPanel.remove(event.getItem()); } }); v.addPickedEdgeStateChangeListener(new PickedStateChangeListener<Correlation>() { public void stateChanged(PickedStateChangeEvent<Correlation> event) { if (event.getStateChange()) infoPanel.add(event.getItem()); else infoPanel.remove(event.getItem()); } }); } private void setVisibleGraph(Scalable graph) { this.visibleGraph = graph; this.saveImageAction.setComponent((Component) graph); } /** * Returns the Experiment associated with this CorrelationDisplayPanel. * * @return The associated Experiment. */ @Deprecated public Experiment getExperiment() { return this.experiments.iterator().next(); } /** * Returns the Experiments associated with this CorrelationDisplayPanel. * * @return The associated Experiments */ public Collection<Experiment> getExperiments() { return this.experiments; } /** * Returns the range setting of the Correlation Filter * * @return The value of the correlation setting */ public MonitorableRange getCorrelationRange() { return this.correlationFilterPanel.getMonitorableRange(); } /** * Scales the graph to the given level relative to the current zoom level. * * @param amount The amount to scale the graph view by. */ public float scale(float amount) { return this.visibleGraph.scale(amount); } /** * Sets the graph to the given zoom level, 1.0 being 100%. * * @param level the new Zoom level. */ public float scaleTo(float level) { return this.visibleGraph.scaleTo(level); } /** * The stateChanged method of the ChangeListener interface. * @see ChangeListener#stateChanged(ChangeEvent) * * @param event The event which triggered this action. */ public void stateChanged(ChangeEvent event) { // LayoutAnimator animator = (LayoutAnimator)event.getSource( ); // if ( animator.isStopped( )) // this.animatedLayoutMenuItem.setState( false ); } /** * The itemStateChanged method of the ItemListener interface. * @see ItemListener#itemStateChanged(ItemEvent) * * @param e The event which triggered this action. */ public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { Object item = e.getSource(); if (item == this.highContrastColorMenuItem) { graph.setBackground(Color.BLACK); graph.setForeground(Color.WHITE); graph.setPickedLabelColor(Color.WHITE); graph.setVertexPaint(Color.ORANGE.darker()); graph.setPickedVertexPaint(Color.RED); graph.setPickedEdgePaint(Color.WHITE); heatMapPanel.setBackground(Color.BLACK); heatMapPanel.setForeground(Color.WHITE); } else if (item == this.normalColorMenuItem) { graph.setBackground(Color.WHITE); graph.setForeground(Color.BLACK); graph.setPickedLabelColor(Color.BLUE); graph.setVertexPaint(Color.ORANGE); graph.setPickedVertexPaint(Color.YELLOW); graph.setPickedEdgePaint(Color.BLACK); heatMapPanel.setForeground(Color.BLACK); heatMapPanel.setBackground(Color.WHITE); } else if (item == pearsonCalculationMenuItem) { correlationMethod.setValue(Correlation.PEARSON); } else if (item == spearmanCalculationMenuItem) { correlationMethod.setValue(Correlation.SPEARMAN); } else if (item == kendallCalculationMenuItem) { correlationMethod.setValue(Correlation.KENDALL); } graph.filterEdges(); graph.repaint(); } } /** * The actionPerformed method of the ActionListener interface. * * @param event The event which triggered this action. */ public void actionPerformed(ActionEvent event) { Component item = (Component) event.getSource(); PickedState<Molecule> pickedVertexState = this.graph.getPickedVertexState(); PickedState<Correlation> pickedEdgeState = this.graph.getPickedEdgeState(); Collection<Molecule> vertices = new Vector(this.graph.getVertices()); Collection<Correlation> edges = new Vector(this.graph.getEdges()); if (item == this.selectAllViewMenuItem) { this.graph.selectAll(); } else if (item == this.clearSelectionViewMenuItem) { this.graph.clearSelection(); } else if (item == this.zoomInViewMenuItem) { this.visibleGraph.scale(1.25f); } else if (item == this.zoomOutViewMenuItem) { this.visibleGraph.scale(0.8f); } else if (item == this.fitToWindowViewMenuItem) { this.visibleGraph.scaleTo(0.99f); } else if (item == this.invertSelectionViewMenuItem) { for (Molecule m : vertices) pickedVertexState.pick(m, !pickedVertexState.isPicked(m)); for (Correlation c : edges) pickedEdgeState.pick(c, !pickedEdgeState.isPicked(c)); } else if (item == this.selectCorrelatedViewMenuItem) { Collection<Molecule> picked = new Vector(pickedVertexState.getPicked()); for (Molecule m : picked) { for (Correlation c : graph.getIncidentEdges(m)) { pickedEdgeState.pick(c, true); pickedVertexState.pick(c.getOpposite(m), true); } } } else if (item == this.hideSelectedViewMenuItem) { for (Molecule m : new Vector<Molecule>(pickedVertexState.getPicked())) this.graph.removeVertex(m); } else if (item == this.hideUnselectedViewMenuItem) { for (Molecule m : vertices) { if (!pickedVertexState.isPicked(m)) this.graph.removeVertex(m); } } else if (item == this.hideUncorrelatedViewMenuItem) { boolean keep; for (Molecule m : vertices) { keep = false; for (Correlation c : graph.getIncidentEdges(m)) { keep = keep || pickedVertexState.isPicked(m) || pickedVertexState.isPicked(c.getOpposite(m)); } if (!keep) { this.graph.removeVertex(m); } } } else if (item == this.hideOrphansViewMenuItem) { for (Molecule m : vertices) { if (this.graph.getNeighborCount(m) == 0) this.graph.removeVertex(m); } } else if (item == this.showCorrelatedViewMenuItem) { this.graph.setIgnoreRepaint(true); for (Correlation c : this.correlations) { if (this.getCorrelationRange().contains(c.getValue(this.correlationMethod)) && (vertices.contains(c.getFirst()) || vertices.contains(c.getSecond()))) { this.graph.addVertex(c.getFirst()); this.graph.addVertex(c.getSecond()); } } this.graph.setIgnoreRepaint(false); } else if (item == this.chooseSampleGroupsMenuItem) { Component frame = this; while (!(frame instanceof Frame) && frame != null) { frame = frame.getParent(); } this.setSampleGroups(SampleGroupDialog.showInputDialog((Frame) frame, Settings.getLanguage().get("Choose groups"), this.samples)); if (this.getSampleGroups().size() > 1) { this.infoPanel.repaint(); Logger logger = Logger.getLogger(getClass()); Iterator<SampleGroup> iterator = this.getSampleGroups().iterator(); SampleGroup group = iterator.next(); logger.debug(group.toString()); for (Sample sample : group) { logger.debug("\t" + sample.toString()); } group = iterator.next(); logger.debug(group.toString()); for (Sample sample : group) { logger.debug("\t" + sample.toString()); } } } else if (item == this.resetSampleGroupsMenuItem) { Collection<SampleGroup> sampleGroups = new ArrayList<SampleGroup>(); sampleGroups.add(new SampleGroup(Settings.getLanguage().get("All samples"), this.samples)); this.setSampleGroups(sampleGroups); this.infoPanel.repaint(); } } public Graph getGraph() { return this.graph; } // =================== PRIVATE/PROTECTED CLASSES ========================== // ====================== MoleculeFilterPanel ============================= /** * A UI class for hiding/showing moledules (nodes) */ private class MoleculeFilterPanel extends JPanel implements ItemListener, ActionListener, GraphItemChangeListener<Molecule>, KeyListener { private HashMap<Molecule, MoleculeCheckBox> checkBoxMap = new HashMap<Molecule, MoleculeCheckBox>(); private JLabel filterLabel; private JButton clearButton; private JButton noneButton; private JButton allButton; private JTextField filterBox = new JTextField(); private MoleculePopup popup = new MoleculePopup(); private JScrollPane moleculeScrollPane; private JPanel moleculeList = new JPanel(new GridLayout(0, 1)) { // Inserts the checkboxes in alphabetical order public Component add(Component component) { Logger logger = Logger.getLogger(getClass()); int index = 0; String componentText = null; // find where the new checkBox should go. componentText = ((MoleculeCheckBox) component).getText(); String cText = null; Component[] components = this.getComponents(); for (int i = 0; i < components.length; i++) { if (components[i] instanceof AbstractButton) { cText = ((AbstractButton) components[i]).getText(); if (cText.compareTo((componentText)) > 0) { logger.debug("Attemping to insert " + componentText + " before " + cText); try { this.add(component, i); } catch (IllegalArgumentException e) { // add it to the end instead. logger.debug("Adding " + componentText + " to the end of the list"); return super.add(component); } return component; } } } // it didn't fit before anything, so add it to the end instead. logger.debug("Adding " + componentText + " to the end of the list"); return super.add(component); } }; /** * Creates a new instance of MoleculeFilterPanel. */ public MoleculeFilterPanel() { super(new BorderLayout()); Language language = Settings.getLanguage(); this.noneButton = new JButton(language.get("None")); this.allButton = new JButton(language.get("All")); this.filterLabel = new JLabel(language.get("Search") + ": ", SwingConstants.RIGHT); this.clearButton = new JButton(language.get("Clear")); JPanel searchPanel = new JPanel(new BorderLayout()); searchPanel.add(this.clearButton, BorderLayout.SOUTH); searchPanel.add(this.filterLabel, BorderLayout.WEST); searchPanel.add(this.filterBox, BorderLayout.CENTER); this.filterBox.addKeyListener(this); this.clearButton.addActionListener(this); this.clearButton.setPreferredSize(new Dimension(75, 20)); // ALL & RESET BUTTONS JPanel moleculeButtonPanel = new JPanel(new BorderLayout()); this.allButton.setPreferredSize(new Dimension(75, 20)); this.noneButton.setPreferredSize(new Dimension(75, 20)); this.allButton.addActionListener(this); this.noneButton.addActionListener(this); moleculeButtonPanel.add(this.allButton, BorderLayout.WEST); moleculeButtonPanel.add(this.noneButton, BorderLayout.EAST); // MOLECULE LIST this.add(searchPanel, BorderLayout.NORTH); JPanel moleculeListPanel = new JPanel(new BorderLayout()); moleculeListPanel.add(this.moleculeList, BorderLayout.NORTH); JPanel padding = new JPanel(); padding.setBackground(Color.WHITE); moleculeListPanel.add(padding, BorderLayout.CENTER); this.moleculeScrollPane = new JScrollPane(moleculeListPanel); this.add(this.moleculeScrollPane, BorderLayout.CENTER); this.add(moleculeButtonPanel, BorderLayout.SOUTH); this.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.BLACK, 1), language.get("Molecule List"), TitledBorder.CENTER, TitledBorder.TOP)); this.moleculeList.setBackground(Color.WHITE); this.filterBox.setBackground(Color.WHITE); this.moleculeScrollPane.getVerticalScrollBar().setUnitIncrement(50); } /** * Adds a molecule to the Graph and creates a Checkbox for removing it. * * @param m The Molecule to add. * @return true if the molecule doesn't already exist in the list. */ public boolean add(Molecule m) { if (this.checkBoxMap.keySet().contains(m)) { return false; } MoleculeCheckBox cb = new MoleculeCheckBox(m, this.popup, true); cb.setBackground(Color.WHITE); this.moleculeList.add(cb); this.checkBoxMap.put(m, cb); cb.addItemListener(this); return true; } /** * Returns An arrayList of MoleculeCheckBoxes. * * @return an ArrayList of MoleculeCheckBoxes from the display panel */ public Collection<JCheckBox> getCheckBoxes() { return new ArrayList<JCheckBox>(this.checkBoxMap.values()); } //for the checkboxes /** * The itemStateChanged method of the ItemListener interface. * @see ItemListener#itemStateChanged(ItemEvent) * * @param event the event which triggered this action. */ public void itemStateChanged(ItemEvent event) { synchronized (graph.getGraph()) { Molecule molecule = ((MoleculeCheckBox) event.getSource()).getMolecule(); if (event.getStateChange() == ItemEvent.SELECTED) { graph.addVertex(molecule); for (Correlation correlation : correlations) { if (graph.isValidEdge(correlation)) { graph.addEdge(correlation, new edu.uci.ics.jung.graph.util.Pair<Molecule>( correlation.toArray(new Molecule[2])), EdgeType.UNDIRECTED); } } } else { graph.getPickedVertexState().pick(molecule, false); graph.removeVertex(molecule); } } graph.repaint(); } // for the select all/none buttons /** * The actionPerformed method of the ActionListener interface. * @see ActionListener#actionPerformed(ActionEvent) * * @param e The event which triggered this action. */ public void actionPerformed(ActionEvent e) { Object source = e.getSource(); if (source == this.allButton) { this.setAllVisible(true); } else if (source == this.noneButton) { this.setAllVisible(false); } else if (source == this.clearButton) { this.filterBox.setText(""); // simulate a key press of backspace. KeyEvent keyEvent = new KeyEvent(this.filterBox, -1, System.currentTimeMillis(), 0, KeyEvent.VK_BACK_SPACE, '\b'); for (KeyListener k : this.filterBox.getKeyListeners()) { k.keyPressed(keyEvent); k.keyTyped(keyEvent); k.keyReleased(keyEvent); } } } /** * The keyReleased method of the KeyListener interface. * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent) * * @param e The event which triggered this action. */ public void keyReleased(KeyEvent e) { Object source = e.getSource(); if (source == this.filterBox) { String text = this.filterBox.getText(); this.filter(text); } } /** * The keyPressed method of the KeyListener interface. Not implemented. * * @param e The event which triggered this action. */ public void keyPressed(KeyEvent e) { } /** * The keyTyped method of the KeyListener interface. Not implemented. * * @param e The event which triggered this action. */ public void keyTyped(KeyEvent e) { } /** * Sets all visible checkboxes to the desired state. * * @param state True for checked, false for unchecked. */ private void setAllVisible(boolean state) { graph.setIgnoreRepaint(true); if (state) { TreeSet<Molecule> currentMolecules = new TreeSet<Molecule>(graph.getVertices()); // the graph seems to slowly come to a crawl on larger datasets if we // don't clear it first (workaround) for (Molecule m : currentMolecules) { graph.removeVertex(m); } for (Component c : this.moleculeList.getComponents()) { MoleculeCheckBox cb = (MoleculeCheckBox) c; Molecule m = cb.getMolecule(); if (m != null) { cb.setSelected(state); currentMolecules.add(m); } } // adding them back in reverse order seems to be slightly faster. for (Molecule m : currentMolecules.descendingSet()) { graph.addVertex(m); } } else { for (Component c : this.moleculeList.getComponents()) { MoleculeCheckBox cb = (MoleculeCheckBox) c; Molecule m = cb.getMolecule(); if (m != null) { cb.setSelected(state); if (graph.containsVertex(m)) graph.removeVertex(m); } } } graph.filterEdges(); graph.setIgnoreRepaint(false); } /** * Sets all checkboxes to the desired state. * * @param state True for checked, false for unchecked. */ private void setAll(boolean state) { graph.setIgnoreRepaint(true); for (MoleculeCheckBox cb : this.checkBoxMap.values()) { Molecule m = cb.getMolecule(); if (m != null) { cb.setSelected(state); if (state) { if (!graph.containsVertex(m)) graph.addVertex(m); } else { if (graph.containsVertex(m)) graph.removeVertex(m); } } } graph.filterEdges(); graph.setIgnoreRepaint(false); } /** * Sets a checkbox to the desired state. * * @param m The Molecule corresponding to the checkbox to be changed. * @param state The new state for the checkbox. */ private void set(Molecule m, boolean state) { MoleculeCheckBox cb = this.checkBoxMap.get(m); cb.setSelected(state); // fire the listeners for (ItemListener i : cb.getItemListeners()) { i.itemStateChanged(new ItemEvent(cb, -1, m, state ? ItemEvent.SELECTED : ItemEvent.DESELECTED)); } } /** * Filters the molecule list based on the passed in string. * * @param filter The string to filter on. */ public void filter(String filter) { // try the string as a regular Pattern, if that fails then try a literal // string match. If all of that fails, just print the stack trace and show // all. Pattern p; // this will cause the string to emulate shell style pattern matching, // since most users will not be expecting regular expression ability. // This may be changed later. some RegEx characters will still work, // however. If the filter is surrounded by '/', we assume it is a regex. if (filter.length() >= 2 && filter.startsWith("/") && filter.endsWith("/")) { filter = String.format(".*%s.*", filter.substring(1, filter.length() - 1)); } else { filter = String.format(".*%s.*", filter.replace("*", ".*").replace("?", ".")); } try { p = Pattern.compile(filter, Pattern.CASE_INSENSITIVE); } catch (PatternSyntaxException e) { try { p = Pattern.compile(filter, Pattern.CASE_INSENSITIVE | Pattern.LITERAL); } catch (PatternSyntaxException exc) { Logger.getLogger(getClass()).debug(e); p = Pattern.compile(".*", Pattern.CASE_INSENSITIVE); } } for (JCheckBox cb : this.getCheckBoxes()) { if (cb.isVisible() && !p.matcher(((MoleculeCheckBox) cb).getMolecule().toString()).matches()) { this.moleculeList.remove(cb); cb.setVisible(false); } else if (!cb.isVisible() && p.matcher(((MoleculeCheckBox) cb).getMolecule().toString()).matches()) { this.moleculeList.add(cb); cb.setVisible(true); } this.moleculeScrollPane.validate(); } } /** * The stateChanged method of the GraphItemChangeListener interface. * @see GraphItemChangeListener#stateChanged(GraphItemChangeEvent) * * @param event The GraphItemChangeEvent which triggered this action. */ public void stateChanged(GraphItemChangeEvent<Molecule> event) { JCheckBox cb = this.checkBoxMap.get(event.getItem()); int change = event.getAction(); if (change == GraphItemChangeEvent.REMOVED) cb.setSelected(false); else if (change == GraphItemChangeEvent.ADDED) cb.setSelected(true); } private class MoleculeCheckBox extends JCheckBox implements MouseListener { private Molecule molecule; private MoleculePopup popup; public MoleculeCheckBox(Molecule m, MoleculePopup popup, boolean checked) { super(m.toString(), checked); this.molecule = m; this.popup = popup; this.addMouseListener(this); } public Molecule getMolecule() { return this.molecule; } public void mouseClicked(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON3) { this.popup.show(this, e.getX(), e.getY(), this.molecule); } } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } } } // ============================ InfoPanel =================================== /** * A class for displaying the table below the graph. */ private class InfoPanel extends JTabbedPane implements ChangeListener, SampleGroupChangeListener { private ConditionPanel conditionPanel; private JPanel topologyPanel; private JPanel degreeDistributionPanel; private JPanel correlationDistributionPanel; private JPanel neighborhoodConnectivityPanel; private JTable moleculeTable; private JTable correlationTable; /** * Creates a new InfoPanel. */ public InfoPanel() { super(); Language language = Settings.getLanguage(); this.conditionPanel = new ConditionPanel(); this.topologyPanel = new TopologyPanel(); this.degreeDistributionPanel = new DegreeDistributionPanel(); this.correlationDistributionPanel = new CorrelationDistributionPanel(); this.neighborhoodConnectivityPanel = new NeighborhoodConnectivityDistributionPanel(); this.moleculeTable = new JTable(0, 0) { public boolean isCellEditable(int row, int col) { return false; } }; this.correlationTable = new JTable(0, 0) { public boolean isCellEditable(int row, int col) { return false; } }; this.setBackground(Color.WHITE); this.conditionPanel.setBackground(Color.WHITE); this.topologyPanel.setBackground(Color.WHITE); this.moleculeTable.setBackground(Color.WHITE); this.correlationTable.setBackground(Color.WHITE); this.moleculeTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); this.correlationTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); this.add(new JScrollPane(moleculeTable), language.get("Molecules")); this.add(new JScrollPane(correlationTable), language.get("Correlations")); this.add(conditionPanel, language.get("Display Conditions")); this.add(topologyPanel, language.get("Topological Information")); this.add(degreeDistributionPanel, language.get("Node Degree Distribution")); this.add(correlationDistributionPanel, language.get("Correlation Distribution")); this.add(neighborhoodConnectivityPanel, language.get("Neighborhood Connectivity")); } /** * Adds a new Molecule to the table. * * @param molecule The new molecule to be added. */ public void add(Molecule molecule) { DefaultTableModel tm = (DefaultTableModel) this.moleculeTable.getModel(); Map<String, String> attributes = molecule.getAttributes(); if (tm.getColumnCount() == 0) { tm.addColumn("id"); for (String key : attributes.keySet()) { tm.addColumn(key); } for (Sample sample : samples) { tm.addColumn(sample.toString()); } Enumeration<TableColumn> columns = this.moleculeTable.getColumnModel().getColumns(); while (columns.hasMoreElements()) columns.nextElement().setPreferredWidth(75); } int c = attributes.size() + samples.size() + 1; Object[] newRow = new Object[c]; int i = 0; newRow[i++] = molecule.getId(); for (String value : attributes.values()) { newRow[i++] = value.toString(); } for (Sample sample : samples) { newRow[i++] = sample.getValue(molecule); } tm.addRow(newRow); } /** * Adds a new Correlation to the table. * * @param correlation The new Correlation to be added. */ public void add(Correlation correlation) { Language language = Settings.getLanguage(); DefaultTableModel tm = (DefaultTableModel) this.correlationTable.getModel(); if (tm.getColumnCount() == 0) { String[] keys = { language.get("Molecule") + " 1", language.get("Molecule") + " 2", language.get("Pearson Value"), language.get("Spearman Rank Value"), language.get("Kendall Tau-b Rank Value") }; for (String key : keys) { tm.addColumn(key); } Enumeration<TableColumn> columns = this.correlationTable.getColumnModel().getColumns(); int column = 0; while (columns.hasMoreElements()) { TableColumn tc = columns.nextElement(); tc.setPreferredWidth(175); switch (column) { case 2: tc.setCellRenderer(new PickedColumnRenderer(pearsonCalculationMenuItem)); break; case 3: tc.setCellRenderer(new PickedColumnRenderer(spearmanCalculationMenuItem)); break; case 4: tc.setCellRenderer(new PickedColumnRenderer(kendallCalculationMenuItem)); break; } column++; } pearsonCalculationMenuItem.addChangeListener(this); spearmanCalculationMenuItem.addChangeListener(this); kendallCalculationMenuItem.addChangeListener(this); } Object[] newRow = new Object[5]; newRow[0] = correlation.getFirst().getId(); newRow[1] = correlation.getSecond().getId(); newRow[2] = String.format("%.5f", correlation.getValue(Correlation.PEARSON)); newRow[3] = String.format("%.5f", correlation.getValue(Correlation.SPEARMAN)); newRow[4] = String.format("%.5f", correlation.getValue(Correlation.KENDALL)); tm.addRow(newRow); } /** * Removes a Molecule from the table. Does nothing if the Molecule is not * present. * * @param molecule the Molecule to be removed. */ public void remove(Molecule molecule) { int row = this.getRowOf(molecule); if (row >= 0) ((DefaultTableModel) this.moleculeTable.getModel()).removeRow(row); } /** * Removes a Correlation from the table. Does nothing if the Correlation * is not present. * * @param correlation the Correlation to be added. */ public void remove(Correlation correlation) { int row = getRowOf(correlation); if (row >= 0) ((DefaultTableModel) this.correlationTable.getModel()).removeRow(row); } /** * Clears the Molecule table. */ public void clearMolecules() { DefaultTableModel tm = (DefaultTableModel) this.moleculeTable.getModel(); while (tm.getRowCount() > 0) { tm.removeRow(0); } } /** * Clears the Correlation Table. */ public void clearCorrelations() { DefaultTableModel tm = (DefaultTableModel) this.correlationTable.getModel(); while (tm.getRowCount() > 0) { tm.removeRow(0); } } /** * Finds the appropriate row in the table for the given Molecule. * * @param Molecule The Molecule to search for. * @return The row number of the Molecule. */ private int getRowOf(Molecule molecule) { DefaultTableModel tm = (DefaultTableModel) this.moleculeTable.getModel(); int returnValue = 0; boolean match; while (returnValue < tm.getRowCount()) { if (tm.getValueAt(returnValue, 0).equals(molecule.getId())) return returnValue; returnValue++; } return -1; } /** * Finds the apropriate row in the table for the given Correlation. * * @param correlation The Correlation to search for. * @return The row number of the Correlation. */ private int getRowOf(Correlation correlation) { DefaultTableModel tm = (DefaultTableModel) this.correlationTable.getModel(); int returnValue = 0; while (returnValue < tm.getRowCount()) { if (correlation.getFirst().getId().equals(tm.getValueAt(returnValue, 0)) && correlation.getSecond().getId().equals(tm.getValueAt(returnValue, 1))) return returnValue; returnValue++; } return -1; } public void stateChanged(ChangeEvent event) { correlationTable.repaint(); } public void groupStateChanged(SampleGroupChangeEvent event) { this.conditionPanel.groupStateChanged(event); } // ====================== PickedColumnRenderer ========================== /** * A class for highlighting the selected correlation in the InfoTable */ private class PickedColumnRenderer extends DefaultTableCellRenderer { private AbstractButton pickedIndicator; private Color pickedColor = Color.YELLOW; public PickedColumnRenderer(AbstractButton indicator) { super(); this.pickedIndicator = indicator; } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component cell = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); if (!isSelected) { if (pickedIndicator.isSelected()) { cell.setBackground(this.pickedColor); } else { cell.setBackground(null); } } return cell; } } // ======================== ConditionPanel ============================= /** * A UI class for displaying the current graph conditions. */ private class ConditionPanel extends JPanel implements ActionListener, SampleGroupChangeListener { private JScrollPane sampleGroupingScrollPane; private JTable sampleGroupingTable; /** * Creates a new ConditionPanel */ public ConditionPanel() { super(); this.sampleGroupingTable = new JTable() { public boolean isCellEditable(int row, int col) { return false; } }; this.sampleGroupingTable.setBackground(Color.WHITE); this.sampleGroupingScrollPane = new JScrollPane(this.sampleGroupingTable); this.setLayout(new BorderLayout()); this.add(this.sampleGroupingScrollPane, BorderLayout.EAST); // listen for changes to layout/calculation multipleCirclesLayoutMenuItem.addActionListener(this); multipleCirclesLayoutMenuItem.addActionListener(this); singleCircleLayoutMenuItem.addActionListener(this); randomLayoutMenuItem.addActionListener(this); kkLayoutMenuItem.addActionListener(this); // frLayoutMenuItem.addActionListener( this ); frSpringLayoutMenuItem.addActionListener(this); pearsonCalculationMenuItem.addActionListener(this); spearmanCalculationMenuItem.addActionListener(this); kendallCalculationMenuItem.addActionListener(this); } /** * Called when the Panel is repainted. * @see Component#paintComponent(java.awt.Graphics) * * @param g The graphics component associated with this panel */ public void paintComponent(Graphics g) { super.paintComponent(g); Language language = Settings.getLanguage(); String text; String layoutName; FontMetrics f = g.getFontMetrics(); try { layoutName = ((LayoutDecorator) graph.getGraphLayout()).getDelegate().getClass() .getSimpleName(); } catch (ClassCastException e) { layoutName = graph.getGraphLayout().getClass().getSimpleName(); } text = String.format(language.get("Layout") + ": %s", layoutName); g.drawString(text, 20, 30); text = String.format(language.get("Correlation Method") + ": %s", Correlation.NAME[correlationMethod.intValue()]); g.drawString(text, 20, 50); // if ( sampleGroups.size( ) > 1 ) { // // int leftMargin = 300; // // list the samples in group 1. // Iterator<SampleGroup> groupIterator = sampleGroups.iterator( ); // SampleGroup group1 = groupIterator.next( ); // text = group1.toString( ); // g.drawString( text, leftMargin, 50 ); // g.drawLine( leftMargin, 52, leftMargin + f.stringWidth( text ), 52 ); // int verticalPos = 70; // for ( Sample s : group1 ) { // g.drawString( s.toString( ), leftMargin, verticalPos ); // verticalPos += 20; // } // // // list the samples in group 2. // SampleGroup group2 = groupIterator.next( ); // text = group2.toString( ); // int col2Margin = leftMargin + 230; // int stringWidth = f.stringWidth( text ); // int rightMargin = col2Margin + stringWidth; // g.drawString( text, col2Margin, 50 ); // g.drawLine( col2Margin, 52, col2Margin + stringWidth, 52 ); // verticalPos = 70; // for ( Sample s : group2 ) { // g.drawString( s.toString( ), col2Margin, verticalPos ); // verticalPos += 20; // } // // // create a heading // int center = leftMargin + ( rightMargin - leftMargin ) / 2; // text = language.get( "Sample Groups" ); // stringWidth = f.stringWidth( text ); // g.drawString( text, center - stringWidth/2, 30 ); // g.drawLine( center - stringWidth/2, 32, center + stringWidth/2, 32 ); // } } /** * The actionPerformed method of the ActionListener interface. * @see ActionListener#actionPerformed(java.awt.event.ActionEvent) * * @param e The event which triggered this action. */ public void actionPerformed(ActionEvent e) { if (this.isVisible()) this.repaint(); } public void groupStateChanged(SampleGroupChangeEvent e) { Collection<SampleGroup> sampleGroups = e.getGroups(); DefaultTableModel tableModel = (DefaultTableModel) this.sampleGroupingTable.getModel(); tableModel.setColumnCount(0); tableModel.setRowCount(0); int columnCount = sampleGroups.size(); Iterator[] groups = new Iterator[columnCount]; int j = 0; for (SampleGroup group : sampleGroups) { tableModel.addColumn(group.toString()); groups[j++] = group.iterator(); } Object[] row = new Object[columnCount]; boolean done = false; do { done = true; for (int i = 0; i < columnCount; i++) { row[i] = groups[i].hasNext() ? groups[i].next().toString() : ""; done = done && !groups[i].hasNext(); } tableModel.addRow(row); } while (!done); if (this.isVisible()) this.repaint(); } } // ============================= TopologyPanel =========================== /** * A UI class for displaying the graph Topology. */ private class TopologyPanel extends JPanel implements GraphItemChangeListener { /** * Creates a new TopologyPanel. */ public TopologyPanel() { super(); graph.addVertexChangeListener(this); graph.addEdgeChangeListener(this); } /** * Called when the panel is repainted. * @see Component#PaintComponent(java.awt.Graphics) * * @param g The Graphics for this Component. */ public void paintComponent(Graphics g) { super.paintComponent(g); Language language = Settings.getLanguage(); Vector<Molecule> molecules = new Vector<Molecule>(graph.getVertices()); Vector<Correlation> correlations = new Vector<Correlation>(graph.getEdges()); // g.setFont( new Font( "Sans Serif", Font.BOLD, 18 )); String text; text = String.format(language.get("Number of Nodes") + ": %d", molecules.size()); g.drawString(text, 20, 16); text = String.format(language.get("Number of Edges") + ": %d", correlations.size()); g.drawString(text, 20, 34); text = String.format(language.get("Number of correlated molecules") + ": %d", getCorrelatedCount(molecules)); g.drawString(text, 20, 52); text = String.format(language.get("Average number of neighbors") + ": %.2f", getAverageNeighbors(molecules)); g.drawString(text, 20, 70); text = String.format(language.get("Network diameter") + ": %d", getNetworkDiameter(molecules)); g.drawString(text, 20, 86); text = String.format(language.get("Characteristic path length") + ": %.2f", getCharacteristicPathLength(molecules)); g.drawString(text, 20, 102); } /** * Returns the number of nodes which are connected to at least one other * node. */ public int getCorrelatedCount(Collection<Molecule> molecules) { int returnValue = 0; for (Molecule m : molecules) { if (graph.getNeighborCount(m) > 0) returnValue++; } return returnValue; } /** * Returns the average number of neighbors (connections) for all nodes in * the network. * * @param molecules A Collection of nodes to check. * @return The average connection count. */ public double getAverageNeighbors(Collection<Molecule> molecules) { int neighbors = 0; for (Molecule m : molecules) { neighbors += graph.getNeighborCount(m); } return (double) neighbors / molecules.size(); } /** * Returns the network diameter, or the longest shortest path from one * node to any other. Nodes which do not have a path to one another are * ignored. * * @param molecule The Collection of molecules to check. * @return The longest shortest path. */ public int getNetworkDiameter(Collection<Molecule> molecules) { int returnValue = 0; int currentValue; List<Correlation> path; for (Molecule m : molecules) { for (Molecule n : molecules) { path = graph.getShortestPath(m, n); if (path != null) { currentValue = path.size(); if (currentValue > returnValue) returnValue = currentValue; } } } return returnValue; } /** * Returns the characteristic path length for the network, or the average * shortest path length from one node to another. Nodes which do not have * a path to one another are ignored. * * @param molecules The collection of molecules to check. * @return The characteristic path length. */ public double getCharacteristicPathLength(Collection<Molecule> molecules) { double returnValue = 0.0; int count = 0; List<Correlation> path; for (Molecule m : molecules) { for (Molecule n : molecules) { path = graph.getShortestPath(m, n); if (path != null) { returnValue += path.size(); count++; } } } return returnValue / count; } /** * The stateChanged method of the GraphItemChangeListener interface. * @see GraphItemChangeListener#stateChanged(GraphItemChangeEvent) * * @param event The event which triggered this action. */ public void stateChanged(GraphItemChangeEvent event) { if (this.isVisible()) this.repaint(); } } // ========================== DistributionPanel =========================== private abstract class DistributionPanel extends JPanel implements GraphItemChangeListener { protected JFreeChart distributionChart; protected DefaultCategoryDataset distributionData; protected DistributionPanel() { super(); graph.addVertexChangeListener(this); graph.addEdgeChangeListener(this); distributionData = new DefaultCategoryDataset(); distributionChart = ChartFactory.createBarChart(null, //title this.getCategoryAxisLabel(), // category axis label this.getValueAxisLabel(), // value axis label distributionData, // plot data PlotOrientation.VERTICAL, // Plot Orientation false, // show legend false, // use tooltips false // configure chart to generate URLs ); // this.distributionChart.getTitle( ).setFont( new Font( "Arial", Font.BOLD, 18 )); CategoryPlot plot = distributionChart.getCategoryPlot(); BarRenderer renderer = (BarRenderer) plot.getRenderer(); renderer.setBarPainter(new StandardBarPainter()); renderer.setShadowVisible(false); plot.setBackgroundPaint(Color.WHITE); plot.setRangeGridlinePaint(Color.GRAY); plot.setDomainGridlinePaint(Color.GRAY); // add a context menu for saving the graph to an image new ContextMenu(this).add(new SaveImageAction(this)); } public abstract void getDistributionData(DefaultCategoryDataset distributionData); public void paintComponent(Graphics g) { CategoryPlot plot = this.distributionChart.getCategoryPlot(); plot.getDomainAxis().setLabel(this.getCategoryAxisLabel()); plot.getRangeAxis().setLabel(this.getValueAxisLabel()); super.paintComponent(g); getDistributionData(distributionData); g.drawImage(distributionChart.createBufferedImage(getWidth(), getHeight()), 0, 0, Color.WHITE, this); } public void stateChanged(GraphItemChangeEvent event) { if (this.isVisible()) this.repaint(); } public abstract String getCategoryAxisLabel(); public abstract String getValueAxisLabel(); } // ========================= DegreeDistributionPanel ====================== private class DegreeDistributionPanel extends DistributionPanel { public DegreeDistributionPanel() { super(); distributionChart.getCategoryPlot().getRangeAxis() .setStandardTickUnits(NumberAxis.createIntegerTickUnits()); // add a context menu for saving the graph to an image new ContextMenu(this).add(new SaveImageAction(this)); } public void getDistributionData(DefaultCategoryDataset distributionData) { Vector<Molecule> molecules = new Vector<Molecule>(graph.getVertices()); int max = -1; int[] dist = new int[molecules.size()]; for (Molecule m : molecules) { int currentDeg = graph.getNeighborCount(m); max = Math.max(max, currentDeg); dist[currentDeg]++; } distributionData.clear(); for (int i = 0; i <= max; i++) { distributionData.addValue(dist[i], "", new Integer(i)); } } public String getCategoryAxisLabel() { return Settings.getLanguage().get("Neighbor Count") + String.format("(%s, Range %.3f - %.3f)", Correlation.NAME[correlationMethod.intValue()], correlationFilterPanel.getRange().getMin(), correlationFilterPanel.getRange().getMax()); } public String getValueAxisLabel() { return Settings.getLanguage().get("Nodes"); } } // ===================== CorrelationDistributionPanel ====================== private class CorrelationDistributionPanel extends JPanel implements ItemListener, GraphItemChangeListener { protected SimpleHistogramDataset distributionData; protected JFreeChart distributionChart; public CorrelationDistributionPanel() { super(); // graph.addVertexChangeListener( this ); // graph.addEdgeChangeListener( this ); Language language = Settings.getLanguage(); pearsonCalculationMenuItem.addItemListener(this); spearmanCalculationMenuItem.addItemListener(this); kendallCalculationMenuItem.addItemListener(this); // add a context menu for saving the graph to an image new ContextMenu(this).add(new SaveImageAction(this)); this.distributionData = new SimpleHistogramDataset("Correlation Distribution"); distributionChart = ChartFactory.createHistogram(null, //title language.get("Correlation Value (All correlations, " + Correlation.NAME[correlationMethod.intValue()] + ")"), // category axis label language.get("Number of Correlations"), // value axis label distributionData, // plot data PlotOrientation.VERTICAL, // Plot Orientation false, // show legend false, // use tooltips false // configure chart to generate URLs ); // this.distributionChart.getTitle( ).setFont( new Font( "Arial", Font.BOLD, 18 )); boolean includeLower = false, includeUpper = true; for (int i = -100; i < 100; i++) { if (i == -1) includeUpper = false; else if (i == 0) includeLower = true; SimpleHistogramBin s = new SimpleHistogramBin(i * 0.01, (i + 1) * 0.01, includeLower, includeUpper); distributionData.addBin(s); } XYPlot plot = distributionChart.getXYPlot(); XYBarRenderer renderer = (XYBarRenderer) plot.getRenderer(); renderer.setBarPainter(new StandardXYBarPainter()); renderer.setShadowVisible(false); plot.getDomainAxis().setRange(-1.0, 1.0); plot.setBackgroundPaint(Color.WHITE); plot.setRangeGridlinePaint(Color.GRAY); plot.setDomainGridlinePaint(Color.GRAY); } public void paintComponent(Graphics g) { XYPlot plot = this.distributionChart.getXYPlot(); plot.getDomainAxis().setLabel(Settings.getLanguage().get("Correlation Value (All correlations, " + Correlation.NAME[correlationMethod.intValue()] + ")")); super.paintComponent(g); getDistributionData(distributionData); g.drawImage(distributionChart.createBufferedImage(getWidth(), getHeight()), 0, 0, Color.WHITE, this); } public void getDistributionData(SimpleHistogramDataset distributionData) { Collection<Correlation> edges = correlations; Range correlationRange = correlationFilterPanel.getRange(); distributionData.clearObservations(); for (Correlation c : edges) { double value = c.getValue(correlationMethod); // if ( correlationRange.contains( Math.abs( value ))) { try { distributionData.addObservation(value); } catch (RuntimeException e) { Logger.getLogger(getClass()).debug(String.format("No bin available for value %f", value), e); } // } } } public void stateChanged(GraphItemChangeEvent event) { if (this.isVisible()) this.repaint(); } public void itemStateChanged(ItemEvent e) { if (this.isVisible()) this.repaint(); } } // ================== NeighborhoodConnectivityDistributionPanel ========== private class NeighborhoodConnectivityDistributionPanel extends DistributionPanel { public NeighborhoodConnectivityDistributionPanel() { super(); } public void getDistributionData(DefaultCategoryDataset distributionData) { Vector<Molecule> nodes = new Vector<Molecule>(graph.getVertices()); int[] neighborCount = new int[nodes.size()]; int[] nodeCount = new int[nodes.size()]; int maxNeighborCount = 0; // First, go through all of the nodes, finding how many neighbors they // have and add the neighbors' degrees to the appropriate "bucket". // also increment the "buckets" to keep track of how many nodes have // which degree. for (int i = 0; i < nodes.size(); i++) { Molecule m = nodes.get(i); Collection<Molecule> neighbors = graph.getNeighbors(m); nodeCount[neighbors.size()]++; if (neighbors.size() > maxNeighborCount) maxNeighborCount = neighbors.size(); for (Molecule n : neighbors) { neighborCount[neighbors.size()] += graph.getNeighborCount(n); } } // Now add the data we collected to the chart. distributionData.clear(); for (int i = 1; i <= maxNeighborCount; i++) { distributionData.addValue((double) neighborCount[i] / (nodeCount[i] * i), "", String.format("%d", i)); } } public String getCategoryAxisLabel() { return Settings.getLanguage().get("Neighbor Count") + String.format("(%s, Range %.3f - %.3f)", Correlation.NAME[correlationMethod.intValue()], correlationFilterPanel.getRange().getMin(), correlationFilterPanel.getRange().getMax()); } public String getValueAxisLabel() { return Settings.getLanguage().get("Average Neighbor Degree"); } } } // ========================= MenuItemListener ============================ /** * A class for listening for clicks on menu items. * @todo Integrate this listener with the CorrelationDisplayPanel class. */ private class MenuItemListener implements ActionListener { /** * The actionPerformed method of the ActionListener interface. * @see ActionListener#actionPerformed(ActionEvent) * * @param event The event which triggered this action. */ public void actionPerformed(ActionEvent event) { Component item = (Component) event.getSource(); if (item == selectAllViewMenuItem) { graph.selectAll(); } else if (item == clearSelectionViewMenuItem) { graph.clearSelection(); } } } // ========================== LayoutChangeListener ========================= /** * A class for listening for changes to the layout menu. * @todo Integrate this class with the CorrelationDisplayPanel class. */ private class LayoutChangeListener implements ActionListener { /** * The actionPerformed method of the ActionListener interface. # @see ActionListener#actionPerformed(ActionEvent) * * @param event The event which triggered this action. */ public void actionPerformed(ActionEvent event) { Component item = (Component) event.getSource(); // if ( item == animatedLayoutMenuItem ) { // graph.animate( animatedLayoutMenuItem.getState( )); // } else { graph.animate(false); if (item == multipleCirclesLayoutMenuItem) { // MultipleCircleLayout needs a copy of the sampleGroups // to initialize. setGraphLayout(MultipleCirclesLayout.class); Layout layout = graph.getGraphLayout(); while (layout instanceof LayoutDecorator) { layout = ((LayoutDecorator) layout).getDelegate(); } MultipleCirclesLayout mclayout = (MultipleCirclesLayout) layout; mclayout.setSampleGroups(new SimplePair<SampleGroup>(getSampleGroups())); mclayout.initialize(); } else if (item == singleCircleLayoutMenuItem) { setGraphLayout(CircleLayout.class); } else if (item == randomLayoutMenuItem) { setGraphLayout(RandomLayout.class); } else if (item == kkLayoutMenuItem) { setGraphLayout(KKLayout.class); // } else if ( item == frLayoutMenuItem ) { // setGraphLayout( FRLayout.class ); // } else if ( item == springLayoutMenuItem ) { // setGraphLayout( SpringLayout2.class ); } else if (item == frSpringLayoutMenuItem) { setGraphLayout(CircleLayout.class); graph.animate(true); } else if (item == heatMapLayoutMenuItem) { heatMap(); } // } } } // ==================== CorrelationGraphMouseListener ====================== /** * A class for implementing a context menu on network nodes. */ private class CorrelationGraphMouseListener implements GraphMouseListener<Molecule> { MoleculePopup popup = new MoleculePopup(); /** * The graphClicked method of the GraphMouseListener class * * @param m The Molecule (node) which was clicked on. * @param e The event which triggered this action. */ public void graphClicked(Molecule m, MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON3) { popup.show(e.getComponent(), e.getX(), e.getY(), m); } else if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() >= 2) { CorrelationGraphVisualizer graph = (CorrelationGraphVisualizer) e.getComponent(); new DetailWindow(correlations, m, graph.getRange(), correlationMethod.intValue()); } } /** * The graphPressed method of the GraphMouseListener class. Not implemented. * * @param m The Molecule (node) which was clicked on. * @param e The event which triggered this action. */ public void graphPressed(Molecule m, MouseEvent e) { } /** * The graphPressed method of the GraphMouseListener class. Not implemented. * * @param m The Molecule (node) which was clicked on. * @param e The event which triggered this action. */ public void graphReleased(Molecule m, MouseEvent e) { } } // =========================== MoleculePopup ============================ /** * A class for implementing the context menu. */ private class MoleculePopup extends JPopupMenu implements ActionListener { protected JMenuItem hideMenuItem; protected JMenuItem showMenuItem; protected JMenuItem detailsMenuItem; protected JMenuItem selectMenuItem; protected JMenuItem selectCorrelatedMenuItem; protected JMenuItem selectSubnetworkMenuItem; protected JMenuItem exploreCorrelationsMenu; protected Molecule molecule; protected HashMap<JMenuItem, Correlation> correlationMap = new HashMap<JMenuItem, Correlation>(); protected SaveImageAction saveImageAction; /** * Creates a new instance of the PopupMenu */ public MoleculePopup() { super(); Language language = Settings.getLanguage(); this.hideMenuItem = new JMenuItem(language.get("Hide")); this.showMenuItem = new JMenuItem(language.get("Show")); this.detailsMenuItem = new JMenuItem(language.get("Details")); this.selectMenuItem = new JMenuItem(language.get("Toggle selection")); this.selectCorrelatedMenuItem = new JMenuItem(language.get("Select Directly Correlated")); this.selectSubnetworkMenuItem = new JMenuItem(language.get("Select Subnetwork")); this.exploreCorrelationsMenu = new JMenu(language.get("Explore Correlations")); this.add(this.hideMenuItem); this.add(this.showMenuItem); this.add(this.detailsMenuItem); this.add(this.selectMenuItem); this.add(this.selectCorrelatedMenuItem); this.add(this.selectSubnetworkMenuItem); this.add(this.exploreCorrelationsMenu); this.addSeparator(); this.saveImageAction = new SaveImageAction(graph); this.add(this.saveImageAction); this.hideMenuItem.addActionListener(this); this.detailsMenuItem.addActionListener(this); this.selectMenuItem.addActionListener(this); this.selectSubnetworkMenuItem.addActionListener(this); this.selectCorrelatedMenuItem.addActionListener(this); } /** * Causes the JPopupMenu to be displayed at the given coordinates. * @see JPopupMenu#show(Component,int,int) * * @param invoker The component which invoked this menu. * @param x The x position to display this menu. * @param y The y position to display this menu. * @param m The molecule which was clicked on to trigger this popup. */ public void show(Component invoker, int x, int y, Molecule m) { this.exploreCorrelationsMenu.removeAll(); this.correlationMap.clear(); this.molecule = m; this.saveImageAction.setComponent(graph); boolean containsVertex = graph.containsVertex(m); this.hideMenuItem.setEnabled(containsVertex); this.showMenuItem.setEnabled(!containsVertex); this.selectMenuItem.setEnabled(containsVertex); this.selectSubnetworkMenuItem.setEnabled(containsVertex); this.selectCorrelatedMenuItem.setEnabled(containsVertex); Component displayPanel = invoker; while (displayPanel != null && !(displayPanel instanceof CorrelationDisplayPanel)) { displayPanel = displayPanel.getParent(); } Range range = ((CorrelationDisplayPanel) displayPanel).getCorrelationRange(); for (Correlation c : correlations) { if (c.contains(m) && range.contains(Math.abs(c.getValue(correlationMethod)))) { JMenuItem menuItem = new JMenuItem(c.getOpposite(m).toString()); this.correlationMap.put(menuItem, c); this.exploreCorrelationsMenu.add(menuItem); menuItem.addActionListener(this); } } this.show(invoker, x, y); } /** * The actionPerformed method of the ActionListener interface. * @see ActionListner#actionPerformed(ActionEvent) * * @param e the event which triggered this action. */ public void actionPerformed(ActionEvent e) { Component displayPanel = this.getInvoker(); while (displayPanel != null && !(displayPanel instanceof CorrelationDisplayPanel)) { displayPanel = displayPanel.getParent(); } CorrelationGraphVisualizer graph = (CorrelationGraphVisualizer) ((CorrelationDisplayPanel) displayPanel) .getGraph(); Range range = graph.getRange(); Object source = e.getSource(); if (this.correlationMap.containsKey(source)) { new DetailWindow(correlations, this.correlationMap.get(source), range, correlationMethod.intValue()); } else if (source == this.hideMenuItem) { graph.removeVertex(this.molecule); } else if (source == this.detailsMenuItem) { new DetailWindow(correlations, this.molecule, range, correlationMethod.intValue()); } else if (source == this.selectMenuItem) { PickedState<Molecule> state = graph.getPickedVertexState(); state.pick(this.molecule, !state.isPicked(this.molecule)); } else if (source == this.selectCorrelatedMenuItem) { PickedState<Molecule> state = graph.getPickedVertexState(); if (!state.isPicked(molecule)) { state.pick(this.molecule, true); } for (Molecule m : graph.getNeighbors(this.molecule)) { state.pick(m, true); } PickedState<Correlation> edgeState = graph.getPickedEdgeState(); for (Correlation c : graph.getIncidentEdges(this.molecule)) { edgeState.pick(c, true); } } else if (source == this.selectSubnetworkMenuItem) { PickedState<Molecule> state = graph.getPickedVertexState(); PickedState<Correlation> edgeState = graph.getPickedEdgeState(); Collection<Molecule> subnetwork = this.getSubnetwork(this.molecule, graph, null); for (Molecule m : subnetwork) { state.pick(m, true); for (Correlation c : graph.getIncidentEdges(m)) { edgeState.pick(c, true); } } } } /** * Recursive function for selecting connected nodes. * * @param molecule The central molcule to select all connected nodes for. * @param graph The graph the molecule belongs to. */ private Collection<Molecule> getSubnetwork(Molecule molecule, CorrelationGraphVisualizer graph, Collection<Molecule> collection) { if (collection == null) collection = new ArrayList<Molecule>(); if (collection.contains(molecule)) return collection; collection.add(molecule); for (Molecule m : graph.getNeighbors(molecule)) { this.getSubnetwork(m, graph, collection); } return collection; } } // ====================== CorrelationFilterPanel ============================= /** * A UI class which shows a set of Spinners for adjusting the visible * correlation range. */ private class CorrelationFilterPanel extends JPanel implements ChangeListener { private JLabel minCorrelationLabel; private JLabel maxCorrelationLabel; private JPanel minCorrelationFilterPanel = new JPanel(); private JPanel maxCorrelationFilterPanel = new JPanel(); private JSpinner minCorrelationSpinner; private JSpinner maxCorrelationSpinner; private MonitorableRange range; /** * Creates a new CorrelationFilterPanel with the default correlation range * (0.6-1.0) a step of 0.5, min of 0.0 and max of 1.0 */ public CorrelationFilterPanel() { this(0.8, 1.0); } /** * Creates a new CorrelationFilterPanel with the the specified correlation * range, a step of 0.05, and min of 0.0, max of 1.0. * * @param low The initial low setting for the filter. * @param high The initial high setting for the filter. */ public CorrelationFilterPanel(double low, double high) { this(0.0, 1.0, 0.05, low, high); } /** * Creates a new Correlation filter with the passed in values. * * @param min The minimum allowed value in the spinners. * @param max The maximum allowed value in the spinners. * @param step The step amount for each spinner click. * @param low The initial low setting for the filter. * @param high The initial high setting for the filter. */ public CorrelationFilterPanel(double min, double max, double step, double low, double high) { super(new BorderLayout()); Language language = Settings.getLanguage(); this.minCorrelationLabel = new JLabel(language.get("Higher Than") + ": ", SwingConstants.RIGHT); this.maxCorrelationLabel = new JLabel(language.get("Lower Than") + ": ", SwingConstants.RIGHT); this.range = new MonitorableRange(low, high); this.minCorrelationSpinner = new JSpinner(new SpinnerNumberModel(low, min, max, step)); this.maxCorrelationSpinner = new JSpinner(new SpinnerNumberModel(high, min, max, step)); this.minCorrelationSpinner.setPreferredSize(new Dimension(80, 25)); this.maxCorrelationSpinner.setPreferredSize(new Dimension(80, 25)); this.minCorrelationFilterPanel = new JPanel(new BorderLayout()); this.minCorrelationFilterPanel.add(this.minCorrelationSpinner, BorderLayout.EAST); this.minCorrelationFilterPanel.add(this.minCorrelationLabel, BorderLayout.CENTER); this.maxCorrelationFilterPanel = new JPanel(new BorderLayout()); this.maxCorrelationFilterPanel.add(this.maxCorrelationSpinner, BorderLayout.EAST); this.maxCorrelationFilterPanel.add(this.maxCorrelationLabel, BorderLayout.CENTER); this.add(this.minCorrelationFilterPanel, BorderLayout.NORTH); this.add(this.maxCorrelationFilterPanel, BorderLayout.SOUTH); this.minCorrelationSpinner.addChangeListener(this); this.maxCorrelationSpinner.addChangeListener(this); this.minCorrelationSpinner.setBackground(Color.WHITE); this.maxCorrelationSpinner.setBackground(Color.WHITE); this.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.BLACK, 1), Settings.getLanguage().get("Correlation Filter"), TitledBorder.CENTER, TitledBorder.TOP)); } /** * Returns the current range setting of the filter. * * @return The current range setting of the filter. */ public Range getRange() { return range; } /** * Returns the current range setting of the filter as a MonitorableRange * which will accept a listener. * * @return The current range setting as a MonitorableRange object. */ public MonitorableRange getMonitorableRange() { return range; } /** * The stateChanged method of the ChangeListener interface. * @see javax.swing.event.ChangeListener#stateChanged(ChangeEvent) * * @param e The event which triggered this action. */ public void stateChanged(ChangeEvent e) { range.setRange(((Double) this.minCorrelationSpinner.getValue()).doubleValue(), ((Double) this.maxCorrelationSpinner.getValue()).doubleValue()); } } // ======================= CorrelationGraphVisualizer ====================== /** * A class for displaying a JUNG graph tailored for BioNet */ private class CorrelationGraphVisualizer extends GraphVisualizer<Molecule, Correlation> implements ChangeListener, GraphMouseListener<Correlation>, ComponentListener, SampleGroupChangeListener { public MonitorableRange range; protected CorrelationSet correlations; protected Collection<Molecule> molecules; protected Spectrum spectrum; protected SpectrumLegend spectrumLegend; protected RegulationLegend regulationLegend; protected Collection<SampleGroup> sampleGroups; protected Color upRegulationOutline = Color.GREEN.darker(); protected Color downRegulationOutline = Color.RED; protected Color vertexColor = Color.ORANGE; /** * Creates a new CorrelationGraphVisualizer. * * @param experiment The experiment to be associated with this * CorrelationGraphVisualizer. * @param range A MonitorableRange object used to determine which * Correlations to show on the graph. */ public CorrelationGraphVisualizer(CorrelationSet correlations, MonitorableRange range) { super(); this.setRange(range); this.setCorrelations(correlations); ArrayList<SampleGroup> sampleGroups = new ArrayList<SampleGroup>(); sampleGroups.add(new SampleGroup("", correlations.getSamples())); this.setSampleGroups(sampleGroups); this.addGraphMouseEdgeListener(this); this.addComponentListener(this); this.spectrum = new SplitSpectrum(range); this.spectrum.setOutOfRangePaint(Color.WHITE); this.spectrumLegend = new SpectrumLegend(this.spectrum, new Range(-1.0, 1.0)); this.regulationLegend = new RegulationLegend(this.upRegulationOutline, this.downRegulationOutline, this.vertexColor); this.setLayout(null); this.add(this.spectrumLegend); Transformer e = new Transformer<Correlation, Paint>() { public Paint transform(Correlation e) { if (getPickedEdgeState().isPicked(e)) { return pickedEdgePaint; } else { return spectrum.getPaint(e.getValue(correlationMethod)); } } }; this.getRenderer().setEdgeRenderer(new FastEdgeRenderer<Molecule, Correlation>()); this.getRenderContext().setEdgeDrawPaintTransformer(e); this.getRenderContext().setVertexStrokeTransformer(new Transformer<Molecule, Stroke>() { Stroke stroke = new BasicStroke(2); public Stroke transform(Molecule m) { return this.stroke; } }); } @Override public void setBackground(Color color) { super.setBackground(color); if (this.spectrumLegend != null) this.spectrumLegend.setBackground(color); if (this.regulationLegend != null) this.regulationLegend.setBackground(color); } @Override public void setForeground(Color color) { super.setForeground(color); if (this.spectrumLegend != null) this.spectrumLegend.setForeground(color); if (this.regulationLegend != null) this.regulationLegend.setForeground(color); } /** * Used to change the experiment which is displayed in this graph. * * @param experiment The new experiment. */ public void setCorrelations(CorrelationSet correlations) { // remove all edges and vertices. for (Molecule v : this.getVertices()) this.removeVertex(v); for (Correlation e : this.getEdges()) this.removeEdge(e); // add the new data. this.correlations = correlations; this.molecules = correlations.getMolecules(); this.addVertices(); this.addEdges(); } /** * The sampleGroupsChanged method of the SampleGroupChangeListener * interface. * * @param event The event which triggered this action. */ public void groupStateChanged(SampleGroupChangeEvent event) { this.setSampleGroups(event.getGroups()); } /** * Sets sample groups for up/downregulation indicators on the display. * * @param sg The selected groups. */ public void setSampleGroups(Collection<SampleGroup> sg) { this.sampleGroups = sg; this.getRenderContext().setVertexDrawPaintTransformer( new RegulationTransformer(sg, this.upRegulationOutline, this.downRegulationOutline, null)); if (sg.size() > 1) { resetSampleGroupsMenuItem.setEnabled(true); this.add(this.regulationLegend); this.componentMoved(new ComponentEvent(this, -1)); multipleCirclesLayoutMenuItem.setEnabled(true); } else { if (this.regulationLegend != null) { this.remove(this.regulationLegend); } resetSampleGroupsMenuItem.setEnabled(false); } this.repaint(); } /** * Sets a new range to be used for filtering. * * @param range The new MonitorableRange object to use. */ public void setRange(MonitorableRange range) { if (this.range != null) this.range.removeChangeListener(this); this.range = range; range.addChangeListener(this); } /** * Gets the current MonitorableRange object being used. * * @return The current MonitorableRange. */ public MonitorableRange getRange() { return this.range; } public CorrelationSet getCorrelations() { return this.correlations; } /** * Adds the Vertices (Molecules) to the Graph */ protected void addVertices() { for (Molecule molecule : this.correlations.getMolecules()) { this.graph.addVertex(molecule); } } /** * Adds the Edges (Correlations) to the Graph. */ protected void addEdges() { for (Correlation correlation : this.correlations) { if (this.isValidEdge(correlation)) { this.graph.addEdge(correlation, new edu.uci.ics.jung.graph.util.Pair<Molecule>(correlation.toArray(new Molecule[2])), EdgeType.UNDIRECTED); } } } /** * Filters the edges displayed in the graph based on the MonitorableRange * associated with this graph. * * @return The new number of edges contained in the graph. */ public int filterEdges() { int returnValue = 0; for (Correlation correlation : this.correlations) { if (this.isValidEdge(correlation)) { returnValue++; // this Correlation belongs on the graph, make sure it is there. if (!this.containsEdge(correlation)) { this.addEdge(correlation, new edu.uci.ics.jung.graph.util.Pair<Molecule>( correlation.toArray(new Molecule[2])), EdgeType.UNDIRECTED); } } else { // this Correlation does not belong on the graph, make sure it is // not there. if (this.containsEdge(correlation)) { this.getPickedEdgeState().pick(correlation, false); try { this.removeEdge(correlation); } catch (NullPointerException e) { Logger.getLogger(getClass()).debug("Removal of edge " + correlation + " failed!", e); } } } } this.repaint(); return returnValue; } /** * Checks to see if an edge is valid and belongs in the graph. * * @param correlation The edge to check. * @return true If the correlation should be shown, false otherwise. */ public boolean isValidEdge(Correlation correlation) { return (this.containsVertex(correlation.getFirst()) && this.containsVertex(correlation.getSecond()) && this.range.contains(Math.abs(correlation.getValue(correlationMethod)))); } /** * The stateChanged method of the ChangeListener interface. * @see ChangeListener#stateChanged(ChangeEvent) * * @param event The event which triggered this action. */ public void stateChanged(ChangeEvent event) { this.filterEdges(); } /** * The graphClicked method of the GraphMouseListener interface. * @see GraphMouseListener#graphClicked(V,MouseEvent) * * @param edge The edge which was clicked on to trigger the event. * @param event The event which triggered this action. */ public void graphClicked(Correlation edge, MouseEvent event) { if (event.getButton() == MouseEvent.BUTTON1) { PickedState<Molecule> state = this.getPickedVertexState(); // see if shift or ctrl is being held down if ((event.getModifiers() & (InputEvent.SHIFT_MASK | InputEvent.CTRL_MASK)) == 0) state.clear(); state.pick(edge.getFirst(), true); state.pick(edge.getSecond(), true); } } /** * The graphPressed method of the GraphMouseListener interface. Not * implemented. * * @param edge The edge which was clicked on to trigger the event. * @param event The event which triggered this action. */ public void graphPressed(Correlation edge, MouseEvent event) { } /** * The graphReleased method of the GraphMouseListener interface. Not * implemented. * * @param edge The edge which was clicked on to trigger the event. * @param event The event which triggered this action. */ public void graphReleased(Correlation edge, MouseEvent event) { } // ComponentListener Methods public void componentHidden(ComponentEvent e) { } public void componentMoved(ComponentEvent e) { int bottom, left, right; if (this.scrollPane != null) { Rectangle view = this.scrollPane.getViewport().getViewRect(); left = view.x; bottom = view.y + view.height; right = view.x + view.width; } else { left = 0; bottom = this.getHeight(); right = this.getWidth(); } Rectangle legendArea = new Rectangle(left + 20, bottom - 35, 150, 20); this.spectrumLegend.setBounds(legendArea); this.spectrumLegend.repaint(); if (this.sampleGroups.size() > 1) { legendArea = new Rectangle(right - 130, bottom - 120, 130, 120); this.regulationLegend.setBounds(legendArea); this.regulationLegend.repaint(); } } public void componentResized(ComponentEvent e) { componentMoved(e); } public void componentShown(ComponentEvent e) { } // ====================== RegulationTransformer ============================ /** * A class for deteriming the outline color of a Molecule vertex. */ private class RegulationTransformer implements Transformer<Molecule, Paint> { private Pair<SampleGroup> sampleGroups; private Paint upPaint; private Paint downPaint; private Paint neutralPaint; /** * Creates a new RegulationTransformer. * * @param sg A pair of SampleGroups to be used for determining up or * downregulation. * @param upPaint The Paint to be used for outlining upregulated * Molecules. * @param downPaint The Paint to be used for outlining downregulated * Molecules. * @param neutralPaint The Paint to be used for outlining Molecules which * are neither up or downregulated. */ private RegulationTransformer(Pair<SampleGroup> sg, Paint upPaint, Paint downPaint, Paint neutralPaint) { this.sampleGroups = sg; this.upPaint = upPaint; this.downPaint = downPaint; this.neutralPaint = neutralPaint; } /** * Creates a new RegulationTransformer. * * @param sg A Collection of SampleGroups to be used for determining up or * downregulation. The Collection should contain at least 2 Samplegroups. * @param upPaint The Paint to be used for outlining upregulated * Molecules. * @param downPaint The Paint to be used for outlining downregulated * Molecules. * @param neutralPaint The Paint to be used for outlining Molecules which * are neither up or downregulated. */ private RegulationTransformer(Collection<SampleGroup> sg, Paint upPaint, Paint downPaint, Paint neutralPaint) { if (sg instanceof Pair) { this.sampleGroups = (Pair<SampleGroup>) sg; } else { if (sg.size() >= 2) { Iterator<SampleGroup> iterator = sg.iterator(); this.sampleGroups = new SimplePair(iterator.next(), iterator.next()); } } this.upPaint = upPaint; this.downPaint = downPaint; this.neutralPaint = neutralPaint; } /** * Returns the appropriate Paint for the indicated molecule. * * @param m The Molecule to determine the outline Paint for. * @return The Paint to use. */ public Paint transform(Molecule m) { if (this.sampleGroups != null) { if (this.sampleGroups.getFirst().size() == 0 || this.sampleGroups.getSecond().size() == 0) { return this.neutralPaint; } double mean1 = m.getValues(sampleGroups.getFirst()).getMean(); double mean2 = m.getValues(sampleGroups.getSecond()).getMean(); double foldChange = Settings.getSettings().getDouble("preferences.correlation.foldChange", 2.0); if (mean2 / mean1 > foldChange) { return this.upPaint; } if (mean1 / mean2 > foldChange) { return this.downPaint; } } return this.neutralPaint; } } } // ====================== RegulationLegend ================================= private class RegulationLegend extends JPanel { private Color upColor; private Color downColor; private Color nodeColor; private int scaleSpace = 13; private boolean drawBorder = true; private NumberFormat numberFormat = new DecimalFormat("0.##"); /** * Creates a new RegulationLegend * * @param spectrum The Spectrum to create a legend for. * @param range The range of values to show on the legend. */ public RegulationLegend(Color upColor, Color downColor, Color nodeColor) { super(); this.upColor = upColor; this.downColor = downColor; this.nodeColor = nodeColor; this.setForeground(Color.BLACK); } protected void paintComponent(Graphics g) { super.paintComponent(g); stamp(g, new Rectangle(0, 0, this.getWidth(), this.getHeight())); } /** * "Stamps" the Graphics instance with the legend in the area specified. * * @param g The Graphics object to "stamp" * @param area The area of the Graphics object to stamp. */ public void stamp(Graphics g, Rectangle area) { // set up a smaller font Font origFont = g.getFont(); Language language = Settings.getLanguage(); int bottom = area.x + area.height; int right = area.y + area.width; int verticalCenter = area.x + area.height / 2; int horizCenter = area.y + area.width / 2; g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 12)); FontMetrics f = g.getFontMetrics(); String s = language.get("Regulation"); g.drawString(s, area.x + area.width / 2 - f.stringWidth(s) / 2, 20); s = language.get("Up"); g.drawString(s, horizCenter - 25 - f.stringWidth(s) / 2, 60); s = language.get("Down"); g.drawString(s, horizCenter + 25 - f.stringWidth(s) / 2, 60); s = language.get("Fold change: ") + Settings.getSettings().getDouble("preferences.correlation.foldChange", 2.0); g.drawString(s, horizCenter - f.stringWidth(s) / 2, 80); s = language.get("Group2 / Group1"); g.drawString(s, horizCenter - f.stringWidth(s) / 2, 100); // reset the font g.setFont(origFont); // draw the legend g.setColor(this.nodeColor); g.fillOval(horizCenter - 35, 27, 20, 20); g.fillOval(horizCenter + 15, 27, 20, 20); ((Graphics2D) g).setStroke(new BasicStroke(2)); g.setColor(this.upColor); g.drawOval(horizCenter - 35, 27, 20, 20); g.setColor(this.downColor); g.drawOval(horizCenter + 15, 27, 20, 20); g.setColor(this.getForeground()); } public void setBounds(Rectangle rect) { Graphics g = this.getGraphics(); if (g != null) { g.setColor(this.getParent().getBackground()); g.fillRect(0, 0, this.getWidth(), this.getHeight()); } super.setBounds(rect); } } }