/*
* Copyright (c) 2009 The Jackson Laboratory
*
* This software was developed by Gary Churchill's Lab at The Jackson
* Laboratory (see http://research.jax.org/faculty/churchill).
*
* This 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.
*
* This software 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 this software. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jax.qtl.scan.gui;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.jax.qtl.cross.Cross;
import org.jax.qtl.scan.PhenotypeDistribution;
import org.jax.qtl.scan.ScanCommandBuilder;
import org.jax.qtl.scan.ScanMethod;
import org.jax.qtl.scan.ScanType;
import org.jax.qtl.ui.CalcGenoprobDialog;
import org.jax.qtl.ui.ImputationDialog;
import org.jax.r.gui.RCommandEditorListener;
import org.jax.util.TextWrapper;
/**
* This panel is responsible for editing the "scan method" and
* "scan parameters" portion of a {@link ScanCommandBuilder}
* @author <A HREF="mailto:keith.sheppard@jax.org">Keith Sheppard</A>
*/
public class ScanMethodAndParametersScanPanel extends ScanCommandEditorPanel
{
/**
* every {@link java.io.Serializable} is supposed to have one of these
*/
private static final long serialVersionUID = -6382665791697788497L;
private final ScanCommandBuilder scanCommand;
private final ConvergenceParametersPanel convergenceParametersPanel;
private final OtherScanParametersPanel otherScanParametersPanel;
private final JDialog parentDialog;
/**
* Constructor
* @param parentDialog
* the parent used for internal dialogs
* @param scanCommand
* the scan command that this panel will edit
*/
public ScanMethodAndParametersScanPanel(
JDialog parentDialog,
ScanCommandBuilder scanCommand)
{
this.scanCommand = scanCommand;
this.parentDialog = parentDialog;
this.convergenceParametersPanel = new ConvergenceParametersPanel(scanCommand);
this.otherScanParametersPanel = new OtherScanParametersPanel(scanCommand);
this.initComponents();
}
/**
* {@inheritDoc}
*/
@Override
public void addRCommandEditorListener(RCommandEditorListener editorListener)
{
super.addRCommandEditorListener(editorListener);
this.convergenceParametersPanel.addRCommandEditorListener(editorListener);
this.otherScanParametersPanel.addRCommandEditorListener(editorListener);
}
/**
* {@inheritDoc}
*/
@Override
public void removeRCommandEditorListener(
RCommandEditorListener editorListener)
{
super.removeRCommandEditorListener(editorListener);
this.convergenceParametersPanel.removeRCommandEditorListener(editorListener);
this.otherScanParametersPanel.removeRCommandEditorListener(editorListener);
}
/**
* Updates the scan method
* @param scanMethod
* the new scan method to use
*/
private void setSelectedScanMethod(ScanMethod scanMethod)
{
if(this.scanCommand.getScanMethod() != scanMethod)
{
this.scanCommand.setScanMethod(scanMethod);
this.refreshConvergencePanel();
this.fireCommandModified();
}
}
/**
* Update the convergence panel based on the scan method.
*/
private void refreshConvergencePanel()
{
ScanMethod scanMethod = this.scanCommand.getScanMethod();
boolean enableConvergenceParameters =
scanMethod == ScanMethod.EM_ALGORITHM ||
scanMethod == ScanMethod.EXTENDED_HALEY_KNOTT_METHOD;
this.convergenceParametersPanel.setEnabled(enableConvergenceParameters);
}
/**
* This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("all")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
scanMethodLabel = new javax.swing.JLabel();
scanMethodComboBox = new javax.swing.JComboBox();
convergencePanelDownCast = this.convergenceParametersPanel;
otherScanParametersDownCast = this.otherScanParametersPanel;
setMinimumSize(new java.awt.Dimension(500, 300));
scanMethodLabel.setText("Scan Method:");
scanMethodComboBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
scanMethodComboBoxActionPerformed(evt);
}
});
convergencePanelDownCast.setBorder(javax.swing.BorderFactory.createTitledBorder("Convergence Parameters (For EM & Extended Haley Knott)"));
otherScanParametersDownCast.setBorder(javax.swing.BorderFactory.createTitledBorder("Other Scan Parameters"));
org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING)
.add(org.jdesktop.layout.GroupLayout.LEADING, convergencePanelDownCast, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 460, Short.MAX_VALUE)
.add(org.jdesktop.layout.GroupLayout.LEADING, otherScanParametersDownCast, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 460, Short.MAX_VALUE)
.add(org.jdesktop.layout.GroupLayout.LEADING, layout.createSequentialGroup()
.add(scanMethodLabel)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(scanMethodComboBox, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(layout.createSequentialGroup()
.add(20, 20, 20)
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
.add(scanMethodLabel)
.add(scanMethodComboBox, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(convergencePanelDownCast, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 117, Short.MAX_VALUE)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(otherScanParametersDownCast, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 125, Short.MAX_VALUE)
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents
private void scanMethodComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_scanMethodComboBoxActionPerformed
Object selectedItem = this.scanMethodComboBox.getSelectedItem();
if(selectedItem instanceof ScanMethod)
{
this.setSelectedScanMethod((ScanMethod)selectedItem);
}
else
{
this.setSelectedScanMethod(null);
}
}//GEN-LAST:event_scanMethodComboBoxActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JPanel convergencePanelDownCast;
private javax.swing.JPanel otherScanParametersDownCast;
private javax.swing.JComboBox scanMethodComboBox;
private javax.swing.JLabel scanMethodLabel;
// End of variables declaration//GEN-END:variables
/**
* Validate the data contained in this panel. The user is alerted if
* there is something wrong with the data format
* @return
* true iff the validation succeeds
*/
public boolean validateData()
{
return this.convergenceParametersPanel.validateData() &&
this.validateGenotypeData();
}
/**
* Make sure that "sim.geno" or "calc.genoprob" was called if they
* were supposed to be called.
* @return
* true on success and false on failure
*/
private boolean validateGenotypeData()
{
ScanMethod scanMethod = this.scanCommand.getScanMethod();
if(scanMethod == ScanMethod.EM_ALGORITHM ||
scanMethod == ScanMethod.HALEY_KNOTT_REGRESSION ||
scanMethod == ScanMethod.EXTENDED_HALEY_KNOTT_METHOD)
{
return this.validateGenotypeProbabilitiesCalculated(
scanMethod);
}
else if(scanMethod == ScanMethod.MULTIPLE_IMPUTATION)
{
return this.validateGenotypeProbabilitiesSimulated(
scanMethod);
}
else
{
return true;
}
}
/**
* Validate that genotype probabilities were calculated "calc.genoprob"
* @param scanMethod
* the scan method we're requiring this for
* @return
* true iff valid
*/
private boolean validateGenotypeProbabilitiesSimulated(
ScanMethod scanMethod)
{
final Cross cross = this.scanCommand.getCross();
if(cross.getSimulateGenotypeWasUsed())
{
return true;
}
else
{
final String message =
"The scan method that you have selected \"" +
scanMethod.toString() + "\" requires that " +
"genotype probabilities be simulated before " +
"running the scan command. Would you like to do this now?";
boolean confirmed = this.getConfirmation(
"Genotype Probabilities Not Yet Simulated",
message);
if(confirmed)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
ScanMethodAndParametersScanPanel.this.simulateGenotypeProbabilities(
cross);
}
});
}
return false;
}
}
/**
* Open up the dialog that allows the user to do a sim.geno
* on the given cross
* @param cross
* the cross
*/
private void simulateGenotypeProbabilities(Cross cross)
{
ImputationDialog simulateGenotypeProbabilitiesDialog =
new ImputationDialog(
this.parentDialog,
new Cross[] {cross},
cross);
simulateGenotypeProbabilitiesDialog.setVisible(true);
}
/**
* Ask the user if we can do something
* @param title
* title for the dialog
* @param message
* message in the dialog
* @return
* the users response
*/
private boolean getConfirmation(String title, String message)
{
int response = JOptionPane.showConfirmDialog(
this.parentDialog,
TextWrapper.wrapText(
message,
TextWrapper.DEFAULT_DIALOG_COLUMN_COUNT),
title,
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE);
return response == JOptionPane.OK_OPTION;
}
/**
* Validate that genotype probabilities were simulated "sim.geno"
* @param scanMethod
* the scan method we're requiring this for
* @return
* true iff valid
*/
private boolean validateGenotypeProbabilitiesCalculated(
ScanMethod scanMethod)
{
final Cross cross = this.scanCommand.getCross();
if(cross.getCalculateConditionalProbabilitiesWasUsed())
{
return true;
}
else
{
final String message =
"The scan method that you have selected \"" +
scanMethod.toString() + "\" requires that " +
"genotype probabilities be calculated before " +
"running the scan command. Would you like to do this now?";
boolean confirmed = this.getConfirmation(
"Genotype Probabilities Not Yet Calculated",
message);
if(confirmed)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
ScanMethodAndParametersScanPanel.this.calculateGenotypeProbabilities(
cross);
}
});
}
return false;
}
}
/**
* Calculate genotype probabilities for the given cross
* @param cross
* the cross
*/
protected void calculateGenotypeProbabilities(Cross cross)
{
CalcGenoprobDialog calculateGenotypeProbabilitiesDialog =
new CalcGenoprobDialog(
this.parentDialog,
new Cross[] {cross},
cross);
calculateGenotypeProbabilitiesDialog.setVisible(true);
}
/**
* This function tells this panel that a component other than itself
* has edited the {@link ScanCommandBuilder} and that we need to refresh
* our graphics to reflect those changes.
*/
public void refreshGui()
{
// the "other" parameters are only valid for scan two
this.otherScanParametersPanel.setVisible(
this.scanCommand.getScanType() == ScanType.SCANTWO);
PhenotypeDistribution phenotypeDistribution =
this.scanCommand.getPhenotypeDistribution();
// figure out what scan methods we should allow
ScanMethod currentScanMethod =
this.scanCommand.getScanMethod();
List<ScanMethod> availableMethodsList =
new ArrayList<ScanMethod>();
if(phenotypeDistribution == PhenotypeDistribution.BINARY)
{
// can only be "em" or "mr"
availableMethodsList.add(ScanMethod.EM_ALGORITHM);
availableMethodsList.add(ScanMethod.MARKER_REGRESSION);
}
else if(phenotypeDistribution != PhenotypeDistribution.OTHER)
{
availableMethodsList.addAll(
Arrays.asList(ScanMethod.values()));
}
// remove any methods that aren't supported
List<ScanMethod> supportedMethodsList = Arrays.asList(
this.scanCommand.getScanType().getSupportedScanMethods());
availableMethodsList.retainAll(supportedMethodsList);
DefaultComboBoxModel scanMethodModel =
(DefaultComboBoxModel)this.scanMethodComboBox.getModel();
scanMethodModel.removeAllElements();
if(availableMethodsList.size() == 0)
{
scanMethodModel.addElement("Non Parametric");
this.scanMethodComboBox.setEnabled(false);
}
else
{
boolean currentScanMethodIsAvailable = false;
for(ScanMethod currAvailableMethod: availableMethodsList)
{
scanMethodModel.addElement(currAvailableMethod);
if(currAvailableMethod == currentScanMethod)
{
currentScanMethodIsAvailable = true;
}
}
if(currentScanMethodIsAvailable)
{
scanMethodModel.setSelectedItem(currentScanMethod);
}
this.scanMethodComboBox.setEnabled(true);
}
this.refreshConvergencePanel();
this.fireCommandModified();
}
/**
* {@inheritDoc}
*/
@Override
protected ScanCommandBuilder getScanCommand()
{
return this.scanCommand;
}
}