/*
* Copyright (c) 2003-2012 Fred Hutchinson Cancer Research Center
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fhcrc.cpl.viewer.quant.gui;
import org.fhcrc.cpl.toolbox.gui.chart.*;
import org.fhcrc.cpl.toolbox.gui.ListenerHelper;
import org.fhcrc.cpl.toolbox.gui.HtmlViewerPanel;
import org.fhcrc.cpl.toolbox.gui.widget.SplashFrame;
import org.fhcrc.cpl.toolbox.TextProvider;
import org.fhcrc.cpl.toolbox.ApplicationContext;
import org.fhcrc.cpl.toolbox.Rounder;
import org.fhcrc.cpl.toolbox.commandline.CommandLineModule;
import org.fhcrc.cpl.toolbox.commandline.CommandLineModuleExecutionException;
import org.fhcrc.cpl.toolbox.commandline.arguments.*;
import org.fhcrc.cpl.toolbox.filehandler.SimpleXMLEventRewriter;
import org.fhcrc.cpl.toolbox.filehandler.TempFileManager;
import org.fhcrc.cpl.toolbox.proteomics.feature.Spectrum;
import org.fhcrc.cpl.toolbox.proteomics.filehandler.ProtXmlReader;
import org.fhcrc.cpl.toolbox.proteomics.ProteinUtilities;
import org.fhcrc.cpl.viewer.gui.WorkbenchFileChooser;
import org.fhcrc.cpl.viewer.gui.WorkbenchFrame;
import org.fhcrc.cpl.viewer.gui.ViewerInteractiveModuleFrame;
import org.fhcrc.cpl.viewer.Localizer;
import org.fhcrc.cpl.viewer.ViewerUserManualGenerator;
import org.fhcrc.cpl.viewer.qa.QAUtilities;
import org.fhcrc.cpl.viewer.commandline.modules.BaseViewerCommandLineModuleImpl;
import org.fhcrc.cpl.viewer.quant.commandline.PeptideQuantVisualizationCLM;
import org.fhcrc.cpl.viewer.quant.commandline.ProteinQuantChartsCLM;
import org.fhcrc.cpl.viewer.quant.QuantEvent;
import org.fhcrc.cpl.viewer.quant.QuantEventAssessor;
import org.apache.log4j.Logger;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.event.ChartProgressEvent;
import javax.swing.*;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.ListSelectionEvent;
import javax.xml.stream.events.XMLEvent;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.XMLStreamException;
import javax.xml.namespace.QName;
import javax.imageio.ImageIO;
import java.util.*;
import java.util.List;
import java.awt.event.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
import java.nio.channels.FileChannel;
import java.util.logging.Level;
/**
* This is the main GUI screen for Qurate. It uses SwiXML for the menu and for the broad outlines, but most of it
* is done right here.
*/
public class QuantitationReviewer extends JDialog
{
//Quantitation events
List<QuantEvent> quantEvents;
//Loaded quantitation summary file
protected File quantFile;
//The multi-chart display panel
protected TabbedMultiChartDisplayPanel multiChartDisplay;
//This is the only way we keep track of the currently-displayed quantitation event. An index into quantEvents
protected int displayedEventIndex = 0;
//everything
public JPanel contentPanel;
public JSplitPane splitPane;
//left of splitpane
public JPanel leftPanel;
//right of splitpane
public JPanel rightPanel;
//for navigating between events
public JPanel navigationPanel;
JButton backButton;
JButton forwardButton;
JButton showEventSummaryButton;
public ProteinQuantChartsCLM settingsCLM;
protected SplashFrame splashFrame;
protected final URL splashImageURL = QuantitationReviewer.class.getResource("qurate_splash.gif");
//For assigning event statuses
public JPanel curationPanel;
protected ButtonGroup quantCurationButtonGroup;
//need a reference to this in order to change title
protected JRadioButton onePeakRatioRadioButton;
ButtonModel unknownRadioButtonModel;
ButtonModel goodRadioButtonModel;
ButtonModel badRadioButtonModel;
ButtonModel onePeakRadioButtonModel;
protected ButtonGroup idCurationButtonGroup;
ButtonModel idUnknownRadioButtonModel;
ButtonModel idGoodRadioButtonModel;
ButtonModel idBadRadioButtonModel;
protected JButton saveChangesButton;
protected JButton filterPepXMLButton;
protected JTextField commentTextField;
//For displaying automatic assessment
public JPanel assessmentPanel;
protected JTextField assessmentTypeTextField;
protected JTextField assessmentDescTextField;
protected ProteinQuantSummaryFrame quantSummaryFrame;
//theoretical peak distribution
public JPanel theoreticalPeaksPanel;
protected PanelWithPeakChart theoreticalPeaksChart;
protected ProteinSummarySelectorFrame proteinSummarySelector;
//menu actions
public Action helpAction = new HelpAction();
public Action exitAction = new ExitAction();
public Action openFileAction;
public Action createChartsAction;
public Action saveAction = new SaveAction(this);
public Action filterPepXMLAction;
public Action proteinSummaryAction;
public Action aboutAction = new AbstractAction("About")
{
public void actionPerformed(ActionEvent e)
{
showSplashScreen();
}
};
public Action summaryChartsAction = new SummaryChartsAction();
protected QuantEventsSummaryTable eventSummaryTable;
protected Frame eventSummaryFrame;
//event properties
protected QuantEvent.QuantEventPropertiesTable propertiesTable;
protected JScrollPane propertiesScrollPane;
//Status message
public JPanel statusPanel;
public JLabel messageLabel;
//Sizes of things
protected int leftPanelWidth = 250;
protected int rightPanelWidth = 990;
protected int imagePanelWidth = 980;
protected int fullWidth = 1200;
protected int fullHeight = 1000;
protected int propertiesWidth = leftPanelWidth - 20;
protected int propertiesHeight = 300;
protected int chartPaneHeight = 950;
protected int theoreticalPeaksPanelHeight = 150;
protected JFrame summaryChartsFrame;
protected TabbedMultiChartDisplayPanel summaryChartsPanel;
protected static Logger _log = Logger.getLogger(QuantitationReviewer.class);
public QuantitationReviewer(boolean showSplash, boolean showFileOpen)
{
if (showSplash)
showSplashScreen();
initGUI();
if (showFileOpen)
openFileAction.actionPerformed(null);
if (showSplash)
splashFrame.dispose();
}
/**
* No-arg constructor doesn't pop up a file chooser, but it does show splash screen
*/
public QuantitationReviewer()
{
this(true, false);
}
public QuantitationReviewer(List<QuantEvent> quantEvents)
{
showSplashScreen();
initGUI();
displayQuantEvents(quantEvents);
splashFrame.dispose();
}
public QuantitationReviewer(File quantFile)
throws IOException
{
showSplashScreen();
initGUI();
displayQuantFile(quantFile);
splashFrame.dispose();
}
public void displayQuantEvents(List<QuantEvent> quantEvents)
{
this.quantEvents = quantEvents;
displayedEventIndex = 0;
eventSummaryTable.displayEvents(quantEvents);
buildSummaryCharts();
displayCurrentQuantEvent(true);
}
public void displayQuantFile(File quantFile)
throws IOException
{
this.quantFile = quantFile;
quantEvents = QuantEvent.loadQuantEvents(quantFile);
//handling for empty file
if (quantEvents != null && !quantEvents.isEmpty())
{
displayQuantEvents(quantEvents);
setMessage("Loaded quantitation events from file " + quantFile.getAbsolutePath());
}
else
setMessage("No events found in file " + quantFile.getAbsolutePath());
}
/**
* Initialize all GUI components and display the UI
*/
protected void initGUI()
{
settingsCLM = new ProteinQuantChartsCLM(false);
setTitle("Qurate");
try
{
setIconImage(ImageIO.read(WorkbenchFrame.class.getResourceAsStream("icon.gif")));
}
catch (Exception e)
{
}
try
{
Localizer.renderSwixml("org/fhcrc/cpl/viewer/quant/gui/QuantitationReviewer.xml",this);
assert null != contentPanel;
}
catch (Exception x)
{
ApplicationContext.errorMessage("error creating dialog", x);
throw new RuntimeException(x);
}
//Menu
openFileAction = new OpenFileAction(this);
createChartsAction = new CreateChartsAction();
filterPepXMLAction = new FilterPepXMLAction(this);
proteinSummaryAction = new ProteinSummaryAction(this);
try
{
JMenuBar jmenu = (JMenuBar)Localizer.getSwingEngine(this).render(
"org/fhcrc/cpl/viewer/quant/gui/QuantitationReviewerMenu.xml");
for (int i=0 ; i<jmenu.getMenuCount() ; i++)
jmenu.getMenu(i).getPopupMenu().setLightWeightPopupEnabled(false);
this.setJMenuBar(jmenu);
}
catch (Exception x)
{
ApplicationContext.errorMessage(TextProvider.getText("ERROR_LOADING_MENUS"), x);
throw new RuntimeException(x);
}
//Global stuff
setSize(fullWidth, fullHeight);
setContentPane(contentPanel);
ListenerHelper helper = new ListenerHelper(this);
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.anchor = GridBagConstraints.PAGE_START;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(5,5,5,5);
gbc.weighty = 1;
gbc.weightx = 1;
leftPanel.setLayout(new GridBagLayout());
leftPanel.setBorder(BorderFactory.createLineBorder(Color.gray));
//Properties panel stuff
propertiesTable = new QuantEvent.QuantEventPropertiesTable();
propertiesScrollPane = new JScrollPane();
propertiesScrollPane.setViewportView(propertiesTable);
propertiesScrollPane.setMinimumSize(new Dimension(propertiesWidth, propertiesHeight));
//event summary table; disembodied
eventSummaryTable = new QuantEventsSummaryTable();
eventSummaryTable.setVisible(true);
ListSelectionModel tableSelectionModel = eventSummaryTable.getSelectionModel();
tableSelectionModel.addListSelectionListener(new EventSummaryTableListSelectionHandler());
JScrollPane eventSummaryScrollPane = new JScrollPane();
eventSummaryScrollPane.setViewportView(eventSummaryTable);
eventSummaryScrollPane.setSize(propertiesWidth, propertiesHeight);
eventSummaryFrame = new Frame("All Events");
eventSummaryFrame.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent event)
{
eventSummaryFrame.setVisible(false);
}
});
eventSummaryFrame.setSize(950, 450);
eventSummaryFrame.add(eventSummaryScrollPane);
//fields related to navigation
navigationPanel = new JPanel();
backButton = new JButton("<");
backButton.setToolTipText("Previous Event");
backButton.setMaximumSize(new Dimension(50, 30));
backButton.setEnabled(false);
forwardButton = new JButton(">");
forwardButton.setToolTipText("Next Event");
forwardButton.setMaximumSize(new Dimension(50, 30));
forwardButton.setEnabled(false);
showEventSummaryButton = new JButton("Show All");
showEventSummaryButton.setToolTipText("Show all events in a table");
showEventSummaryButton.setEnabled(false);
helper.addListener(backButton, "buttonBack_actionPerformed");
helper.addListener(forwardButton, "buttonForward_actionPerformed");
helper.addListener(showEventSummaryButton, "buttonShowEventSummary_actionPerformed");
gbc.fill = GridBagConstraints.NONE;
gbc.gridwidth = GridBagConstraints.RELATIVE;
gbc.anchor = GridBagConstraints.WEST;
navigationPanel.add(backButton, gbc);
gbc.gridwidth = GridBagConstraints.RELATIVE;
navigationPanel.add(forwardButton, gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
navigationPanel.add(showEventSummaryButton, gbc);
gbc.fill = GridBagConstraints.BOTH;
navigationPanel.setBorder(BorderFactory.createTitledBorder("Event"));
gbc.anchor = GridBagConstraints.PAGE_START;
//Fields related to curation of events
curationPanel = new JPanel();
curationPanel.setLayout(new GridBagLayout());
curationPanel.setBorder(BorderFactory.createTitledBorder("Curation"));
//Quantitation curation
JPanel quantCurationPanel = new JPanel();
quantCurationPanel.setLayout(new GridBagLayout());
quantCurationPanel.setBorder(BorderFactory.createTitledBorder("Quantitation"));
quantCurationButtonGroup = new ButtonGroup();
JRadioButton unknownRadioButton = new JRadioButton("?");
JRadioButton goodRadioButton = new JRadioButton("Good");
JRadioButton badRadioButton = new JRadioButton("Bad");
onePeakRatioRadioButton = new JRadioButton("1-Peak");
unknownRadioButton.setEnabled(false);
goodRadioButton.setEnabled(false);
badRadioButton.setEnabled(false);
onePeakRatioRadioButton.setEnabled(false);
quantCurationButtonGroup.add(unknownRadioButton);
quantCurationButtonGroup.add(goodRadioButton);
quantCurationButtonGroup.add(badRadioButton);
quantCurationButtonGroup.add(onePeakRatioRadioButton);
unknownRadioButtonModel = unknownRadioButton.getModel();
goodRadioButtonModel = goodRadioButton.getModel();
badRadioButtonModel = badRadioButton.getModel();
onePeakRadioButtonModel = onePeakRatioRadioButton.getModel();
helper.addListener(unknownRadioButton, "buttonCuration_actionPerformed");
helper.addListener(goodRadioButton, "buttonCuration_actionPerformed");
helper.addListener(badRadioButton, "buttonCuration_actionPerformed");
helper.addListener(onePeakRadioButtonModel, "buttonCuration_actionPerformed");
gbc.anchor = GridBagConstraints.WEST;
quantCurationPanel.add(unknownRadioButton, gbc);
quantCurationPanel.add(badRadioButton, gbc);
quantCurationPanel.add(goodRadioButton, gbc);
quantCurationPanel.add(onePeakRatioRadioButton, gbc);
gbc.anchor = GridBagConstraints.PAGE_START;
//ID curation
JPanel idCurationPanel = new JPanel();
idCurationPanel.setLayout(new GridBagLayout());
idCurationPanel.setBorder(BorderFactory.createTitledBorder("ID"));
idCurationButtonGroup = new ButtonGroup();
JRadioButton idUnknownRadioButton = new JRadioButton("?");
JRadioButton idGoodRadioButton = new JRadioButton("Good");
JRadioButton idBadRadioButton = new JRadioButton("Bad");
idUnknownRadioButton.setEnabled(false);
idGoodRadioButton.setEnabled(false);
idBadRadioButton.setEnabled(false);
idCurationButtonGroup.add(idUnknownRadioButton);
idCurationButtonGroup.add(idGoodRadioButton);
idCurationButtonGroup.add(idBadRadioButton);
idUnknownRadioButtonModel = idUnknownRadioButton.getModel();
idGoodRadioButtonModel = idGoodRadioButton.getModel();
idBadRadioButtonModel = idBadRadioButton.getModel();
helper.addListener(idUnknownRadioButton, "buttonIDCuration_actionPerformed");
helper.addListener(idGoodRadioButton, "buttonIDCuration_actionPerformed");
helper.addListener(idBadRadioButton, "buttonIDCuration_actionPerformed");
gbc.anchor = GridBagConstraints.WEST;
idCurationPanel.add(idUnknownRadioButton, gbc);
idCurationPanel.add(idBadRadioButton, gbc);
idCurationPanel.add(idGoodRadioButton, gbc);
gbc.gridwidth = GridBagConstraints.RELATIVE;
curationPanel.add(quantCurationPanel,gbc);
gbc.gridwidth = GridBagConstraints.REMAINDER;
curationPanel.add(idCurationPanel,gbc);
//curation comment
commentTextField = new JTextField();
commentTextField.setToolTipText("Comment on this event");
//saves after every keypress. Would be more efficient to save when navigating away or saving to file
commentTextField.addKeyListener(
new KeyAdapter() {
public void keyReleased(KeyEvent e) {
if (quantEvents == null)
return;
QuantEvent quantEvent = quantEvents.get(displayedEventIndex);
//save the comment, being careful about tabs and new lines
quantEvent.setComment(commentTextField.getText().replace("\t"," ").replace("\n"," "));
}
public void keyTyped(KeyEvent e) {
}
public void keyPressed(KeyEvent e) {
}
}
);
curationPanel.add(commentTextField,gbc);
assessmentPanel = new JPanel();
assessmentPanel.setLayout(new GridBagLayout());
assessmentPanel.setBorder(BorderFactory.createTitledBorder("Algorithmic Assessment"));
assessmentTypeTextField = new JTextField();
assessmentTypeTextField.setEditable(false);
assessmentPanel.add(assessmentTypeTextField, gbc);
assessmentDescTextField = new JTextField();
assessmentDescTextField.setEditable(false);
assessmentPanel.add(assessmentDescTextField, gbc);
//Theoretical peak distribution
gbc.fill = GridBagConstraints.NONE;
gbc.anchor = GridBagConstraints.CENTER;
theoreticalPeaksPanel = new JPanel();
theoreticalPeaksPanel.setBorder(BorderFactory.createTitledBorder("Theoretical Peaks"));
theoreticalPeaksPanel.setLayout(new GridBagLayout());
theoreticalPeaksPanel.setMinimumSize(new Dimension(leftPanelWidth-10, theoreticalPeaksPanelHeight));
theoreticalPeaksPanel.setMaximumSize(new Dimension(1200, theoreticalPeaksPanelHeight));
showTheoreticalPeaks();
//Add everything to the left panel
gbc.insets = new Insets(0,5,0,5);
gbc.fill = GridBagConstraints.BOTH;
gbc.anchor = GridBagConstraints.PAGE_START;
leftPanel.addComponentListener(new LeftPanelResizeListener());
gbc.weighty = 10;
gbc.fill = GridBagConstraints.VERTICAL;
leftPanel.add(propertiesScrollPane, gbc);
gbc.fill = GridBagConstraints.NONE;
gbc.weighty = 1;
gbc.anchor = GridBagConstraints.PAGE_END;
gbc.fill = GridBagConstraints.HORIZONTAL;
leftPanel.add(assessmentPanel, gbc);
leftPanel.add(theoreticalPeaksPanel, gbc);
gbc.fill = GridBagConstraints.HORIZONTAL;
leftPanel.add(curationPanel, gbc);
leftPanel.add(navigationPanel, gbc);
gbc.fill = GridBagConstraints.BOTH;
gbc.weighty = 1;
gbc.anchor = GridBagConstraints.PAGE_START;
//Chart display
multiChartDisplay = new TabbedMultiChartDisplayPanel();
multiChartDisplay.setResizeDelayMS(0);
rightPanel.addComponentListener(new RightPanelResizeListener());
rightPanel.add(multiChartDisplay, gbc);
//status message
messageLabel.setBackground(Color.WHITE);
messageLabel.setFont(Font.decode("verdana plain 12"));
messageLabel.setText(" ");
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
//paranoia. Sometimes it seems Qurate doesn't exit when you close every window
addWindowStateListener(new WindowStateListener()
{
public void windowStateChanged(WindowEvent e)
{
if (e.getNewState() == WindowEvent.WINDOW_CLOSED)
{
dispose();
System.exit(0);
}
}
});
}
//button actions
public void buttonBack_actionPerformed(ActionEvent event)
{
if (displayedEventIndex > 0)
{
displayedEventIndex--;
displayCurrentQuantEvent(true);
}
}
public void buttonForward_actionPerformed(ActionEvent event)
{
if (displayedEventIndex < quantEvents.size()-1)
{
displayedEventIndex++;
displayCurrentQuantEvent(true);
}
}
public void buttonShowEventSummary_actionPerformed(ActionEvent event)
{
eventSummaryFrame.setVisible(true);
}
public void buttonCuration_actionPerformed(ActionEvent event)
{
QuantEvent quantEvent = quantEvents.get(displayedEventIndex);
ButtonModel selectedButtonModel = quantCurationButtonGroup.getSelection();
if (selectedButtonModel == goodRadioButtonModel)
quantEvent.setQuantCurationStatus(QuantEvent.CURATION_STATUS_GOOD);
else if (selectedButtonModel == badRadioButtonModel)
quantEvent.setQuantCurationStatus(QuantEvent.CURATION_STATUS_BAD);
else if (selectedButtonModel == onePeakRadioButtonModel)
quantEvent.setQuantCurationStatus(QuantEvent.CURATION_STATUS_RATIO_ONEPEAK);
else
quantEvent.setQuantCurationStatus(QuantEvent.CURATION_STATUS_UNKNOWN);
}
public void buttonIDCuration_actionPerformed(ActionEvent event)
{
QuantEvent quantEvent = quantEvents.get(displayedEventIndex);
ButtonModel selectedButtonModel = idCurationButtonGroup.getSelection();
if (selectedButtonModel == idGoodRadioButtonModel)
quantEvent.setIdCurationStatus(QuantEvent.CURATION_STATUS_GOOD);
else if (selectedButtonModel == idBadRadioButtonModel)
{
//setting ID to bad is special -- also sets quantitation to bad
quantEvent.setIdCurationStatus(QuantEvent.CURATION_STATUS_BAD);
quantEvent.setQuantCurationStatus(QuantEvent.CURATION_STATUS_BAD);
quantCurationButtonGroup.setSelected(badRadioButtonModel, true);
}
else
quantEvent.setIdCurationStatus(QuantEvent.CURATION_STATUS_UNKNOWN);
}
/**
* Build summary charts for all displayed events.
*
* At the moment, there's just one chart, a scatterplot of algorithm vs singlepeak ratio (log) that's clickable
* to navigate between events
*/
protected void buildSummaryCharts()
{
if (summaryChartsFrame != null)
summaryChartsFrame.dispose();
int chartWidth = 800;
int chartHeight = 800;
summaryChartsPanel = new TabbedMultiChartDisplayPanel();
summaryChartsFrame = new JFrame();
summaryChartsFrame.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
summaryChartsFrame.setTitle("Summary Charts");
try
{
//Data values for the two series (good and bad) on the chart
List<Float> algLogRatiosGood = new ArrayList<Float>();
List<Float> singlePeakLogRatiosGood = new ArrayList<Float>();
List<Float> algLogRatiosBad = new ArrayList<Float>();
List<Float> singlePeakLogRatiosBad = new ArrayList<Float>();
double initialDomainCrosshairValue = 0;
double initialRangeCrosshairValue = 0;
for (int i=0; i< quantEvents.size(); i++)
{
QuantEvent quantEvent = quantEvents.get(i);
List<Float> algList = algLogRatiosGood;
List<Float> singleList = singlePeakLogRatiosGood;
if (quantEvent.getAlgorithmicAssessment().getStatus() != QuantEventAssessor.FLAG_REASON_OK)
{
algList = algLogRatiosBad;
singleList = singlePeakLogRatiosBad;
}
float domainVal = (float)Math.log(Math.max(0.0001, quantEvent.getRatio()));
algList.add(domainVal);
float rangeVal = (float)Math.log(Math.max(0.0001,quantEvent.getRatioOnePeak()));
singleList.add(rangeVal);
if (i == displayedEventIndex)
{
initialDomainCrosshairValue = domainVal;
initialRangeCrosshairValue = rangeVal;
}
}
//This scatterplot will show algorithm ratio vs. singlepeak ratio, colored differently by good/bad assessment,
//and will let the user pick datapoints and go to those events, via crosshairs
PanelWithScatterPlot pwsp = new PanelWithScatterPlot(true);
pwsp.setName("Algorithm vs. Single Peak Log Ratio");
boolean hasGood = false;
if (!algLogRatiosGood.isEmpty())
{
pwsp.addData(algLogRatiosGood, singlePeakLogRatiosGood, "Assessed Good");
pwsp.setSeriesColor(0, Color.GREEN);
hasGood = true;
}
if (!algLogRatiosBad.isEmpty())
{
pwsp.addData(algLogRatiosBad, singlePeakLogRatiosBad, "Assessed Bad");
pwsp.setSeriesColor(hasGood ? 1 : 0, Color.RED);
}
pwsp.setAxisLabels("Algorithm Log Ratio","Single Peak Log Ratio");
pwsp.setSize(chartWidth, chartHeight);
pwsp.setMinimumSize(new Dimension(chartWidth, chartHeight));
pwsp.setPreferredSize(new Dimension(chartWidth, chartHeight));
//change the displayed event when the user selects a new one
pwsp.addCrosshairsAndListener(new CrosshairChangeListener()
{
public void crosshairValueChanged(ChartProgressEvent event)
{
for (int i=0; i<quantEvents.size(); i++)
{
QuantEvent quantEvent = quantEvents.get(i);
//it would be less work to store these values, but this isn't done all that often
double domainDiff = (float)Math.log(Math.max(0.0001, quantEvent.getRatio())) - (float) domainValue;
double rangeDiff = (float)Math.log(Math.max(0.0001,quantEvent.getRatioOnePeak())) -
(float) rangeValue;
if ( Math.abs(domainDiff) < 0.001 && Math.abs(rangeDiff) < 0.001)
{
if (displayedEventIndex != i)
{
displayedEventIndex = i;
displayCurrentQuantEvent(false);
}
break;
}
}
}
}
, initialDomainCrosshairValue, initialRangeCrosshairValue);
summaryChartsFrame.setSize(new Dimension(pwsp.getWidth() + 10, pwsp.getHeight() + 50));
summaryChartsFrame.add(pwsp);
}
catch (NullPointerException e)
{
infoMessage("Warning: failed to generate heuristic summary chart.");
}
}
/**
* Update lots of UI components after a change of quantitation event
*/
protected void updateUIAfterChange(boolean shouldUpdateTable)
{
QuantEvent quantEvent = quantEvents.get(displayedEventIndex);
if (displayedEventIndex > 0)
backButton.setEnabled(true);
else
backButton.setEnabled(false);
if (displayedEventIndex < quantEvents.size()-1)
forwardButton.setEnabled(true);
else
forwardButton.setEnabled(false);
showEventSummaryButton.setEnabled(true);
onePeakRatioRadioButton.setText("" + Rounder.round(quantEvent.getRatioOnePeak(), 2));
Enumeration<AbstractButton> quantButtons = quantCurationButtonGroup.getElements();
while (quantButtons.hasMoreElements())
quantButtons.nextElement().setEnabled(true);
Enumeration<AbstractButton> idButtons = idCurationButtonGroup.getElements();
while (idButtons.hasMoreElements())
idButtons.nextElement().setEnabled(true);
navigationPanel.setBorder(BorderFactory.createTitledBorder(
"Event " + (displayedEventIndex+1) + " / " + quantEvents.size()));
ButtonModel buttonModelToSelect = null;
switch (quantEvent.getQuantCurationStatus())
{
case QuantEvent.CURATION_STATUS_UNKNOWN:
buttonModelToSelect = unknownRadioButtonModel;
break;
case QuantEvent.CURATION_STATUS_GOOD:
buttonModelToSelect = goodRadioButtonModel;
break;
case QuantEvent.CURATION_STATUS_BAD:
buttonModelToSelect = badRadioButtonModel;
break;
case QuantEvent.CURATION_STATUS_RATIO_ONEPEAK:
buttonModelToSelect = onePeakRadioButtonModel;
break;
}
quantCurationButtonGroup.setSelected(buttonModelToSelect, true);
switch (quantEvent.getIdCurationStatus())
{
case QuantEvent.CURATION_STATUS_UNKNOWN:
buttonModelToSelect = idUnknownRadioButtonModel;
break;
case QuantEvent.CURATION_STATUS_GOOD:
buttonModelToSelect = idGoodRadioButtonModel;
break;
case QuantEvent.CURATION_STATUS_BAD:
buttonModelToSelect = idBadRadioButtonModel;
break;
}
idCurationButtonGroup.setSelected(buttonModelToSelect, true);
multiChartDisplay.setPreferredSize(new Dimension(rightPanel.getWidth(), rightPanel.getHeight()));
multiChartDisplay.updateUI();
commentTextField.setText(quantEvent.getComment() != null ? quantEvent.getComment() : "");
commentTextField.setToolTipText(quantEvent.getComment() != null ? quantEvent.getComment() :
"Comment on this event");
//dhmay danger of infinite loop here
if (shouldUpdateTable)
eventSummaryTable.getSelectionModel().setSelectionInterval(displayedEventIndex, displayedEventIndex);
QuantEventAssessor.QuantEventAssessment assessment = quantEvent.getAlgorithmicAssessment();
if (assessment == null)
{
assessmentTypeTextField.setText("");
assessmentDescTextField.setText("");
assessmentTypeTextField.setBackground(Color.LIGHT_GRAY);
}
else
{
assessmentTypeTextField.setText(QuantEventAssessor.flagReasonDescriptions[assessment.getStatus()]);
assessmentDescTextField.setText(assessment.getExplanation());
Color bgColor = assessmentPanel.getBackground();
switch (assessment.getStatus())
{
case QuantEventAssessor.FLAG_REASON_UNEVALUATED:
bgColor = Color.YELLOW;
break;
case QuantEventAssessor.FLAG_REASON_OK:
bgColor = Color.GREEN;
break;
default:
bgColor = Color.RED;
break;
}
assessmentTypeTextField.setBackground(bgColor);
}
assessmentDescTextField.setToolTipText(assessmentDescTextField.getText());
showTheoreticalPeaks();
}
/**
* Calculate and show theoretical isotopic distribution peaks, with light encroaching on heavy
* if necessary
*/
protected void showTheoreticalPeaks()
{
int horizSlop = 30;
int vertSlop = 35;
if (System.getProperty("os.name").toLowerCase().contains("mac"))
{
horizSlop=40;
vertSlop=45;
}
int chartWidth = Math.max(200, leftPanelWidth-horizSlop);
int chartHeight = Math.max(theoreticalPeaksPanel.getHeight(), theoreticalPeaksPanelHeight) - vertSlop;
if (theoreticalPeaksChart != null && theoreticalPeaksChart.getComponentCount() > 0)
theoreticalPeaksPanel.remove(0);
QuantEvent quantEvent = null;
if (quantEvents != null)
{
quantEvent = quantEvents.get(displayedEventIndex);
theoreticalPeaksChart =
QuantitationVisualizer.buildTheoreticalPeakChart(quantEvent, chartWidth, chartHeight);
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.anchor = GridBagConstraints.PAGE_START;
gbc.gridwidth = GridBagConstraints.REMAINDER;
theoreticalPeaksPanel.add(theoreticalPeaksChart, gbc);
float lightNeutralMass = (quantEvent.getLightMz() - Spectrum.HYDROGEN_ION_MASS) *
quantEvent.getCharge();
float heavyNeutralMass = (quantEvent.getHeavyMz() - Spectrum.HYDROGEN_ION_MASS) *
quantEvent.getCharge();
theoreticalPeaksPanel.setToolTipText("LightMass=" + lightNeutralMass + ", HeavyMass=" + heavyNeutralMass +
", Ratio=" + quantEvent.getRatio());
theoreticalPeaksChart.updateUI();
}
}
/**
* Take care of the charts and the properties panel
*
* @param shouldUpdateTable Should we update the events table? Need this to avoid infinite loop
*/
protected void displayCurrentQuantEvent(boolean shouldUpdateTable)
{
QuantEvent quantEvent = quantEvents.get(displayedEventIndex);
List<PanelWithChart> multiChartPanels = multiChartDisplay.getChartPanels();
//first-time initialization
if (multiChartPanels == null || multiChartPanels.isEmpty())
{
multiChartDisplay.addChartPanel(new PanelWithBlindImageChart("Intensity Sum"));
multiChartDisplay.addChartPanel(new PanelWithBlindImageChart("Spectrum"));
multiChartDisplay.addChartPanel(new PanelWithBlindImageChart("Scans"));
if (quantEvent.getFile3D() != null)
multiChartDisplay.addChartPanel(new PanelWithBlindImageChart("3D"));
}
try
{
PanelWithBlindImageChart intensitySumChart = (PanelWithBlindImageChart) multiChartPanels.get(0);
PanelWithBlindImageChart spectrumChart = (PanelWithBlindImageChart) multiChartPanels.get(1);
PanelWithBlindImageChart scansChart = (PanelWithBlindImageChart) multiChartPanels.get(2);
spectrumChart.setImage(ImageIO.read(quantEvent.getSpectrumFile()));
scansChart.setImage(ImageIO.read(quantEvent.getScansFile()));
if (quantEvent.getIntensitySumFile() != null)
intensitySumChart.setImage(ImageIO.read(quantEvent.getIntensitySumFile()));
if (quantEvent.getFile3D() != null && multiChartPanels.size() > 3)
{
PanelWithBlindImageChart chart3D = (PanelWithBlindImageChart) multiChartPanels.get(3);
if (quantEvent.getFile3D().exists())
chart3D.setImage(ImageIO.read(quantEvent.getFile3D()));
else
{
_log.debug("Warning: no 3D chart for this event");
}
}
}
catch (IOException e)
{
ApplicationContext.errorMessage("Failure displaying charts",e);
}
propertiesTable.displayQuantEvent(quantEvent);
updateUIAfterChange(shouldUpdateTable);
}
public int getDisplayedEventIndex()
{
return displayedEventIndex;
}
public void setDisplayedEventIndex(int displayedEventIndex)
{
this.displayedEventIndex = displayedEventIndex;
}
/**
* Manually manage the size of the multi-chart panel
*/
protected class RightPanelResizeListener implements ComponentListener
{
public void componentResized(ComponentEvent event)
{
multiChartDisplay.setPreferredSize(new Dimension(rightPanel.getWidth(), rightPanel.getHeight()-5));
}
public void componentMoved(ComponentEvent event) {}
public void componentShown(ComponentEvent event) {}
public void componentHidden(ComponentEvent event) {}
}
/**
* Manually manage the size of the properties table
*/
protected class LeftPanelResizeListener implements ComponentListener
{
public void componentResized(ComponentEvent event)
{
propertiesScrollPane.setPreferredSize(new Dimension(leftPanel.getWidth()-15,
propertiesScrollPane.getHeight()));
}
public void componentMoved(ComponentEvent event) {}
public void componentShown(ComponentEvent event) {}
public void componentHidden(ComponentEvent event) {}
}
/**
* Display a dialog box with info message
* @param message
*/
public static void infoMessage(String message)
{
JOptionPane.showMessageDialog(ApplicationContext.getFrame(), message, "Information",
JOptionPane.INFORMATION_MESSAGE);
}
/**
* Display a dialog box with info message and stack trace
* @param message
* @param t
*/
protected void errorMessage(String message, Throwable t)
{
if (null != t)
{
message = message + "\n" + t.getMessage() + "\n";
StringWriter sw = new StringWriter();
PrintWriter w = new PrintWriter(sw);
t.printStackTrace(w);
w.flush();
message += "\n";
message += sw.toString();
}
ApplicationContext.errorMessage(message, t);
JOptionPane.showMessageDialog(ApplicationContext.getFrame(), message, "Information",
JOptionPane.INFORMATION_MESSAGE);
}
/**
* Remove all the events the user has designated as 'bad' from the pepXML file they came from
* TODO: report how many events weren't found
* @param quantEvents
* @param pepXmlFile
* @param outFile
* @throws IOException
* @throws XMLStreamException
*/
public static void filterBadEventsFromFile(List<QuantEvent> quantEvents,
File pepXmlFile, File outFile)
throws IOException, XMLStreamException
{
Map<String, List<Integer>> fractionBadQuantScanListMap = new HashMap<String, List<Integer>>();
Map<String, List<Integer>> fractionBadIDScanListMap = new HashMap<String, List<Integer>>();
Map<String, Map<Integer, Float>> fractionScanNewRatioMap = new HashMap<String, Map<Integer, Float>>();
for (QuantEvent quantEvent : quantEvents)
{
String fraction = quantEvent.getFraction();
if (quantEvent.getIdCurationStatus() == QuantEvent.CURATION_STATUS_BAD)
{
List<Integer> thisFractionList =
fractionBadIDScanListMap.get(fraction);
if (thisFractionList == null)
{
thisFractionList = new ArrayList<Integer>();
fractionBadIDScanListMap.put(fraction, thisFractionList);
}
thisFractionList.add(quantEvent.getScan());
for (QuantEvent otherEvent : quantEvent.getOtherEvents())
if (!thisFractionList.contains(otherEvent.getScan()))
thisFractionList.add(otherEvent.getScan());
_log.debug("Stripping ID for " + (quantEvent.getOtherEvents().size() + 1) +
" events for peptide " +
quantEvent.getPeptide() + " from fraction " + fraction);
}
//only if the ID was unknown or good do we check quant -- quant is automatically
//filtered for bad IDs
else if (quantEvent.getQuantCurationStatus() == QuantEvent.CURATION_STATUS_BAD)
{
List<Integer> thisFractionList =
fractionBadQuantScanListMap.get(fraction);
if (thisFractionList == null)
{
thisFractionList = new ArrayList<Integer>();
fractionBadQuantScanListMap.put(fraction, thisFractionList);
}
thisFractionList.add(quantEvent.getScan());
int numOtherEvents = 0;
if (quantEvent.getOtherEvents() != null)
{
for (QuantEvent otherEvent : quantEvent.getOtherEvents())
if (!thisFractionList.contains(otherEvent.getScan()))
thisFractionList.add(otherEvent.getScan());
numOtherEvents = quantEvent.getOtherEvents().size();
}
ApplicationContext.infoMessage("Stripping Quantitation for " + (numOtherEvents + 1) +
" events for peptide " +
quantEvent.getPeptide() + " from fraction " + fraction);
}
//dhmay adding 20090723
else if (quantEvent.getQuantCurationStatus() == QuantEvent.CURATION_STATUS_RATIO_ONEPEAK)
{
Map<Integer, Float> thisFractionMap = fractionScanNewRatioMap.get(fraction);
if (thisFractionMap == null)
{
thisFractionMap = new HashMap<Integer, Float>();
fractionScanNewRatioMap.put(fraction, thisFractionMap);
}
float newRatio = quantEvent.getRatioOnePeak();
thisFractionMap.put(quantEvent.getScan(), newRatio);
for (QuantEvent otherEvent : quantEvent.getOtherEvents())
if (!thisFractionMap.containsKey(otherEvent.getScan()))
thisFractionMap.put(otherEvent.getScan(), newRatio);
ApplicationContext.infoMessage("Changing ratios to " + newRatio + " for " +
(quantEvent.getOtherEvents().size() + 1) +
" events for peptide " +
quantEvent.getPeptide() + " from fraction " + fraction);
}
}
ApplicationContext.infoMessage("OK, decided what to do. Now to strip the IDs from the pepXML file....");
File actualOutFile = outFile;
String dummyOwner = "DUMMY_OWNER_QURATE_STRIP";
boolean sameInputAndOutput = pepXmlFile.getAbsolutePath().equals(outFile.getAbsolutePath());
if (sameInputAndOutput)
actualOutFile = TempFileManager.createTempFile("temp_quratestrip_" + outFile.getName(), dummyOwner);
StripQuantOrIDPepXmlRewriter quantStripper = new StripQuantOrIDPepXmlRewriter(pepXmlFile, actualOutFile,
fractionBadIDScanListMap, fractionBadQuantScanListMap, fractionScanNewRatioMap);
quantStripper.rewrite();
quantStripper.close();
if (sameInputAndOutput)
{
//file-copying. Java 1.6 still doesn't have a reasonable and concise way to do it.
FileChannel inChannel = new
FileInputStream(actualOutFile).getChannel();
FileChannel outChannel = new
FileOutputStream(outFile).getChannel();
try
{
inChannel.transferTo(0, inChannel.size(),
outChannel);
}
catch (IOException e)
{
throw e;
}
finally
{
if (inChannel != null) inChannel.close();
if (outChannel != null) outChannel.close();
}
TempFileManager.deleteTempFiles(dummyOwner);
}
ApplicationContext.infoMessage("Done! Saved stripped file to " + outFile.getAbsolutePath());
}
/**
* Set status message. Separate thread necessary or UI hangs
* @param message
*/
public void setMessage(String message)
{
if (EventQueue.isDispatchThread())
{
if (null == message || 0 == message.length())
message = " ";
messageLabel.setText(message);
}
else
{
final String msg = message;
EventQueue.invokeLater(new Runnable()
{
public void run()
{
setMessage(msg);
}
});
}
}
/**
* pepXML rewriter that can strip out quantitation events or entire spectrum_query tags.
* dhmay 20090723: adding ability to alter ratios of events
*/
static class StripQuantOrIDPepXmlRewriter extends SimpleXMLEventRewriter
{
//Track the bad stuff by fraction name and scan number
Map<String, List<Integer>> fractionBadQuantScansMap;
Map<String, List<Integer>> fractionBadIDScansMap;
Map<String, Map<Integer, Float>> fractionScanNewRatioMap;
protected boolean insideSkippedQuantEvent = false;
protected boolean insideSkippedSpectrumQuery = false;
protected boolean insideScanWithSkippedEvent = false;
protected boolean insideScanWithRatioChange = false;
List<Integer> currentFractionBadQuantScans;
List<Integer> currentFractionBadIDScans;
Map<Integer, Float> currentFractionScanNewRatioMap;
float currentScanNewRatio;
public StripQuantOrIDPepXmlRewriter(File inputFile, File outputFile,
Map<String, List<Integer>> fractionBadIDScansMap,
Map<String, List<Integer>> fractionBadQuantScansMap,
Map<String, Map<Integer, Float>> fractionScanNewRatioMap)
{
super(inputFile.getAbsolutePath(), outputFile.getAbsolutePath());
this.fractionBadQuantScansMap = fractionBadQuantScansMap;
this.fractionBadIDScansMap = fractionBadIDScansMap;
this.fractionScanNewRatioMap = fractionScanNewRatioMap;
}
public void add(XMLEvent event)
throws XMLStreamException
{
if (!insideSkippedQuantEvent && !insideSkippedSpectrumQuery)
{
super.add(event);
}
}
/**
* special handling for keeping track of fraction and dealing with the skipped stuff
* @param event
* @throws XMLStreamException
*/
public void handleStartElement(StartElement event)
throws XMLStreamException
{
QName qname = event.getName();
String elementName = qname.getLocalPart();
XMLEvent eventToAdd = event;
if ("msms_run_summary".equals(elementName))
{
Attribute baseNameAttr = event.getAttributeByName(new QName("base_name"));
String baseName = baseNameAttr.getValue();
currentFractionBadQuantScans = fractionBadQuantScansMap.get(baseName);
currentFractionBadIDScans = fractionBadIDScansMap.get(baseName);
currentFractionScanNewRatioMap = fractionScanNewRatioMap.get(baseName);
}
else if ("spectrum_query".equals(elementName))
{
int scan = Integer.parseInt(event.getAttributeByName(new QName("start_scan")).getValue());
if (currentFractionBadIDScans != null && currentFractionBadIDScans.contains(scan))
{
insideSkippedSpectrumQuery = true;
_log.debug("Skipping ID for scan " + scan);
}
else if (currentFractionBadQuantScans != null && currentFractionBadQuantScans.contains(scan))
{
insideScanWithSkippedEvent = true;
_log.debug("Skipping quantitation for scan " + scan);
}
else if (currentFractionScanNewRatioMap != null && currentFractionScanNewRatioMap.containsKey(scan))
{
insideScanWithRatioChange = true;
currentScanNewRatio = currentFractionScanNewRatioMap.get(scan);
}
}
else if ("analysis_result".equals(elementName))
{
if (insideScanWithSkippedEvent)
{
String analysisType = event.getAttributeByName(new QName("analysis")).getValue();
if ("q3".equals(analysisType) || "xpress".equals(analysisType))
insideSkippedQuantEvent = true;
}
}
//Adjust Q3 or XPress ratios. Note: doesn't affect Q3's Q2 ratio or KL scores
else if ("xpressratio_result".equals(elementName) || "q3ratio_result".equals(elementName))
{
if (insideScanWithRatioChange)
{
float lightArea = (float) Rounder.round(Float.parseFloat(event.getAttributeByName(new QName("light_area")).getValue()), 4);
//guard against division by 0. If we do this, light area 100 is arbitrary
boolean lightWas0 = false;
if (lightArea == 0)
{
lightWas0 = true;
lightArea = 100f;
}
float newHeavyArea = (float) Rounder.round(lightArea / currentScanNewRatio, 4);
_log.debug("Changing ratio to " + currentScanNewRatio + ". Light=" + lightArea + ", heavy=" + newHeavyArea + ". Light was 0? " + lightWas0);
SimpleStartElement newEvent = new SimpleStartElement(qname.getLocalPart());
Iterator<Attribute> attIter = event.getAttributes();
while (attIter.hasNext())
{
Attribute oldAttr = attIter.next();
String attrName = oldAttr.getName().getLocalPart();
if (attrName.equals("decimal_ratio"))
{
newEvent.addAttribute("decimal_ratio", currentScanNewRatio);
}
else if (attrName.equals("heavy_area"))
{
newEvent.addAttribute("heavy_area", newHeavyArea);
}
else if (attrName.equals("light_area"))
{
newEvent.addAttribute("light_area", lightArea);
}
else if (attrName.equals("heavy2light_ratio"))
{
newEvent.addAttribute("heavy2light_ratio", 1.0f / currentScanNewRatio);
}
else
newEvent.addAttribute(attrName, oldAttr.getValue());
}
eventToAdd = newEvent.getEvent();
}
}
add(eventToAdd);
}
/**
* Pop out of skipping mode
* @param event
* @throws XMLStreamException
*/
public void handleEndElement(EndElement event)
throws XMLStreamException
{
QName qname = event.getName();
add(event);
if (insideSkippedQuantEvent && "analysis_result".equals(qname.getLocalPart()))
insideSkippedQuantEvent = false;
else if ("spectrum_query".equals(qname.getLocalPart()))
{
insideScanWithSkippedEvent = false;
insideSkippedSpectrumQuery = false;
insideScanWithRatioChange = false;
}
}
}
/**
* Display chart dialog
*/
protected class SummaryChartsAction extends AbstractAction
{
public void actionPerformed(ActionEvent event)
{
if (summaryChartsFrame != null)
summaryChartsFrame.setVisible(true);
}
}
/**
* Display help from static help file
*/
public static class HelpAction extends AbstractAction
{
public void actionPerformed(ActionEvent event)
{
try
{
JDialog dialog = HtmlViewerPanel.showResourceInDialog(
"org/fhcrc/cpl/viewer/quant/gui/qurate_help.html", "Qurate Help");
}
catch (Exception e)
{
ApplicationContext.errorMessage("Error displaying help", e);
}
}
}
/**
* "Create Charts" in the File menu. Create charts for running this on, by invoking the
* 'quantchart' command programmatically, letting user specify args
*/
protected class CreateChartsAction extends AbstractAction
{
public void actionPerformed(ActionEvent event)
{
Thread createChartsThread = new Thread(new Runnable()
{
public void run()
{
PeptideQuantVisualizationCLM createChartsModule = new PeptideQuantVisualizationCLM();
ViewerInteractiveModuleFrame interactFrame =
new ViewerInteractiveModuleFrame(createChartsModule, true, null);
interactFrame.setUserManualGenerator(new ViewerUserManualGenerator());
boolean shouldExecute = interactFrame.collectArguments();
if (shouldExecute)
{
try
{
setMessage("Building charts. This could take a while. Details on command line.");
contentPanel.updateUI();
createChartsModule.execute();
infoMessage("Saved charts. Opening summary file " +
createChartsModule.getOutTsvFile().getAbsolutePath());
contentPanel.updateUI();
try
{
displayQuantFile(createChartsModule.getOutTsvFile());
}
catch (IOException e)
{
ApplicationContext.errorMessage("Failed to open quantitation file " +
quantFile.getAbsolutePath(),e);
} }
catch (Exception e)
{
String message = "Error creating charts: " + e.getMessage();
errorMessage(message,e);
}
}
}
});
createChartsThread.start();
}
}
/**
* Open a tsv file
*/
protected class OpenFileAction extends AbstractAction
{
protected Component parentComponent;
public OpenFileAction(Component parentComponent)
{
this.parentComponent = parentComponent;
}
public void actionPerformed(ActionEvent event)
{
WorkbenchFileChooser wfc = new WorkbenchFileChooser();
if (quantFile != null)
wfc.setSelectedFile(quantFile);
wfc.setDialogTitle("Open Quantitation Event File");
int chooserStatus = wfc.showOpenDialog(parentComponent);
//if user didn't hit OK, ignore
if (chooserStatus != JFileChooser.APPROVE_OPTION)
return;
quantFile = wfc.getSelectedFile();
try
{
displayQuantFile(quantFile);
}
catch (IOException e)
{
errorMessage("Failed to open quantitation file " +
quantFile.getAbsolutePath(),e);
}
}
}
/**
* Save changes to a file
*/
protected class SaveAction extends AbstractAction
{
protected Component parentComponent;
public SaveAction(Component parentComponent)
{
this.parentComponent = parentComponent;
}
public void actionPerformed(ActionEvent event)
{
WorkbenchFileChooser wfc = new WorkbenchFileChooser();
if (quantFile != null)
wfc.setSelectedFile(quantFile);
wfc.setDialogTitle("Output File");
int chooserStatus = wfc.showOpenDialog(parentComponent);
//if user didn't hit OK, ignore
if (chooserStatus != JFileChooser.APPROVE_OPTION)
return;
quantFile = wfc.getSelectedFile();
try
{
QuantEvent.saveQuantEventsToTSV(quantEvents, quantFile, true, true);
setMessage("Saved changes to file " + quantFile.getAbsolutePath());
}
catch (IOException e)
{
errorMessage("ERROR: failed to save file " + quantFile.getAbsolutePath(), e);
}
}
}
public void showProteinQuantSummaryFrame(List<ProtXmlReader.Protein> proteins)
{
showProteinQuantSummaryFrame(proteins, null);
}
public void showProteinQuantSummaryFrame(List<ProtXmlReader.Protein> proteins,
Map<String, List<String>> proteinGenesMap)
{
List<QuantEvent> selectedQuantEvents = null;
if (quantSummaryFrame != null)
quantSummaryFrame.dispose();
// File outFile = settingsCLM.outFile;
// if (outFile == null)
// {
// //We may not actually want to keep the output file, in which case we need a temp file
// outFile = TempFileManager.createTempFile("qurate_ProteinSelectedActionListener.tsv",
// "DUMMY_ProteinSelectedActionListener_CALLER");
// }
try
{
quantSummaryFrame = new ProteinQuantSummaryFrame(settingsCLM.mzXmlDir);
quantSummaryFrame.setExistingQuantEvents(quantEvents);
quantSummaryFrame.setProteinGeneMap(proteinGenesMap);
setMessage("Locating quantitation events for " + proteins.size() + " proteins...");
_log.debug("About to displayData");
quantSummaryFrame.displayData(settingsCLM.pepXmlFile, settingsCLM.protXmlFile, proteins);
System.err.println("ran displayData");
setMessage("");
quantSummaryFrame.setModal(true);
quantSummaryFrame.setVisible(true);
selectedQuantEvents = quantSummaryFrame.getSelectedQuantEvents();
quantSummaryFrame.dispose();
}
catch (IllegalArgumentException e)
{
infoMessage(e.getMessage());
return;
}
finally
{
if (quantSummaryFrame != null)
quantSummaryFrame.dispose();
}
//will have no effect if temp file not created
TempFileManager.deleteTempFiles("DUMMY_ProteinSelectedActionListener_CALLER");
if (selectedQuantEvents == null ||
selectedQuantEvents.isEmpty())
return;
setMessage(selectedQuantEvents.size() + " events selected for charts");
if (quantEvents == null)
quantEvents = new ArrayList<QuantEvent>();
quantEvents.addAll(selectedQuantEvents);
eventSummaryTable.setEvents(selectedQuantEvents);
displayedEventIndex = quantEvents.size() - selectedQuantEvents.size();
buildSummaryCharts();
displayCurrentQuantEvent(true);
}
/**
* Clean up the windows that might be open
*/
public void dispose()
{
_log.debug("QuantitationReviewer.dispose()");
if (quantSummaryFrame != null)
quantSummaryFrame.dispose();
if (proteinSummarySelector != null)
proteinSummarySelector.dispose();
if (eventSummaryFrame != null)
eventSummaryFrame.dispose();
if (summaryChartsFrame != null)
summaryChartsFrame.dispose();
super.dispose();
}
public File getQuantFile()
{
return quantFile;
}
public void setQuantFile(File quantFile)
{
this.quantFile = quantFile;
}
/**
* Displays the splash screen. Side effect: sets the splashFrame variable, so it can be disposed later
*/
protected void showSplashScreen()
{
splashFrame = new SplashFrame(splashImageURL);
splashFrame.setVisible(true);
}
//supporting inner classes
protected class ProteinSelectedActionListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if (proteinSummarySelector.getSelectedProteins() != null &&
!proteinSummarySelector.getSelectedProteins().isEmpty())
{
showProteinQuantSummaryFrame(proteinSummarySelector.getSelectedProteins());
}
}
}
protected class ProteinSummaryAction extends AbstractAction
{
List<QuantEvent> selectedQuantEvents = null;
protected Component parentComponent;
public ProteinSummaryAction(Component parentComponent)
{
this.parentComponent = parentComponent;
}
public void actionPerformed(ActionEvent event)
{
ViewerInteractiveModuleFrame interactFrame =
new ViewerInteractiveModuleFrame(settingsCLM, true, null);
interactFrame.setModal(true);
interactFrame.setTitle("Settings");
interactFrame.setUserManualGenerator(new ViewerUserManualGenerator());
boolean hasRunSuccessfully = interactFrame.collectArguments();
interactFrame.dispose();
if (!hasRunSuccessfully) return;
if (settingsCLM.proteins != null)
{
showProteinQuantSummaryFrame(settingsCLM.proteins, settingsCLM.getProteinGeneListMap());
}
else
{
if (proteinSummarySelector == null || !proteinSummarySelector.isVisible())
try
{
proteinSummarySelector = new ProteinSummarySelectorFrame();
proteinSummarySelector.setMinProteinProphet(settingsCLM.minProteinProphet);
proteinSummarySelector.setMinHighRatio(settingsCLM.minHighRatio);
proteinSummarySelector.setMaxLowRatio(settingsCLM.maxLowRatio);
proteinSummarySelector.setProteinGeneMap(settingsCLM.proteinGeneListMap);
proteinSummarySelector.addSelectionListener(new ProteinSelectedActionListener());
proteinSummarySelector.displayProteins(settingsCLM.protXmlFile);
proteinSummarySelector.setVisible(true);
}
catch (Exception e)
{
errorMessage("Error opening ProtXML file " + settingsCLM.protXmlFile.getAbsolutePath(),e);
}
}
}
}
/**
* Action to quit
*/
protected class ExitAction extends AbstractAction
{
public void actionPerformed(ActionEvent event)
{
setVisible(false);
dispose();
}
}
/**
* Action to remove bad events and IDs from the file they came from. Choose source and output pepXML
* file
*/
protected class FilterPepXMLAction extends AbstractAction
{
protected Component parentComponent;
public FilterPepXMLAction(Component parentComponent)
{
this.parentComponent = parentComponent;
}
public void actionPerformed(ActionEvent event)
{
if (quantEvents == null || quantEvents.isEmpty())
{
infoMessage("No events selected for filtering.");
return;
}
boolean foundBad = false;
for (QuantEvent quantEvent : quantEvents)
{
int idStatus = quantEvent.getIdCurationStatus();
int quantStatus = quantEvent.getQuantCurationStatus();
if (idStatus == QuantEvent.CURATION_STATUS_BAD ||
(quantStatus != QuantEvent.CURATION_STATUS_GOOD &&
quantStatus != QuantEvent.CURATION_STATUS_UNKNOWN))
{
foundBad = true;
break;
}
}
if (!foundBad)
{
infoMessage("No selected events have 'Bad' or 'Single-Peak Ratio' status.");
return;
}
WorkbenchFileChooser wfc = new WorkbenchFileChooser();
wfc.setDialogTitle("Choose PepXML File to Filter");
int chooserStatus = wfc.showOpenDialog(parentComponent);
//if user didn't hit OK, ignore
if (chooserStatus != JFileChooser.APPROVE_OPTION)
return;
final File file = wfc.getSelectedFile();
if (null == file)
return;
WorkbenchFileChooser wfc2 = new WorkbenchFileChooser();
wfc2.setSelectedFile(file);
wfc2.setDialogTitle("Choose Output File");
chooserStatus = wfc2.showOpenDialog(parentComponent);
//if user didn't hit OK, ignore
if (chooserStatus != JFileChooser.APPROVE_OPTION)
return;
final File outFile = wfc2.getSelectedFile();
if (null == outFile)
return;
try
{
setMessage("Filtering bad events from file...");
//do this in a separate thread so the UI doesn't freeze
Thread filterThread = new Thread(new Runnable()
{
public void run()
{
try
{
filterBadEventsFromFile(quantEvents, file, outFile);
setMessage("Filtered events saved to file " + outFile.getAbsolutePath());
}
catch (Exception e)
{
errorMessage("Failed to filter file " + file.getAbsolutePath(),e);
}
}
});
filterThread.start();
}
catch (Exception e)
{
errorMessage("Error filtering bad events from file " + file.getAbsolutePath() + " into file " +
outFile.getAbsolutePath(), e);
}
}
}
/**
* display the properties for the selected event, if only one's selected
*/
public class EventSummaryTableListSelectionHandler implements ListSelectionListener
{
protected int oldIndex = -1;
public void valueChanged(ListSelectionEvent e)
{
if (!e.getValueIsAdjusting())
{
if (eventSummaryTable.getSelectedIndex() >= 0 && oldIndex != eventSummaryTable.getSelectedIndex())
{
displayedEventIndex = eventSummaryTable.getSelectedIndex();
displayCurrentQuantEvent(false);
oldIndex = eventSummaryTable.getSelectedIndex();
}
}
}
}
}