/* massXpert - the true massist's program. -------------------------------------- Copyright(C) 2006,2007 Filippo Rusconi http://www.massxpert.org/massXpert This file is part of the massXpert project. The massxpert project is the successor to the "GNU polyxmass" project that is an official GNU project package(see www.gnu.org). The massXpert project is not endorsed by the GNU project, although it is released ---in its entirety--- under the GNU General Public License. A huge part of the code in massXpert is actually a C++ rewrite of code in GNU polyxmass. As such massXpert was started at the Centre National de la Recherche Scientifique(FRANCE), that granted me the formal authorization to publish it under this Free Software License. This software is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 3, as published by the Free Software Foundation. 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, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ /////////////////////// Qt includes #include #include /////////////////////// Local includes #include "compositionsDlg.hpp" #include "application.hpp" #include "coordinates.hpp" namespace massXpert { CompositionsDlg::CompositionsDlg(QWidget *parent, Polymer *polymer, CalcOptions *calcOptions, IonizeRule *ionizeRule) : QDialog(parent), mp_polymer(polymer), mp_calcOptions(calcOptions), mp_ionizeRule(ionizeRule) { Q_ASSERT(parent); Q_ASSERT(mp_polymer && mp_calcOptions && mp_ionizeRule); m_ui.setupUi(this); mp_editorWnd = static_cast(parent); updateSelectionData(); setupTreeView(); // The results-exporting menus. //////////////////////////////// QStringList comboBoxItemList; comboBoxItemList << tr("To Clipboard") << tr("To File") << tr("Select File"); m_ui.exportResultsComboBox->addItems(comboBoxItemList); connect(m_ui.exportResultsComboBox, SIGNAL(activated(int)), this, SLOT(exportResults(int))); mpa_resultsString = new QString(); //////////////////////////////////// The results-exporting menus. QSettings settings (static_cast(qApp)->configSettingsFilePath(), QSettings::IniFormat); settings.beginGroup("compositions_dlg"); restoreGeometry(settings.value("geometry").toByteArray()); m_ui.splitter->restoreState(settings.value("splitter").toByteArray()); settings.endGroup(); connect(m_ui.updateSelectionDataPushButton, SIGNAL(clicked()), this, SLOT(updateSelectionData())); connect(m_ui.monomericPushButton, SIGNAL(clicked()), this, SLOT(monomericComposition())); connect(m_ui.elementalPushButton, SIGNAL(clicked()), this, SLOT(elementalComposition())); } CompositionsDlg::~CompositionsDlg() { delete mpa_compositionTreeViewModel; delete mpa_compositionProxyModel; delete mpa_resultsString; freeMonomerList(); } void CompositionsDlg::closeEvent(QCloseEvent *event) { if (event) printf("%s", ""); QSettings settings (static_cast(qApp)->configSettingsFilePath(), QSettings::IniFormat); settings.beginGroup("compositions_dlg"); settings.setValue("geometry", saveGeometry()); settings.setValue("splitter", m_ui.splitter->saveState()); settings.endGroup(); } SequenceEditorWnd * CompositionsDlg::editorWnd() { return mp_editorWnd; } void CompositionsDlg::updateSelectionData() { // The selection might exist as a list of region selections. if (mp_editorWnd->mpa_editorGraphicsView-> selectionIndices(&m_coordinateList)) { m_ui.selectionCoordinatesLineEdit-> setText(m_coordinateList.positionsAsText()); m_ui.selectedSequenceRadioButton->setChecked(true); } else { m_ui.selectionCoordinatesLineEdit->setText(""); m_ui.wholeSequenceRadioButton->setChecked(true); } m_ui.monomericPushButton->setFocus(); } bool CompositionsDlg::fetchValidateInputData() { if (!m_ui.selectedSequenceRadioButton->isChecked()) { m_coordinateList.setCoordinates(Coordinates(0, mp_polymer->size() - 1)); } // Make sure the sequence still has a number of residues // compatible with the current m_coordinateList. this because the // sequence might be edited between opening this dialog window and // actually using it. int polymerSize = mp_polymer->size(); for (int iter = 0; iter < m_coordinateList.size(); ++iter) { // New coordinates instance we are iterating into. Coordinates *coordinates = m_coordinateList.at(iter); if(coordinates->start() >= polymerSize || coordinates->end() >= polymerSize) { QMessageBox::warning(0, tr("massXpert - Compositions"), tr("Selection data are no more valid.\n" "Please update these data."), QMessageBox::Ok); return false; } } // We also want to know if the cross-links should be taken into account. if (mp_calcOptions->monomerEntities() & MXT_MONOMER_CHEMENT_CROSS_LINK) { // If the whole sequence is dealt with, then by definition all // the cross-links are encompassed by the sequence. if(!m_ui.selectedSequenceRadioButton->isChecked()) { m_ui.incompleteCrossLinkWarningLabel->setText (tr("Incomplete cross-links: %1"). arg(0)); } else { // We have to count the incomplete cross-links. const CrossLinkList &crossLinkList = mp_polymer->crossLinkList(); int crossLinkPartial = 0; for (int iter = 0; iter < crossLinkList.size(); ++iter) { CrossLink *crossLink = crossLinkList.at(iter); int ret = crossLink->encompassedBy(m_coordinateList); if(ret == MXP_CROSS_LINK_ENCOMPASSED_FULL) { // qDebug() << __FILE__ << __LINE__ // << "CrossLink at iter:" << iter // << "is fully encompassed"; } else if (ret == MXP_CROSS_LINK_ENCOMPASSED_PARTIAL) { // qDebug() << __FILE__ << __LINE__ // << "CrossLink at iter:" << iter // << "is partially encompassed"; ++crossLinkPartial; } else { // qDebug() << __FILE__ << __LINE__ // << "CrossLink at iter:" << iter // << "is not encompassed at all"; } } m_ui.incompleteCrossLinkWarningLabel->setText (tr("Incomplete cross-links: %1"). arg(crossLinkPartial)); } } else { m_ui.incompleteCrossLinkWarningLabel-> setText(tr("Not accounting for cross-links")); } return true; } void CompositionsDlg::monomericComposition() { if (!fetchValidateInputData()) { QMessageBox::warning(0, tr("massXpert - Compositions"), tr("Failed validating input data."), QMessageBox::Ok); return; } Monomer *fakeMonomer = 0; // Empty the treeview. This will also remove all the monomer items // in the m_monomerList. mpa_compositionTreeViewModel->removeAll(); setCursor(Qt::WaitCursor); // Iterate in the sequence and count all the occurrences of the // different monomer that comprise the sequence. if (mpa_compositionTreeViewModel) { delete mpa_compositionTreeViewModel; mpa_compositionTreeViewModel = 0; } if (mpa_compositionProxyModel) { delete mpa_compositionProxyModel; mpa_compositionProxyModel = 0; } for (int iter = 0; iter < m_coordinateList.size(); ++iter) { // New coordinates instance we are iterating into. Coordinates *coordinates = m_coordinateList.at(iter); for(int jter = coordinates->start() ; jter < coordinates->end() + 1; ++jter) { bool processed = false; const Monomer *iterMonomer = mp_polymer->at(jter); Q_ASSERT(iterMonomer); // Check if monomer by same name is not already in our list of // fake monomers. for (int kter = 0; kter < m_monomerList.size(); ++kter) { fakeMonomer = m_monomerList.at(kter); Q_ASSERT(fakeMonomer); if(iterMonomer->name() == fakeMonomer->name()) { Prop *prop = fakeMonomer->prop("MONOMER_COUNT"); // The fake monomer MUST have a count prop ! Q_ASSERT(prop); int *count = new int(*static_cast(prop->data())); ++(*count); prop->setData(count); if (iterMonomer->isModified()) { // The monomer in the sequence is modified. Let the // fake monomer know it. Prop *prop = fakeMonomer->prop("MODIF_COUNT"); // The fake monomer might not have a modif prop. if(prop) { int *count = new int(*static_cast(prop->data())); ++(*count); prop->setData(count); } else { IntProp *prop = new IntProp("MODIF_COUNT", 1); fakeMonomer->appendProp(prop); } } processed = true; break; } } // End of // for (int kter = 0; kter < m_monomerList.size(); ++kter) // Did we find a fake monomer, thus processing the currently // iterated sequence monomer or not ? If not we still have to do // all the work. if (!processed) { fakeMonomer = iterMonomer->clone(); IntProp *prop = new IntProp("MONOMER_COUNT", 1); fakeMonomer->appendProp(prop); if(iterMonomer->isModified()) { // The monomer in the sequence is modified. Let the // fake monomer know it. IntProp *prop = new IntProp("MODIF_COUNT", 1); fakeMonomer->appendProp(prop); } m_monomerList.append(fakeMonomer); } } // End of // for (int iter = startIndex ; iter < endIndex + 1; ++iter) setupTreeView(); prepareResultsTxtString(MXP_TARGET_MONOMERIC); setCursor(Qt::ArrowCursor); } } void CompositionsDlg::elementalComposition() { // For each monomer in the sequence, get the formula and account it. if (!fetchValidateInputData()) { QMessageBox::warning(0, tr("massXpert - Compositions"), tr("Failed validating input data."), QMessageBox::Ok); return; } QString composition = mp_polymer->elementalComposition(*mp_ionizeRule, m_coordinateList, *mp_calcOptions); m_ui.elementalCompositionLineEdit-> setText(composition); prepareResultsTxtString(MXP_TARGET_ELEMENTAL); setCursor(Qt::ArrowCursor); } void CompositionsDlg::setupTreeView() { // Model stuff all thought for sorting. mpa_compositionTreeViewModel = new CompositionTreeViewModel(&m_monomerList, this); mpa_compositionProxyModel = new CompositionTreeViewSortProxyModel(this); mpa_compositionProxyModel->setSourceModel(mpa_compositionTreeViewModel); m_ui.compositionTreeView->setModel(mpa_compositionProxyModel); m_ui.compositionTreeView->setParentDlg(this); mpa_compositionTreeViewModel->setTreeView(m_ui.compositionTreeView); } void CompositionsDlg::freeMonomerList() { while(!m_monomerList.isEmpty()) delete m_monomerList.takeFirst(); } // The results-exporting functions. //////////////////////////////// // The results-exporting functions. //////////////////////////////// // The results-exporting functions. //////////////////////////////// void CompositionsDlg::exportResults(int index) { // Remember that we had set up the combobox with the following strings: // << tr("To &Clipboard") // << tr("To &File") // << tr("&Select File"); if (index == 0) { exportResultsClipboard(); } else if (index == 1) { exportResultsFile(); } else if (index == 2) { selectResultsFile(); } else Q_ASSERT(0); } void CompositionsDlg::prepareResultsTxtString(int target) { mpa_resultsString->clear(); *mpa_resultsString += QObject::tr("# \n" "# -------------\n" "# Compositions: \n" "# -------------\n"); if (target == MXP_TARGET_ELEMENTAL) { *mpa_resultsString += QObject::tr("\nIonization rule:\n"); *mpa_resultsString += QObject::tr("Formula: %1 - ") .arg(mp_ionizeRule->formula()); *mpa_resultsString += QObject::tr("Charge: %1 - ") .arg(mp_ionizeRule->charge()); *mpa_resultsString += QObject::tr("Level: %1\n") .arg(mp_ionizeRule->level()); *mpa_resultsString += QObject::tr("\nCalculation options:\n"); bool withEntities =(mp_calcOptions->monomerEntities() & MXT_MONOMER_CHEMENT_MODIF); // We want a delimited sequence with indication of the // different sequence regions for which the composition was // determined, thus the true below. QString *sequence = mp_polymer->monomerText(m_coordinateList, withEntities, true); *mpa_resultsString += *sequence; delete sequence; if(withEntities) *mpa_resultsString += QObject::tr("Account monomer modifs: yes\n"); else *mpa_resultsString += QObject::tr("Account monomer modifs: no\n"); // Left end and right end modifs withEntities =(mp_calcOptions->polymerEntities() & MXT_POLYMER_CHEMENT_LEFT_END_MODIF || mp_calcOptions->polymerEntities() & MXT_POLYMER_CHEMENT_RIGHT_END_MODIF); if(!withEntities) { *mpa_resultsString += QObject::tr("Account ends' modifs: no\n"); } else { *mpa_resultsString += QObject::tr("Account ends' modifs: yes - "); // Left end modif withEntities =(mp_calcOptions->polymerEntities() & MXT_POLYMER_CHEMENT_LEFT_END_MODIF); if (withEntities) { *mpa_resultsString += QObject::tr("Left end modif: %1 - ") .arg(mp_polymer->leftEndModif().name()); } // Right end modif withEntities =(mp_calcOptions->polymerEntities() & MXT_POLYMER_CHEMENT_RIGHT_END_MODIF); if (withEntities) { *mpa_resultsString += QObject::tr("Right end modif: %1") .arg(mp_polymer->leftEndModif().name()); } } *mpa_resultsString += QObject::tr("\n\nElemental composition: %1") .arg(m_ui.elementalCompositionLineEdit->text()); } else if (target == MXP_TARGET_MONOMERIC) { CompositionTreeViewModel *model = static_cast (m_ui.compositionTreeView->model()); Q_ASSERT(model); int rowCount = model->rowCount(); // qDebug() << __FILE__ << __LINE__ << "rowCount" << rowCount; if(!rowCount) return; QString composString; for(int iter = 0; iter < rowCount; ++iter) { QModelIndex currentIndex = model->index(iter, COMPOSITION_NAME_COLUMN, QModelIndex()); Q_ASSERT(currentIndex.isValid()); composString += QObject::tr("%1 - "). arg(model->data(currentIndex, Qt::DisplayRole).toString()); currentIndex = model->index(iter, COMPOSITION_CODE_COLUMN, QModelIndex()); Q_ASSERT(currentIndex.isValid()); composString += QObject::tr("%1 - "). arg(model->data(currentIndex, Qt::DisplayRole).toString()); currentIndex = model->index(iter, COMPOSITION_MODIF_COLUMN, QModelIndex()); Q_ASSERT(currentIndex.isValid()); composString += QObject::tr("Modified ?: %1 - "). arg(model->data(currentIndex, Qt::DisplayRole).toString()); currentIndex = model->index(iter, COMPOSITION_COUNT_COLUMN, QModelIndex()); Q_ASSERT(currentIndex.isValid()); composString += QObject::tr("Count: %1.\n"). arg(model->data(currentIndex, Qt::DisplayRole).toString()); } *mpa_resultsString += composString; } else Q_ASSERT(0); } bool CompositionsDlg::exportResultsClipboard() { QClipboard *clipboard = QApplication::clipboard(); clipboard->setText(*mpa_resultsString, QClipboard::Clipboard); return true; } bool CompositionsDlg::exportResultsFile() { if (m_resultsFilePath.isEmpty()) { if(!selectResultsFile()) return false; } QFile file(m_resultsFilePath); if (!file.open(QIODevice::WriteOnly | QIODevice::Append)) { QMessageBox::information(0, tr("massXpert - Export Data"), tr("Failed to open file in append mode."), QMessageBox::Ok); return false; } QTextStream stream(&file); stream.setCodec("UTF-8"); stream << *mpa_resultsString; file.close(); return true; } bool CompositionsDlg::selectResultsFile() { m_resultsFilePath = QFileDialog::getSaveFileName(this, tr("Select file to export data to"), QDir::homePath(), tr("Data files(*.dat *.DAT)")); if (m_resultsFilePath.isEmpty()) return false; return true; } //////////////////////////////////// The results-exporting functions. //////////////////////////////////// The results-exporting functions. //////////////////////////////////// The results-exporting functions. } // namespace massXpert