From 50ca5e294d63c11cda265f2c02e8870dd96d2534 Mon Sep 17 00:00:00 2001 From: Jiri Novak Date: Wed, 21 Feb 2018 15:28:55 +0100 Subject: [PATCH] release 1.0.1512 --- CycloBranch/CycloBranch-Linux.pro | 14 +- CycloBranch/CycloBranch-MacOSX.pro | 18 +- CycloBranch/CycloBranch.vcxproj | 146 +- CycloBranch/CycloBranch.vcxproj.filters | 60 +- CycloBranch/core/cBrick.cpp | 13 + CycloBranch/core/cBrick.h | 25 + CycloBranch/core/cBricksDatabase.cpp | 51 + CycloBranch/core/cBricksDatabase.h | 15 + CycloBranch/core/cCandidate.cpp | 250 +++- CycloBranch/core/cCandidate.h | 83 +- CycloBranch/core/cDeNovoGraph.cpp | 302 +++- CycloBranch/core/cDeNovoGraph.h | 2 +- CycloBranch/core/cDeNovoGraphNode.cpp | 6 +- CycloBranch/core/cDeNovoGraphNode.h | 6 +- CycloBranch/core/cFragmentIons.cpp | 1196 +++++++++++++++- CycloBranch/core/cFragmentIons.h | 354 +++-- CycloBranch/core/cImzML.cpp | 228 +++ CycloBranch/core/cImzML.h | 117 ++ CycloBranch/core/cParameters.cpp | 232 +++- CycloBranch/core/cParameters.h | 111 +- CycloBranch/core/cPeak.cpp | 16 +- CycloBranch/core/cPeak.h | 20 +- CycloBranch/core/cPeakListSeries.cpp | 171 +++ CycloBranch/core/cPeakListSeries.h | 130 ++ CycloBranch/core/cPeaksList.cpp | 106 +- CycloBranch/core/cPeaksList.h | 34 + CycloBranch/core/cSequence.cpp | 8 +- CycloBranch/core/cSequence.h | 8 +- CycloBranch/core/cSequenceDatabase.cpp | 19 +- CycloBranch/core/cTheoreticalSpectrum.cpp | 1231 ++++++++++++----- CycloBranch/core/cTheoreticalSpectrum.h | 108 +- CycloBranch/core/cTheoreticalSpectrumList.cpp | 26 +- CycloBranch/core/cTheoreticalSpectrumList.h | 8 - CycloBranch/core/utilities.cpp | 54 +- CycloBranch/core/utilities.h | 32 +- CycloBranch/gui/cAboutWidget.cpp | 12 +- CycloBranch/gui/cBranchCyclicWidget.cpp | 362 +++++ CycloBranch/gui/cBranchCyclicWidget.h | 131 ++ CycloBranch/gui/cBricksDatabaseWidget.cpp | 37 +- CycloBranch/gui/cBricksDatabaseWidget.h | 5 + CycloBranch/gui/cCyclicWidget.cpp | 47 +- CycloBranch/gui/cDelegate.cpp | 12 + CycloBranch/gui/cDelegate.h | 38 + CycloBranch/gui/cDrawPeptideWidget.cpp | 96 +- CycloBranch/gui/cDrawPeptideWidget.h | 7 +- CycloBranch/gui/cFragmentIonsListWidget.cpp | 6 +- CycloBranch/gui/cGraphWidget.cpp | 8 + CycloBranch/gui/cGraphWidget.h | 3 + CycloBranch/gui/cHTMLExportDialog.cpp | 77 ++ CycloBranch/gui/cHTMLExportDialog.h | 111 ++ CycloBranch/gui/cLinearWidget.cpp | 36 +- CycloBranch/gui/cMainThread.cpp | 132 +- CycloBranch/gui/cMainWindow.cpp | 350 +++-- CycloBranch/gui/cMainWindow.h | 12 +- CycloBranch/gui/cModificationsWidget.cpp | 37 +- CycloBranch/gui/cModificationsWidget.h | 6 + CycloBranch/gui/cParametersWidget.cpp | 185 ++- CycloBranch/gui/cParametersWidget.h | 3 + CycloBranch/gui/cSequenceDatabaseWidget.cpp | 55 +- CycloBranch/gui/cSequenceDatabaseWidget.h | 6 + CycloBranch/gui/cSpectrumDetailWidget.cpp | 946 +++++++++++-- CycloBranch/gui/cSpectrumDetailWidget.h | 93 +- CycloBranch/gui/cSpectrumSceneWidget.cpp | 80 +- CycloBranch/gui/cSpectrumSceneWidget.h | 27 +- CycloBranch/images.qrc | 2 + CycloBranch/images/icons/25.png | Bin 0 -> 883 bytes CycloBranch/images/icons/5.png | Bin 0 -> 1042 bytes CycloBranch/images/splash.cdr | Bin 99027 -> 110206 bytes CycloBranch/images/splash.png | Bin 52065 -> 73016 bytes CycloBranch/main.cpp | 6 +- CycloBranch/parallel/cGraphReaderThread.cpp | 148 +- .../parallel/cSpectrumComparatorThread.cpp | 12 +- CycloBranch/readme-linux-compile.txt | 1 + CycloBranch/readme-macosx-compile.txt | 1 + readme.txt | 2 +- 75 files changed, 7188 insertions(+), 1104 deletions(-) create mode 100644 CycloBranch/core/cImzML.cpp create mode 100644 CycloBranch/core/cImzML.h create mode 100644 CycloBranch/core/cPeakListSeries.cpp create mode 100644 CycloBranch/core/cPeakListSeries.h create mode 100644 CycloBranch/gui/cBranchCyclicWidget.cpp create mode 100644 CycloBranch/gui/cBranchCyclicWidget.h create mode 100644 CycloBranch/gui/cDelegate.cpp create mode 100644 CycloBranch/gui/cDelegate.h create mode 100644 CycloBranch/gui/cHTMLExportDialog.cpp create mode 100644 CycloBranch/gui/cHTMLExportDialog.h create mode 100644 CycloBranch/images/icons/25.png create mode 100644 CycloBranch/images/icons/5.png diff --git a/CycloBranch/CycloBranch-Linux.pro b/CycloBranch/CycloBranch-Linux.pro index 996dcea..20323f5 100644 --- a/CycloBranch/CycloBranch-Linux.pro +++ b/CycloBranch/CycloBranch-Linux.pro @@ -7,7 +7,7 @@ TARGET = CycloBranch QT += core gui widgets printsupport svg INCLUDEPATH += . core gui parallel QMAKE_CXXFLAGS += -std=c++0x -DLINUX -m64 -QMAKE_LIBS += -lboost_regex +QMAKE_LIBS += -lboost_regex -lxerces-c OBJECTS_DIR = build/ MOC_DIR = moc/ @@ -21,8 +21,10 @@ HEADERS += core/cAllocator.h \ core/cDeNovoGraph.h \ core/cDeNovoGraphNode.h \ core/cFragmentIons.h \ + core/cImzML.h \ core/cParameters.h \ core/cPeak.h \ + core/cPeakListSeries.h \ core/cPeaksList.h \ core/cSequence.h \ core/cSequenceDatabase.h \ @@ -31,16 +33,18 @@ HEADERS += core/cAllocator.h \ core/cTheoreticalSpectrumList.h \ core/utilities.h \ gui/cAboutWidget.h \ + gui/cBranchCyclicWidget.h \ gui/cBranchedWidget.h \ gui/cBricksDatabaseWidget.h \ gui/cCyclicWidget.h \ + gui/cDelegate.h \ gui/cDrawPeptideWidget.h \ gui/cEventFilter.h \ gui/cExportDialog.h \ gui/cFindDialog.h \ gui/cFragmentIonsListWidget.h \ gui/cGraphWidget.h \ - gui/cLassoWidget.h \ + gui/cHTMLExportDialog.h \ gui/cLinearWidget.h \ gui/cMainThread.h \ gui/cMainWindow.h \ @@ -58,8 +62,10 @@ SOURCES += core/cBrick.cpp \ core/cDeNovoGraph.cpp \ core/cDeNovoGraphNode.cpp \ core/cFragmentIons.cpp \ + core/cImzML.cpp \ core/cParameters.cpp \ core/cPeak.cpp \ + core/cPeakListSeries.cpp \ core/cPeaksList.cpp \ core/cSequence.cpp \ core/cSequenceDatabase.cpp \ @@ -68,16 +74,18 @@ SOURCES += core/cBrick.cpp \ core/cTheoreticalSpectrumList.cpp \ core/utilities.cpp \ gui/cAboutWidget.cpp \ + gui/cBranchCyclicWidget.cpp \ gui/cBranchedWidget.cpp \ gui/cBricksDatabaseWidget.cpp \ gui/cCyclicWidget.cpp \ + gui/cDelegate.cpp \ gui/cDrawPeptideWidget.cpp \ gui/cEventFilter.cpp \ gui/cExportDialog.cpp \ gui/cFindDialog.cpp \ gui/cFragmentIonsListWidget.cpp \ gui/cGraphWidget.cpp \ - gui/cLassoWidget.cpp \ + gui/cHTMLExportDialog.cpp \ gui/cLinearWidget.cpp \ gui/cMainThread.cpp \ gui/cMainWindow.cpp \ diff --git a/CycloBranch/CycloBranch-MacOSX.pro b/CycloBranch/CycloBranch-MacOSX.pro index 822b6d9..7014a69 100644 --- a/CycloBranch/CycloBranch-MacOSX.pro +++ b/CycloBranch/CycloBranch-MacOSX.pro @@ -5,10 +5,10 @@ TEMPLATE = app TARGET = CycloBranch QT += core gui widgets printsupport svg -INCLUDEPATH += . core gui parallel /usr/local/Cellar/boost/1.57.0/include +INCLUDEPATH += . core gui parallel /usr/local/Cellar/boost/1.57.0/include /usr/local/Cellar/xerces-c/3.1.1/include QMAKE_CXXFLAGS += -std=c++0x -DMACOSX -m64 -QMAKE_LIBS += -lboost_regex -QMAKE_LIBDIR = /usr/local/Cellar/boost/1.57.0/lib +QMAKE_LIBS += -lboost_regex -lxerces-c +QMAKE_LIBDIR = /usr/local/Cellar/boost/1.57.0/lib /usr/local/Cellar/xerces-c/3.1.1/lib OBJECTS_DIR = build/ MOC_DIR = moc/ ICON = images/cb.icns @@ -29,8 +29,10 @@ HEADERS += core/cAllocator.h \ core/cDeNovoGraph.h \ core/cDeNovoGraphNode.h \ core/cFragmentIons.h \ + core/cImzML.h \ core/cParameters.h \ core/cPeak.h \ + core/cPeakListSeries.h \ core/cPeaksList.h \ core/cSequence.h \ core/cSequenceDatabase.h \ @@ -39,16 +41,18 @@ HEADERS += core/cAllocator.h \ core/cTheoreticalSpectrumList.h \ core/utilities.h \ gui/cAboutWidget.h \ + gui/cBranchCyclicWidget.h \ gui/cBranchedWidget.h \ gui/cBricksDatabaseWidget.h \ gui/cCyclicWidget.h \ + gui/cDelegate.h \ gui/cDrawPeptideWidget.h \ gui/cEventFilter.h \ gui/cExportDialog.h \ gui/cFindDialog.h \ gui/cFragmentIonsListWidget.h \ gui/cGraphWidget.h \ - gui/cLassoWidget.h \ + gui/cHTMLExportDialog.h \ gui/cLinearWidget.h \ gui/cMainThread.h \ gui/cMainWindow.h \ @@ -66,8 +70,10 @@ SOURCES += core/cBrick.cpp \ core/cDeNovoGraph.cpp \ core/cDeNovoGraphNode.cpp \ core/cFragmentIons.cpp \ + core/cImzML.cpp \ core/cParameters.cpp \ core/cPeak.cpp \ + core/cPeakListSeries.cpp \ core/cPeaksList.cpp \ core/cSequence.cpp \ core/cSequenceDatabase.cpp \ @@ -76,16 +82,18 @@ SOURCES += core/cBrick.cpp \ core/cTheoreticalSpectrumList.cpp \ core/utilities.cpp \ gui/cAboutWidget.cpp \ + gui/cBranchCyclicWidget.cpp \ gui/cBranchedWidget.cpp \ gui/cBricksDatabaseWidget.cpp \ gui/cCyclicWidget.cpp \ + gui/cDelegate.cpp \ gui/cDrawPeptideWidget.cpp \ gui/cEventFilter.cpp \ gui/cExportDialog.cpp \ gui/cFindDialog.cpp \ gui/cFragmentIonsListWidget.cpp \ gui/cGraphWidget.cpp \ - gui/cLassoWidget.cpp \ + gui/cHTMLExportDialog.cpp \ gui/cLinearWidget.cpp \ gui/cMainThread.cpp \ gui/cMainWindow.cpp \ diff --git a/CycloBranch/CycloBranch.vcxproj b/CycloBranch/CycloBranch.vcxproj index 3431970..714f7a1 100644 --- a/CycloBranch/CycloBranch.vcxproj +++ b/CycloBranch/CycloBranch.vcxproj @@ -125,7 +125,7 @@ UNICODE;WIN32;WIN64;QT_DLL;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;QT_GUI_LIB;QT_WIDGETS_LIB;QT_PRINTSUPPORT_LIB;QT_SVG_LIB;%(PreprocessorDefinitions) - .;.\GeneratedFiles;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtPrintSupport;C:\boost_1_57_0\;C:\boost_1_57_0\boost;%(AdditionalIncludeDirectories) + .;.\GeneratedFiles;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtSvg;$(QTDIR)\include\QtPrintSupport;C:\boost_1_57_0\;C:\xerces-c-3.1.1\include;%(AdditionalIncludeDirectories) ProgramDatabase MultiThreadedDLL true @@ -138,9 +138,9 @@ Windows $(OutDir)\$(ProjectName).exe - $(QTDIR)\lib;C:\boost_1_57_0\lib64-msvc-11.0;%(AdditionalLibraryDirectories) + $(QTDIR)\lib;C:\boost_1_57_0\lib64-msvc-11.0;C:\xerces-c-3.1.1\lib;%(AdditionalLibraryDirectories) false - qtmain.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5Svg.lib;Qt5PrintSupport.lib;libboost_regex-vc110-mt-1_57.lib;%(AdditionalDependencies) + qtmain.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5Svg.lib;Qt5PrintSupport.lib;libboost_regex-vc110-mt-1_57.lib;xerces-c_3.lib;%(AdditionalDependencies) true true UseLinkTimeCodeGeneration @@ -154,8 +154,10 @@ + + @@ -167,6 +169,10 @@ true true + + true + true + true true @@ -179,6 +185,10 @@ true true + + true + true + true true @@ -207,7 +217,7 @@ true true - + true true @@ -261,6 +271,10 @@ true true + + true + true + true true @@ -273,6 +287,10 @@ true true + + true + true + true true @@ -301,7 +319,7 @@ true true - + true true @@ -342,16 +360,18 @@ true + + - + @@ -373,13 +393,69 @@ + + + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing cHTMLExportDialog.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing cHTMLExportDialog.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing cHTMLExportDialog.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing cHTMLExportDialog.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" + + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing cBranchCyclicWidget.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing cBranchCyclicWidget.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing cBranchCyclicWidget.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing cBranchCyclicWidget.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" + + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing cDelegate.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing cDelegate.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing cDelegate.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing cDelegate.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" + $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cExportDialog.h... @@ -396,7 +472,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cExportDialog.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" $(QTDIR)\bin\moc.exe;%(FullPath) @@ -414,7 +490,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cFindDialog.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" $(QTDIR)\bin\moc.exe;%(FullPath) @@ -432,7 +508,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cSpectrumSceneWidget.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" $(QTDIR)\bin\moc.exe;%(FullPath) @@ -450,7 +526,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cEventFilter.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" @@ -494,7 +570,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cDrawPeptideWidget.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" $(QTDIR)\bin\moc.exe;%(FullPath) @@ -512,7 +588,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cModificationsWidget.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" $(QTDIR)\bin\moc.exe;%(FullPath) @@ -530,7 +606,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cSequenceDatabaseWidget.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" $(QTDIR)\bin\moc.exe;%(FullPath) @@ -548,25 +624,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cBricksDatabaseWidget.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" - - - $(QTDIR)\bin\moc.exe;%(FullPath) - Moc%27ing cLassoWidget.h... - .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" - $(QTDIR)\bin\moc.exe;%(FullPath) - Moc%27ing cLassoWidget.h... - .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" - $(QTDIR)\bin\moc.exe;%(FullPath) - Moc%27ing cLassoWidget.h... - .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" - $(QTDIR)\bin\moc.exe;%(FullPath) - Moc%27ing cLassoWidget.h... - .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" $(QTDIR)\bin\moc.exe;%(FullPath) @@ -584,7 +642,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cLinearWidget.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" $(QTDIR)\bin\moc.exe;%(FullPath) @@ -602,7 +660,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cCyclicWidget.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" $(QTDIR)\bin\moc.exe;%(FullPath) @@ -620,7 +678,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cBranchedWidget.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" $(QTDIR)\bin\moc.exe;%(FullPath) @@ -638,7 +696,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cFragmentIonsListWidget.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" $(QTDIR)\bin\moc.exe;%(FullPath) @@ -656,7 +714,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cGraphReaderThread.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" Moc%27ing cSpectrumComparatorThread.h... @@ -670,7 +728,7 @@ "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" Moc%27ing cSpectrumComparatorThread.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" $(QTDIR)\bin\moc.exe;%(FullPath) $(QTDIR)\bin\moc.exe;%(FullPath) $(QTDIR)\bin\moc.exe;%(FullPath) @@ -692,7 +750,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cSpectrumDetailWidget.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" @@ -736,7 +794,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cMainWindow.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" $(QTDIR)\bin\moc.exe;%(FullPath) @@ -754,7 +812,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cMainThread.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" $(QTDIR)\bin\moc.exe;%(FullPath) @@ -772,7 +830,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cParametersWidget.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" $(QTDIR)\bin\moc.exe;%(FullPath) @@ -790,7 +848,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cGraphWidget.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" $(QTDIR)\bin\moc.exe;%(FullPath) @@ -808,7 +866,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) Moc%27ing cAboutWidget.h... .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp - "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-IC:\boost_1_57_0" "-IC:\boost_1_57_0\boost" "-I$(QTDIR)\include\QtPrintSupport" "-I$(QTDIR)\include\QtSvg" + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -DQT_PRINTSUPPORT_LIB -DQT_SVG_LIB "-I." "-I.\GeneratedFiles" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtSvg" "-I$(QTDIR)\include\QtPrintSupport" "-IC:\boost_1_57_0" "-IC:\xerces-c-3.1.1\include" diff --git a/CycloBranch/CycloBranch.vcxproj.filters b/CycloBranch/CycloBranch.vcxproj.filters index aa5e87e..aaef3c0 100644 --- a/CycloBranch/CycloBranch.vcxproj.filters +++ b/CycloBranch/CycloBranch.vcxproj.filters @@ -194,15 +194,6 @@ Source Files\gui - - Generated Files\Debug - - - Generated Files\Release - - - Source Files\gui - Source Files\core @@ -290,6 +281,39 @@ Source Files\gui + + Generated Files\Debug + + + Generated Files\Release + + + Source Files\gui + + + Source Files\core + + + Source Files\gui + + + Generated Files\Debug + + + Generated Files\Release + + + Source Files\gui + + + Generated Files\Debug + + + Generated Files\Release + + + Source Files\core + @@ -331,9 +355,6 @@ Header Files\gui - - Header Files\gui - Header Files\gui @@ -364,6 +385,15 @@ Header Files\gui + + Header Files\gui + + + Header Files\gui + + + Header Files\gui + @@ -411,6 +441,12 @@ Header Files\core + + Header Files\core + + + Header Files\core + diff --git a/CycloBranch/core/cBrick.cpp b/CycloBranch/core/cBrick.cpp index 3737504..8c488b2 100644 --- a/CycloBranch/core/cBrick.cpp +++ b/CycloBranch/core/cBrick.cpp @@ -50,6 +50,7 @@ void cBrick::clear() { mass = 0; composition = ""; artificial = false; + residuelosstype = water; } @@ -365,6 +366,16 @@ bool cBrick::isArtificial() { } +void cBrick::setResidueLossType(eResidueLossType residuelosstype) { + this->residuelosstype = residuelosstype; +} + + +eResidueLossType cBrick::getResidueLossType() { + return residuelosstype; +} + + void cBrick::store(ofstream& os) { storeString(name, os); storeStringVector(acronyms, os); @@ -373,6 +384,7 @@ void cBrick::store(ofstream& os) { os.write((char *)&mass, sizeof(double)); storeString(composition, os); os.write((char *)&artificial, sizeof(bool)); + os.write((char *)&residuelosstype, sizeof(eResidueLossType)); } @@ -384,5 +396,6 @@ void cBrick::load(ifstream& is) { is.read((char *)&mass, sizeof(double)); loadString(composition, is); is.read((char *)&artificial, sizeof(bool)); + is.read((char *)&residuelosstype, sizeof(eResidueLossType)); } diff --git a/CycloBranch/core/cBrick.h b/CycloBranch/core/cBrick.h index 5a717be..4711641 100644 --- a/CycloBranch/core/cBrick.h +++ b/CycloBranch/core/cBrick.h @@ -17,6 +17,16 @@ using namespace std; +/** + \brief Residue loss types. +*/ +enum eResidueLossType { + water = 0, + h2, + h2o2 +}; + + /** \brief Get the number of bricks in a composition. \param composition string containing ids of bricks separated by '-' @@ -44,6 +54,7 @@ class cBrick { double mass; string composition; bool artificial; + eResidueLossType residuelosstype; public: @@ -220,6 +231,20 @@ class cBrick { bool isArtificial(); + /** + \brief Set the residue loss type. + \param residuelosstype the residue loss type. + */ + void setResidueLossType(eResidueLossType residuelosstype); + + + /** + \brief Get the residue loss type. + \retval eResidueLossType the residue loss type. + */ + eResidueLossType getResidueLossType(); + + /** \brief Store the structure into an output stream. \param os an output stream diff --git a/CycloBranch/core/cBricksDatabase.cpp b/CycloBranch/core/cBricksDatabase.cpp index e1693b7..6c43754 100644 --- a/CycloBranch/core/cBricksDatabase.cpp +++ b/CycloBranch/core/cBricksDatabase.cpp @@ -128,6 +128,11 @@ int cBricksDatabase::loadFromPlainTextStream(ifstream &stream, string& errormess size_t pos; double mass; +#if POLYKETIDE_SIDEROPHORES == 1 + regex rx; + string name; +#endif + bool error = false; errormessage = ""; cSummaryFormula formula; @@ -160,6 +165,20 @@ int cBricksDatabase::loadFromPlainTextStream(ifstream &stream, string& errormess break; } +#if POLYKETIDE_SIDEROPHORES == 1 + name = b.getName(); + + rx = "^\\(-2H\\) "; + if (regex_search(name, rx)) { + b.setResidueLossType(h2); + } + + rx = "^\\(-2OH\\) "; + if (regex_search(name, rx)) { + b.setResidueLossType(h2o2); + } +#endif + // load acronyms pos = s.find('\t'); if (pos != string::npos) { @@ -534,3 +553,35 @@ void cBricksDatabase::load(ifstream& is) { } } + +#if POLYKETIDE_SIDEROPHORES == 1 + + +bool cBricksDatabase::checkPolyketideBlocks(cBrick& brickseries) { + vector intcomposition; + brickseries.explodeToIntComposition(intcomposition); + + if (intcomposition.size() == 0) { + return false; + } + + int hydrogens = 0; + int hydroxyls = 0; + for (int i = 0; i < (int)intcomposition.size(); i++) { + if (bricks[intcomposition[i] - 1].getResidueLossType() == h2) { + hydrogens++; + } + if (bricks[intcomposition[i] - 1].getResidueLossType() == h2o2) { + hydroxyls++; + } + } + + if ((hydrogens == hydroxyls) || (hydrogens == hydroxyls + 1) || (hydrogens + 1 == hydroxyls)) { + return true; + } + + return false; +} + + +#endif \ No newline at end of file diff --git a/CycloBranch/core/cBricksDatabase.h b/CycloBranch/core/cBricksDatabase.h index ff62d24..3a236c8 100644 --- a/CycloBranch/core/cBricksDatabase.h +++ b/CycloBranch/core/cBricksDatabase.h @@ -222,6 +222,21 @@ class cBricksDatabase { */ void load(ifstream& is); + +#if POLYKETIDE_SIDEROPHORES == 1 + + + /** + \brief Check if the numbers of hydrogen and hydroxyl blocks are correct. + \param brickseries a tested combination of building blocks + \retval bool true when the numbers of blocks are correct + */ + bool checkPolyketideBlocks(cBrick& brickseries); + + +#endif + + }; diff --git a/CycloBranch/core/cCandidate.cpp b/CycloBranch/core/cCandidate.cpp index c784205..cca1343 100644 --- a/CycloBranch/core/cCandidate.cpp +++ b/CycloBranch/core/cCandidate.cpp @@ -47,7 +47,7 @@ void cCandidate::getPermutationsIter(cCandidateSet& permutations, vector } -void cCandidate::attachSubBranchCandidates(cCandidate& candidate, cCandidateSet& result, peptideType peptidetype, bool* terminatecomputation) { +void cCandidate::attachSubBranchCandidates(cCandidate& candidate, cCandidateSet& result, ePeptideType peptidetype, bool* terminatecomputation) { if (*terminatecomputation) { return; } @@ -81,7 +81,7 @@ void cCandidate::attachSubBranchCandidates(cCandidate& candidate, cCandidateSet& } -void cCandidate::attachAllBranches(cCandidate& candidate, cCandidateSet& result, peptideType peptidetype, bool* terminatecomputation) { +void cCandidate::attachAllBranches(cCandidate& candidate, cCandidateSet& result, ePeptideType peptidetype, bool* terminatecomputation) { cCandidate c; int start, end; int cumsize = 0; @@ -135,7 +135,7 @@ void cCandidate::getPartialRotations(const string& composition, vector& } -void cCandidate::getPartialLassoRotations(const string& composition, vector& lassorotations, int branchstart, int branchend) { +void cCandidate::getPartialBranchCyclicRotations(const string& composition, vector& branchcyclicrotations, int branchstart, int branchend) { cBrick b; string s; bool leftbracketput; @@ -176,7 +176,7 @@ void cCandidate::getPartialLassoRotations(const string& composition, vector ne; v.push_back(s); cCandidate c(v, ne, startmodifID, endmodifID, middlemodifID, (branchstart + count - i) % count, (branchend + count - i) % count); - lassorotations.push_back(c); + branchcyclicrotations.push_back(c); //cout << s << endl; } } @@ -298,14 +298,14 @@ void cCandidate::revertComposition() { } -void cCandidate::prepareBranchedCandidates(cCandidateSet& result, peptideType peptidetype, bool* terminatecomputation) { +void cCandidate::prepareBranchedCandidates(cCandidateSet& result, ePeptideType peptidetype, bool* terminatecomputation) { cCandidate c; result.getSet().clear(); - if (peptidetype == lasso) { + if (peptidetype == branchcyclic) { // just one from startmodifid and middlemodifid may be > 0 (checked in getCandidatesIter) - // endmodifid is always 0 because precursor_ion is not used in initializeFragmentIonsForDeNovoGraphOfLassoPeptides + // endmodifid is always 0 because precursor_ion is not used in initializeFragmentIonsForDeNovoGraphOfBranchCyclicPeptides // startmodifid is always N-terminal because only b-ions are used in the de novo graph if ((startmodifID > 0) && (composition.size() > 0)) { @@ -751,9 +751,32 @@ double cCandidate::getPrecursorMass(cBricksDatabase& brickdatabasewithcombinatio case branched: mass = parameters->fragmentdefinitions[precursor_ion].massdifference + parameters->searchedmodifications[startmodifID].massdifference + parameters->searchedmodifications[endmodifID].massdifference + parameters->searchedmodifications[middlemodifID].massdifference; break; - case lasso: + case branchcyclic: mass = parameters->fragmentdefinitions[cyclic_precursor_ion].massdifference + parameters->searchedmodifications[middlemodifID].massdifference; break; +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + switch (getResidueLossType(brickdatabasewithcombinations)) + { + case water: + mass = parameters->fragmentdefinitions[linear_polyketide_precursor_ion_h_oh].massdifference; + break; + case h2: + mass = parameters->fragmentdefinitions[linear_polyketide_precursor_ion_h_h].massdifference; + break; + case h2o2: + mass = parameters->fragmentdefinitions[linear_polyketide_precursor_ion_oh_oh].massdifference; + break; + default: + mass = 0; + break; + } + mass += parameters->searchedmodifications[startmodifID].massdifference + parameters->searchedmodifications[endmodifID].massdifference; + break; + case cyclicpolyketide: + mass = parameters->fragmentdefinitions[cyclic_polyketide_precursor_ion].massdifference; + break; +#endif case other: break; default: @@ -909,6 +932,18 @@ bool cCandidate::hasOnlyArtificialBricks(cBricksDatabase& brickdatabasewithcombi } +bool cCandidate::hasFirstBrickArtificial(cBricksDatabase& brickdatabasewithcombinations) { + cBrick b; + vector intcomposition; + b.setComposition(internalcomposition, false); + b.explodeToIntComposition(intcomposition); + if ((intcomposition.size() > 0) && brickdatabasewithcombinations[intcomposition[0] - 1].isArtificial()) { + return true; + } + return false; +} + + bool cCandidate::hasLastBrickArtificial(cBricksDatabase& brickdatabasewithcombinations) { cBrick b; vector intcomposition; @@ -921,6 +956,18 @@ bool cCandidate::hasLastBrickArtificial(cBricksDatabase& brickdatabasewithcombin } +bool cCandidate::hasLastBrickInvalid(cBricksDatabase& brickdatabasewithcombinations) { + cBrick b; + vector intcomposition; + b.setComposition(internalcomposition, false); + b.explodeToIntComposition(intcomposition); + if ((intcomposition.size() > 0) && brickdatabasewithcombinations[intcomposition.back() - 1].isArtificial() && (brickdatabasewithcombinations[intcomposition.back() - 1].getMass() <= 0)) { + return true; + } + return false; +} + + void cCandidate::getRotations(vector& rotations, bool includerevertedrotations) { rotations.clear(); //rotations.push_back(composition); @@ -935,16 +982,16 @@ void cCandidate::getRotations(vector& rotations, bool includerevertedrot } -void cCandidate::getLassoRotations(vector& lassorotations, bool includerevertedrotations) { - lassorotations.clear(); - getPartialLassoRotations(internalcomposition, lassorotations, branchstart, branchend); +void cCandidate::getBranchCyclicRotations(vector& branchcyclicrotations, bool includerevertedrotations) { + branchcyclicrotations.clear(); + getPartialBranchCyclicRotations(internalcomposition, branchcyclicrotations, branchstart, branchend); if (includerevertedrotations) { - getPartialLassoRotations(getRevertedTComposition(false), lassorotations, numberofinternalbricks - branchend - 1, numberofinternalbricks - branchstart - 1); + getPartialBranchCyclicRotations(getRevertedTComposition(false), branchcyclicrotations, numberofinternalbricks - branchend - 1, numberofinternalbricks - branchstart - 1); } } -cSummaryFormula cCandidate::getSummaryFormula(cParameters& parameters, peptideType peptidetype) { +cSummaryFormula cCandidate::getSummaryFormula(cParameters& parameters, ePeptideType peptidetype) { cBrick b; vector bricks; b.setComposition(internalcomposition, false); @@ -971,9 +1018,37 @@ cSummaryFormula cCandidate::getSummaryFormula(cParameters& parameters, peptideTy formula.addFormula(parameters.searchedmodifications[endmodifID].summary); formula.addFormula(parameters.searchedmodifications[middlemodifID].summary); break; - case lasso: + case branchcyclic: formula.addFormula(parameters.searchedmodifications[middlemodifID].summary); break; +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + switch (getResidueLossType(parameters.bricksdatabase)) + { + case water: + summary = "H2O"; + break; + case h2: + summary = "H2"; + break; + case h2o2: + summary = "H2O2"; + break; + default: + break; + } + + if (hasFirstBrickArtificial(parameters.bricksdatabase) || hasLastBrickArtificial(parameters.bricksdatabase)) { + summary = ""; + } + + formula.addFormula(summary); + formula.addFormula(parameters.searchedmodifications[startmodifID].summary); + formula.addFormula(parameters.searchedmodifications[endmodifID].summary); + break; + case cyclicpolyketide: + break; +#endif case other: break; default: @@ -1154,7 +1229,7 @@ void cCandidate::setPath(cDeNovoGraph& graph, cParameters* parameters) { stringpath += "ppm error: " + to_string(currentedge->ppmerror) + ", "; stringpath += "source charge: " + to_string(currentedge->sourcecharge) + ", "; stringpath += "target charge: " + to_string(currentedge->targetcharge); - if ((parameters->peptidetype == branched) || (parameters->peptidetype == lasso)) { + if ((parameters->peptidetype == branched) || (parameters->peptidetype == branchcyclic)) { if (currentedge->middlemodifID > 0) { stringpath += ", branch modification: " + parameters->searchedmodifications[currentedge->middlemodifID].name; } @@ -1175,16 +1250,20 @@ string& cCandidate::getPathAsString() { } -void cCandidate::setRealPeptideName(cBricksDatabase& bricksdatabase, peptideType peptidetype) { +void cCandidate::setRealPeptideName(cBricksDatabase& bricksdatabase, ePeptideType peptidetype) { switch (peptidetype) { case linear: case cyclic: +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + case cyclicpolyketide: +#endif case linearpolysaccharide: realpeptidename = bricksdatabase.getRealName(internalcomposition); break; case branched: - case lasso: + case branchcyclic: realpeptidename = getRealNameTComposition(bricksdatabase); break; case other: @@ -1195,16 +1274,20 @@ void cCandidate::setRealPeptideName(cBricksDatabase& bricksdatabase, peptideType } -void cCandidate::setAcronymPeptideNameWithHTMLReferences(cBricksDatabase& bricksdatabase, peptideType peptidetype) { +void cCandidate::setAcronymPeptideNameWithHTMLReferences(cBricksDatabase& bricksdatabase, ePeptideType peptidetype) { switch (peptidetype) { case linear: case cyclic: +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + case cyclicpolyketide: +#endif case linearpolysaccharide: acronympeptidename = bricksdatabase.getAcronymName(internalcomposition, true); break; case branched: - case lasso: + case branchcyclic: acronympeptidename = getAcronymsTComposition(bricksdatabase); break; case other: @@ -1225,6 +1308,135 @@ string& cCandidate::getAcronymPeptideNameWithHTMLReferences() { } +eResidueLossType cCandidate::getResidueLossType(cBricksDatabase& bricksdatabase) { + cBrick b; + b.setComposition(internalcomposition, false); + vector intcomposition; + b.explodeToIntComposition(intcomposition); + + int hydrogens = 0; + int hydroxyls = 0; + for (int i = 0; i < (int)intcomposition.size(); i++) { + if (bricksdatabase[intcomposition[i] - 1].getResidueLossType() == h2) { + hydrogens++; + } + if (bricksdatabase[intcomposition[i] - 1].getResidueLossType() == h2o2) { + hydroxyls++; + } + } + + if (hydrogens == hydroxyls) { + return water; + } + + if (hydrogens > hydroxyls) { + return h2; + } + + return h2o2; +} + + +#if POLYKETIDE_SIDEROPHORES == 1 + + +bool cCandidate::checkPolyketideSequence(cBricksDatabase& bricksdatabase, ePeptideType peptidetype) { + cBrick b; + b.setComposition(internalcomposition, false); + vector intcomposition; + b.explodeToIntComposition(intcomposition); + + if (intcomposition.size() == 0) { + return false; + } + + bool hasfirstblockartificial = hasFirstBrickArtificial(bricksdatabase); + bool haslastblockartificial = hasLastBrickArtificial(bricksdatabase); + + // cyclic polyketide has always an even number of blocks + if ((peptidetype == cyclicpolyketide) && !hasfirstblockartificial && !haslastblockartificial && ((int)intcomposition.size() % 2 == 1)) { + return false; + } + + for (int i = (hasfirstblockartificial?2:1); i < (haslastblockartificial?(int)intcomposition.size()-1:(int)intcomposition.size()); i++) { + if (!(((bricksdatabase[intcomposition[i] - 1].getResidueLossType() == h2) && (bricksdatabase[intcomposition[i - 1] - 1].getResidueLossType() == h2o2)) + || ((bricksdatabase[intcomposition[i] - 1].getResidueLossType() == h2o2) && (bricksdatabase[intcomposition[i - 1] - 1].getResidueLossType() == h2)))) { + return false; + } + } + + return true; +} + + +eResidueLossType cCandidate::getLeftResidueType(cBricksDatabase& bricksdatabase) { + cBrick b; + b.setComposition(internalcomposition, false); + vector intcomposition; + b.explodeToIntComposition(intcomposition); + + if (intcomposition.size() == 0) { + return water; + } + + return bricksdatabase[intcomposition[0] - 1].getResidueLossType(); +} + + +eResidueLossType cCandidate::getRightResidueType(cBricksDatabase& bricksdatabase) { + cBrick b; + b.setComposition(internalcomposition, false); + vector intcomposition; + b.explodeToIntComposition(intcomposition); + + if (intcomposition.size() == 0) { + return water; + } + + return bricksdatabase[intcomposition.back() - 1].getResidueLossType(); +} + + +bool cCandidate::checkPolyketideBlocks(cBricksDatabase& bricksdatabase, ePeptideType peptidetype) { + cBrick b; + b.setComposition(internalcomposition, false); + vector intcomposition; + b.explodeToIntComposition(intcomposition); + + if (intcomposition.size() == 0) { + return false; + } + + bool hasfirstblockartificial = hasFirstBrickArtificial(bricksdatabase); + bool haslastblockartificial = hasLastBrickArtificial(bricksdatabase); + + // cyclic polyketide has always an even number of blocks + if ((peptidetype == cyclicpolyketide) && !hasfirstblockartificial && !haslastblockartificial && ((int)intcomposition.size() % 2 == 1)) { + return false; + } + + int hydrogens = 0; + int hydroxyls = 0; + for (int i = (hasfirstblockartificial?1:0); i < (haslastblockartificial?(int)intcomposition.size()-1:(int)intcomposition.size()); i++) { + if (bricksdatabase[intcomposition[i] - 1].getResidueLossType() == h2) { + hydrogens++; + } + if (bricksdatabase[intcomposition[i] - 1].getResidueLossType() == h2o2) { + hydroxyls++; + } + } + + if ((hydrogens == hydroxyls) || (hydrogens == hydroxyls + 1) || (hydrogens + 1 == hydroxyls)) { + return true; + } + + return false; +} + + +#endif + + bool operator == (cCandidate const& a, cCandidate const& b) { return ((cCandidate &)a).isEqualTo((cCandidate &)b); } diff --git a/CycloBranch/core/cCandidate.h b/CycloBranch/core/cCandidate.h index 6204bf1..0afa307 100644 --- a/CycloBranch/core/cCandidate.h +++ b/CycloBranch/core/cCandidate.h @@ -153,13 +153,13 @@ class cCandidate { void getPermutationsIter(cCandidateSet& permutations, vector& currentcandidate, int position, bool* terminatecomputation); - void attachSubBranchCandidates(cCandidate& candidate, cCandidateSet& result, peptideType peptidetype, bool* terminatecomputation); + void attachSubBranchCandidates(cCandidate& candidate, cCandidateSet& result, ePeptideType peptidetype, bool* terminatecomputation); - void attachAllBranches(cCandidate& candidate, cCandidateSet& result, peptideType peptidetype, bool* terminatecomputation); + void attachAllBranches(cCandidate& candidate, cCandidateSet& result, ePeptideType peptidetype, bool* terminatecomputation); void getPartialRotations(const string& composition, vector& rotations); - void getPartialLassoRotations(const string& composition, vector& lassorotations, int branchstart, int branchend); + void getPartialBranchCyclicRotations(const string& composition, vector& branchcyclicrotations, int branchstart, int branchend); public: @@ -244,7 +244,7 @@ class cCandidate { \param peptidetype a type of an analyzed peptide \param terminatecomputation pointer to a variable determining that the computation must be stopped */ - void prepareBranchedCandidates(cCandidateSet& result, peptideType peptidetype, bool* terminatecomputation); + void prepareBranchedCandidates(cCandidateSet& result, ePeptideType peptidetype, bool* terminatecomputation); /** @@ -404,6 +404,14 @@ class cCandidate { bool hasOnlyArtificialBricks(cBricksDatabase& brickdatabasewithcombinations); + /** + \brief Check if the first brick is artificial. + \param brickdatabasewithcombinations reference to an input database of bricks with combinations of bricks + \retval bool true if the first brick is artificial, false otherwise + */ + bool hasFirstBrickArtificial(cBricksDatabase& brickdatabasewithcombinations); + + /** \brief Check if the last brick is artificial. \param brickdatabasewithcombinations reference to an input database of bricks with combinations of bricks @@ -411,7 +419,15 @@ class cCandidate { */ bool hasLastBrickArtificial(cBricksDatabase& brickdatabasewithcombinations); - + + /** + \brief Check if the last brick is invalid. + \param brickdatabasewithcombinations reference to an input database of bricks with combinations of bricks + \retval bool true if the last brick is invalid, false otherwise + */ + bool hasLastBrickInvalid(cBricksDatabase& brickdatabasewithcombinations); + + /** \brief Get rotations of a cyclic peptide sequence. \param rotations reference to an output vector containing rotations of a sequence @@ -422,10 +438,10 @@ class cCandidate { /** \brief Get branch-cyclic rotations of a branch-cyclic peptide sequence. - \param lassorotations reference to an output vector containing branch-cyclic rotations of a sequence + \param branchcyclicrotations reference to an output vector containing branch-cyclic rotations of a sequence \param includerevertedrotations if true then reverted branch-cyclic rotations are also included */ - void getLassoRotations(vector& lassorotations, bool includerevertedrotations); + void getBranchCyclicRotations(vector& branchcyclicrotations, bool includerevertedrotations); /** @@ -434,7 +450,7 @@ class cCandidate { \param peptidetype the type of peptide \retval cSummaryFormula the summary formula */ - cSummaryFormula getSummaryFormula(cParameters& parameters, peptideType peptidetype); + cSummaryFormula getSummaryFormula(cParameters& parameters, ePeptideType peptidetype); /** @@ -527,7 +543,7 @@ class cCandidate { \param bricksdatabase the database of building blocks \param peptidetype the type of peptide */ - void setRealPeptideName(cBricksDatabase& bricksdatabase, peptideType peptidetype); + void setRealPeptideName(cBricksDatabase& bricksdatabase, ePeptideType peptidetype); /** @@ -535,7 +551,7 @@ class cCandidate { \param bricksdatabase the database of building blocks \param peptidetype the type of peptide */ - void setAcronymPeptideNameWithHTMLReferences(cBricksDatabase& bricksdatabase, peptideType peptidetype); + void setAcronymPeptideNameWithHTMLReferences(cBricksDatabase& bricksdatabase, ePeptideType peptidetype); /** @@ -551,6 +567,53 @@ class cCandidate { */ string& getAcronymPeptideNameWithHTMLReferences(); + + /** + \brief Get the residue loss type. + \param bricksdatabase a database of building blocks + \retval eResidueLossType the residue loss type + */ + eResidueLossType getResidueLossType(cBricksDatabase& bricksdatabase); + + +#if POLYKETIDE_SIDEROPHORES == 1 + + /** + \brief Check if the order of blocks is correct. + \param bricksdatabase a database of building blocks + \param peptidetype the type of peptide + \retval bool true when the order of blocks is correct; false otherwise + */ + bool checkPolyketideSequence(cBricksDatabase& bricksdatabase, ePeptideType peptidetype); + + + /** + \brief Get the residue type of the left-most building block. + \param bricksdatabase a database of building blocks + \retval eResidueLossType residue loss type of the left-most building block + */ + eResidueLossType getLeftResidueType(cBricksDatabase& bricksdatabase); + + + /** + \brief Get the residue type of the right-most building block. + \param bricksdatabase a database of building blocks + \retval eResidueLossType residue loss type of the right-most building block + */ + eResidueLossType getRightResidueType(cBricksDatabase& bricksdatabase); + + + /** + \brief Check if the numbers of hydrogen and hydroxyl blocks are correct. + \param bricksdatabase a database of building blocks + \param peptidetype the type of peptide + \retval bool true when the numbers of blocks are correct + */ + bool checkPolyketideBlocks(cBricksDatabase& bricksdatabase, ePeptideType peptidetype); + + +#endif + }; diff --git a/CycloBranch/core/cDeNovoGraph.cpp b/CycloBranch/core/cDeNovoGraph.cpp index 4ae488b..e06a1db 100644 --- a/CycloBranch/core/cDeNovoGraph.cpp +++ b/CycloBranch/core/cDeNovoGraph.cpp @@ -145,7 +145,7 @@ int cDeNovoGraph::createGraph(bool& terminatecomputation) { double unchargedmz; int i; - sortedpeaklist = parameters->peaklist; + sortedpeaklist = parameters->peaklistseries[0]; // insert the single charged precursor, if neccessary sortedpeaklist.sortbyMass(); sortedpeaklist.cropMaximumMZRatio(unchargedprecursormass, parameters->precursormasserrortolerance); @@ -242,7 +242,7 @@ int cDeNovoGraph::createGraph(bool& terminatecomputation) { //sort(graph.begin() + 1, graph.end(), compareNodes); break; - case lasso: + case branchcyclic: node.clear(); node.setMZRatio(0); node.setIntensity(0); @@ -300,6 +300,154 @@ int cDeNovoGraph::createGraph(bool& terminatecomputation) { lastsystemnode = (int)graph.size() - 1; startnode = 1; break; +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + node.clear(); + node.setMZRatio(0); + node.setIntensity(0); + node.addIonAnnotation(l1h_ion); + node.addIonAnnotation(l2h_ion); + node.addIonAnnotation(l1oh_ion); + node.addIonAnnotation(l2oh_ion); + graph.push_back(node); + + node.clear(); + node.setMZRatio(parameters->fragmentdefinitions[l1h_ion].massdifference + negativeshift); + node.setIntensity(0); + node.addIonAnnotation(l1h_ion); + graph.push_back(node); + + node.clear(); + node.setMZRatio(parameters->fragmentdefinitions[l2h_ion].massdifference + negativeshift); + node.setIntensity(0); + node.addIonAnnotation(l2h_ion); + graph.push_back(node); + + node.clear(); + node.setMZRatio(parameters->fragmentdefinitions[l1oh_ion].massdifference + negativeshift); + node.setIntensity(0); + node.addIonAnnotation(l1oh_ion); + graph.push_back(node); + + node.clear(); + node.setMZRatio(parameters->fragmentdefinitions[l2oh_ion].massdifference + negativeshift); + node.setIntensity(0); + node.addIonAnnotation(l2oh_ion); + graph.push_back(node); + + e.clear(); + e.composition = "0"; + e.targetnode = 1; + graph[0].insertTempEdge(e); + + e.clear(); + e.composition = "0"; + e.targetnode = 2; + graph[0].insertTempEdge(e); + + e.clear(); + e.composition = "0"; + e.targetnode = 3; + graph[0].insertTempEdge(e); + + e.clear(); + e.composition = "0"; + e.targetnode = 4; + graph[0].insertTempEdge(e); + + e.clear(); + e.composition = "0"; + e.targetnode = 4; + + for (i = 1; i < (int)parameters->searchedmodifications.size(); i++) { + + if (parameters->searchedmodifications[i].nterminal) { + node.clear(); + node.setMZRatio(parameters->fragmentdefinitions[l1h_ion].massdifference + parameters->searchedmodifications[i].massdifference + negativeshift); + node.setIntensity(0); + node.addIonAnnotation(l1h_ion); + graph.push_back(node); + e.targetnode++; + graph[0].insertTempEdge(e); + + node.clear(); + node.setMZRatio(parameters->fragmentdefinitions[l2h_ion].massdifference + parameters->searchedmodifications[i].massdifference + negativeshift); + node.setIntensity(0); + node.addIonAnnotation(l2h_ion); + graph.push_back(node); + e.targetnode++; + graph[0].insertTempEdge(e); + } + + if (parameters->searchedmodifications[i].cterminal) { + node.clear(); + node.setMZRatio(parameters->fragmentdefinitions[l1oh_ion].massdifference + parameters->searchedmodifications[i].massdifference + negativeshift); + node.setIntensity(0); + node.addIonAnnotation(l1oh_ion); + graph.push_back(node); + e.targetnode++; + graph[0].insertTempEdge(e); + + node.clear(); + node.setMZRatio(parameters->fragmentdefinitions[l2oh_ion].massdifference + parameters->searchedmodifications[i].massdifference + negativeshift); + node.setIntensity(0); + node.addIonAnnotation(l2oh_ion); + graph.push_back(node); + e.targetnode++; + graph[0].insertTempEdge(e); + } + + } + + lastsystemnode = (int)graph.size() - 1; + startnode = 1; + break; + case cyclicpolyketide: + node.clear(); + node.setMZRatio(0); + node.setIntensity(0); + node.addIonAnnotation(l0h_ion); + node.addIonAnnotation(l1h_ion); + node.addIonAnnotation(l2h_ion); + graph.push_back(node); + + node.clear(); + node.setMZRatio(parameters->fragmentdefinitions[l0h_ion].massdifference + negativeshift); + node.setIntensity(0); + node.addIonAnnotation(l0h_ion); + graph.push_back(node); + + node.clear(); + node.setMZRatio(parameters->fragmentdefinitions[l1h_ion].massdifference + negativeshift); + node.setIntensity(0); + node.addIonAnnotation(l1h_ion); + graph.push_back(node); + + node.clear(); + node.setMZRatio(parameters->fragmentdefinitions[l2h_ion].massdifference + negativeshift); + node.setIntensity(0); + node.addIonAnnotation(l2h_ion); + graph.push_back(node); + + e.clear(); + e.composition = "0"; + e.targetnode = 1; + graph[0].insertTempEdge(e); + + e.clear(); + e.composition = "0"; + e.targetnode = 2; + graph[0].insertTempEdge(e); + + e.clear(); + e.composition = "0"; + e.targetnode = 3; + graph[0].insertTempEdge(e); + + lastsystemnode = (int)graph.size() - 1; + startnode = 1; + break; +#endif case linearpolysaccharide: node.clear(); node.setMZRatio(0); @@ -435,6 +583,13 @@ int cDeNovoGraph::createGraph(bool& terminatecomputation) { b.clear(); b.setMass(bricksdatabasewithcombinations.getMassOfComposition(combarray)); b.setComposition(compositionname, true); + +#if POLYKETIDE_SIDEROPHORES == 1 + if (((parameters->peptidetype == linearpolyketide) || (parameters->peptidetype == cyclicpolyketide)) && !bricksdatabasewithcombinations.checkPolyketideBlocks(b)) { + continue; + } +#endif + bricksdatabasewithcombinations.push_back(b); } @@ -467,37 +622,76 @@ int cDeNovoGraph::createGraph(bool& terminatecomputation) { for (int k = 0; k < (int)parameters->fragmentionsfordenovograph.size(); k++) { - if (/*!graph[i].ionannotation[parameters->fragmentionsfordenovograph[k]] ||*/ (parameters->fragmentionsfordenovograph[k] == precursor_ion)) { + if (/*!graph[i].ionannotation[parameters->fragmentionsfordenovograph[k]] ||*/ (parameters->fragmentionsfordenovograph[k] == precursor_ion) +#if POLYKETIDE_SIDEROPHORES == 1 + || (parameters->fragmentionsfordenovograph[k] == linear_polyketide_precursor_ion_h_h) || (parameters->fragmentionsfordenovograph[k] == linear_polyketide_precursor_ion_h_oh) + || (parameters->fragmentionsfordenovograph[k] == linear_polyketide_precursor_ion_oh_oh) || (parameters->fragmentionsfordenovograph[k] == cyclic_polyketide_precursor_ion) +#endif + ) { continue; } for (int m = 0; m < (int)parameters->fragmentionsfordenovograph.size(); m++) { // test for incompatible ion series - if ((!((parameters->fragmentdefinitions[parameters->fragmentionsfordenovograph[k]].nterminal == parameters->fragmentdefinitions[parameters->fragmentionsfordenovograph[m]].nterminal) && - (parameters->fragmentdefinitions[parameters->fragmentionsfordenovograph[k]].cterminal == parameters->fragmentdefinitions[parameters->fragmentionsfordenovograph[m]].cterminal))) && (parameters->fragmentionsfordenovograph[m] != precursor_ion)) { + if ( +#if POLYKETIDE_SIDEROPHORES == 1 + ( +#endif + !((parameters->fragmentdefinitions[parameters->fragmentionsfordenovograph[k]].nterminal == parameters->fragmentdefinitions[parameters->fragmentionsfordenovograph[m]].nterminal) && + (parameters->fragmentdefinitions[parameters->fragmentionsfordenovograph[k]].cterminal == parameters->fragmentdefinitions[parameters->fragmentionsfordenovograph[m]].cterminal)) +#if POLYKETIDE_SIDEROPHORES == 1 + || + (((parameters->peptidetype == linearpolyketide) || (parameters->peptidetype == cyclicpolyketide)) && + !(((parameters->fragmentionsfordenovograph[k] == l0h_ion) && (parameters->fragmentionsfordenovograph[m] == l0h_ion)) || ((parameters->fragmentionsfordenovograph[k] == l1h_ion) && (parameters->fragmentionsfordenovograph[m] == l1h_ion)) || ((parameters->fragmentionsfordenovograph[k] == l2h_ion) && (parameters->fragmentionsfordenovograph[m] == l2h_ion)) + || ((parameters->fragmentionsfordenovograph[k] == l1oh_ion) && (parameters->fragmentionsfordenovograph[m] == l1oh_ion)) || ((parameters->fragmentionsfordenovograph[k] == l2oh_ion) && (parameters->fragmentionsfordenovograph[m] == l2oh_ion)))) + ) + && (parameters->fragmentionsfordenovograph[m] != linear_polyketide_precursor_ion_h_h) && (parameters->fragmentionsfordenovograph[m] != linear_polyketide_precursor_ion_h_oh) + && (parameters->fragmentionsfordenovograph[m] != linear_polyketide_precursor_ion_oh_oh) && (parameters->fragmentionsfordenovograph[m] != cyclic_polyketide_precursor_ion) +#endif + && (parameters->fragmentionsfordenovograph[m] != precursor_ion) + ) { continue; } // terminal modifications for (int n = 0; n < (int)parameters->searchedmodifications.size(); n++) { - if ((n > 0) && (parameters->fragmentionsfordenovograph[m] != precursor_ion)) { + if ((n > 0) && (parameters->fragmentionsfordenovograph[m] != precursor_ion) +#if POLYKETIDE_SIDEROPHORES == 1 + && (parameters->fragmentionsfordenovograph[m] != linear_polyketide_precursor_ion_h_h) && (parameters->fragmentionsfordenovograph[m] != linear_polyketide_precursor_ion_h_oh) + && (parameters->fragmentionsfordenovograph[m] != linear_polyketide_precursor_ion_oh_oh) && (parameters->fragmentionsfordenovograph[m] != cyclic_polyketide_precursor_ion) +#endif + ) { continue; } // middle modifications for branched and branch-cyclic peptides for (int p = 0; p < (int)parameters->searchedmodifications.size(); p++) { - if ((p > 0) && ((parameters->peptidetype == linear) || (parameters->peptidetype == cyclic) || (parameters->peptidetype == linearpolysaccharide))) { + if ((p > 0) && ((parameters->peptidetype == linear) || (parameters->peptidetype == cyclic) || (parameters->peptidetype == linearpolysaccharide) +#if POLYKETIDE_SIDEROPHORES == 1 + || (parameters->peptidetype == linearpolyketide) || (parameters->peptidetype == cyclicpolyketide) +#endif + )) { continue; } termmass = 0; +#if POLYKETIDE_SIDEROPHORES == 1 + if ((parameters->fragmentionsfordenovograph[m] == precursor_ion) + || (parameters->fragmentionsfordenovograph[m] == linear_polyketide_precursor_ion_h_h) || (parameters->fragmentionsfordenovograph[m] == linear_polyketide_precursor_ion_h_oh) + || (parameters->fragmentionsfordenovograph[m] == linear_polyketide_precursor_ion_oh_oh) || (parameters->fragmentionsfordenovograph[m] == cyclic_polyketide_precursor_ion)) { +#else if (parameters->fragmentionsfordenovograph[m] == precursor_ion) { +#endif - if ((parameters->peptidetype == linear) || ((parameters->peptidetype == linearpolysaccharide))) { + if ((parameters->peptidetype == linear) || (parameters->peptidetype == linearpolysaccharide) +#if POLYKETIDE_SIDEROPHORES == 1 + || (parameters->peptidetype == linearpolyketide) +#endif + ) { if ((n > 0) && ((parameters->fragmentdefinitions[parameters->fragmentionsfordenovograph[k]].nterminal && parameters->searchedmodifications[n].nterminal) || (parameters->fragmentdefinitions[parameters->fragmentionsfordenovograph[k]].cterminal && parameters->searchedmodifications[n].cterminal))) { continue; } @@ -527,7 +721,7 @@ int cDeNovoGraph::createGraph(bool& terminatecomputation) { mass += parameters->fragmentdefinitions[parameters->fragmentionsfordenovograph[m]].massdifference; mass += termmass; - if ((parameters->peptidetype == branched) || (parameters->peptidetype == lasso)) { + if ((parameters->peptidetype == branched) || (parameters->peptidetype == branchcyclic)) { mass += parameters->searchedmodifications[p].massdifference; } @@ -572,7 +766,14 @@ int cDeNovoGraph::createGraph(bool& terminatecomputation) { } // irrelevant connection with precursor +#if POLYKETIDE_SIDEROPHORES == 1 + if (((parameters->fragmentionsfordenovograph[m] == precursor_ion) + || (parameters->fragmentionsfordenovograph[m] == linear_polyketide_precursor_ion_h_h) || (parameters->fragmentionsfordenovograph[m] == linear_polyketide_precursor_ion_h_oh) + || (parameters->fragmentionsfordenovograph[m] == linear_polyketide_precursor_ion_oh_oh) || (parameters->fragmentionsfordenovograph[m] == cyclic_polyketide_precursor_ion)) + && (middle != (int)graph.size() - 1) && (i != (int)graph.size() - 1)) { +#else if ((parameters->fragmentionsfordenovograph[m] == precursor_ion) && (middle != (int)graph.size() - 1) && (i != (int)graph.size() - 1)) { +#endif middle++; continue; } @@ -782,11 +983,12 @@ int cDeNovoGraph::connectEdgesFormingPathsNotFinishingInPrecursor(bool& terminat graph[i].insertEdge(e); edgescreated++; - // normal brick cBrick b; + string s = "unknown"; + + // normal brick b.setName(to_string(e.massdifference)); b.setAcronyms(to_string(e.massdifference)); - string s = "unknown"; b.setReferences(s); b.setSummary(s); b.setMass(e.massdifference); @@ -794,7 +996,7 @@ int cDeNovoGraph::connectEdgesFormingPathsNotFinishingInPrecursor(bool& terminat b.setArtificial(true); bricksdatabasewithcombinations.push_back(b); - // water loss brick + // -H2O brick (1) b.setName(to_string(e.massdifference - H2O)); b.setAcronyms(to_string(e.massdifference - H2O)); b.setReferences(s); @@ -803,6 +1005,80 @@ int cDeNovoGraph::connectEdgesFormingPathsNotFinishingInPrecursor(bool& terminat b.setComposition(to_string((int)bricksdatabasewithcombinations.size() + 1), false); b.setArtificial(true); bricksdatabasewithcombinations.push_back(b); + + // +H2O brick (2) + b.setName(to_string(e.massdifference + H2O)); + b.setAcronyms(to_string(e.massdifference + H2O)); + b.setReferences(s); + b.setSummary(s); + b.setMass(e.massdifference + H2O); + b.setComposition(to_string((int)bricksdatabasewithcombinations.size() + 1), false); + b.setArtificial(true); + bricksdatabasewithcombinations.push_back(b); + +#if POLYKETIDE_SIDEROPHORES == 1 + + // -H2 brick (3) + b.setName(to_string(e.massdifference - 2*H)); + b.setAcronyms(to_string(e.massdifference - 2*H)); + b.setReferences(s); + b.setSummary(s); + b.setMass(e.massdifference - 2*H); + b.setComposition(to_string((int)bricksdatabasewithcombinations.size() + 1), false); + b.setArtificial(true); + bricksdatabasewithcombinations.push_back(b); + + // -O brick (4) + b.setName(to_string(e.massdifference - O)); + b.setAcronyms(to_string(e.massdifference - O)); + b.setReferences(s); + b.setSummary(s); + b.setMass(e.massdifference - O); + b.setComposition(to_string((int)bricksdatabasewithcombinations.size() + 1), false); + b.setArtificial(true); + bricksdatabasewithcombinations.push_back(b); + + // +O brick (5) + b.setName(to_string(e.massdifference + O)); + b.setAcronyms(to_string(e.massdifference + O)); + b.setReferences(s); + b.setSummary(s); + b.setMass(e.massdifference + O); + b.setComposition(to_string((int)bricksdatabasewithcombinations.size() + 1), false); + b.setArtificial(true); + bricksdatabasewithcombinations.push_back(b); + + // -O2 brick (6) + b.setName(to_string(e.massdifference - 2*O)); + b.setAcronyms(to_string(e.massdifference - 2*O)); + b.setReferences(s); + b.setSummary(s); + b.setMass(e.massdifference - 2*O); + b.setComposition(to_string((int)bricksdatabasewithcombinations.size() + 1), false); + b.setArtificial(true); + bricksdatabasewithcombinations.push_back(b); + + // -H2+O brick (7) + b.setName(to_string(e.massdifference - 2*H + O)); + b.setAcronyms(to_string(e.massdifference - 2*H + O)); + b.setReferences(s); + b.setSummary(s); + b.setMass(e.massdifference - 2*H + O); + b.setComposition(to_string((int)bricksdatabasewithcombinations.size() + 1), false); + b.setArtificial(true); + bricksdatabasewithcombinations.push_back(b); + + // -H2O2 brick (8) + b.setName(to_string(e.massdifference - 2*H - 2*O)); + b.setAcronyms(to_string(e.massdifference - 2*H - 2*O)); + b.setReferences(s); + b.setSummary(s); + b.setMass(e.massdifference - 2*H - 2*O); + b.setComposition(to_string((int)bricksdatabasewithcombinations.size() + 1), false); + b.setArtificial(true); + bricksdatabasewithcombinations.push_back(b); + +#endif //} } @@ -1020,7 +1296,7 @@ string cDeNovoGraph::printGraph() { g += "ppm error: " + s2 + ", "; g += "source charge: " + to_string(graph[i][j].sourcecharge) + ", "; g += "target charge: " + to_string(graph[i][j].targetcharge); - if ((parameters->peptidetype == branched) || (parameters->peptidetype == lasso)) { + if ((parameters->peptidetype == branched) || (parameters->peptidetype == branchcyclic)) { if (graph[i][j].middlemodifID > 0) { g += ", branch modification: " + parameters->searchedmodifications[graph[i][j].middlemodifID].name; } diff --git a/CycloBranch/core/cDeNovoGraph.h b/CycloBranch/core/cDeNovoGraph.h index 045fecf..90ab09b 100644 --- a/CycloBranch/core/cDeNovoGraph.h +++ b/CycloBranch/core/cDeNovoGraph.h @@ -14,7 +14,7 @@ #include "core/cParameters.h" #include "core/cDeNovoGraphNode.h" -#include "core/cPeaksList.h" +#include "core/cPeakListSeries.h" #include "core/cBricksDatabase.h" #include "core/cTheoreticalSpectrumList.h" #include "parallel/cGraphReaderThread.h" diff --git a/CycloBranch/core/cDeNovoGraphNode.cpp b/CycloBranch/core/cDeNovoGraphNode.cpp index 453b7e5..b75dd92 100644 --- a/CycloBranch/core/cDeNovoGraphNode.cpp +++ b/CycloBranch/core/cDeNovoGraphNode.cpp @@ -40,7 +40,7 @@ void cDeNovoGraphNode::clear() { for (int i = 0; i < fragmentIonTypeEnd; i++) { - ionannotation[(fragmentIonType)i] = false; + ionannotation[(eFragmentIonType)i] = false; } tempedges.clear(); @@ -117,12 +117,12 @@ void cDeNovoGraphNode::clearEdges() { } -void cDeNovoGraphNode::addIonAnnotation(fragmentIonType iontype) { +void cDeNovoGraphNode::addIonAnnotation(eFragmentIonType iontype) { ionannotation[iontype] = true; } -bool cDeNovoGraphNode::checkIonAnnotation(fragmentIonType iontype) { +bool cDeNovoGraphNode::checkIonAnnotation(eFragmentIonType iontype) { return ionannotation[iontype]; } diff --git a/CycloBranch/core/cDeNovoGraphNode.h b/CycloBranch/core/cDeNovoGraphNode.h index 4dda405..ffdf579 100644 --- a/CycloBranch/core/cDeNovoGraphNode.h +++ b/CycloBranch/core/cDeNovoGraphNode.h @@ -196,7 +196,7 @@ class cDeNovoGraphNode { unordered_set tempedges; vector edges; - map ionannotation; + map ionannotation; public: @@ -305,7 +305,7 @@ class cDeNovoGraphNode { \brief Add ion annotation. \param iontype type of fragment ion */ - void addIonAnnotation(fragmentIonType iontype); + void addIonAnnotation(eFragmentIonType iontype); /** @@ -313,7 +313,7 @@ class cDeNovoGraphNode { \param iontype type of fragment ion \retval bool true when the ion type was assigned to the node */ - bool checkIonAnnotation(fragmentIonType iontype); + bool checkIonAnnotation(eFragmentIonType iontype); }; diff --git a/CycloBranch/core/cFragmentIons.cpp b/CycloBranch/core/cFragmentIons.cpp index 9d38635..dc09253 100644 --- a/CycloBranch/core/cFragmentIons.cpp +++ b/CycloBranch/core/cFragmentIons.cpp @@ -137,9 +137,10 @@ void fragmentDescription::store(ofstream& os) { os.write((char *)&massdifference, sizeof(double)); os.write((char *)&nterminal, sizeof(bool)); os.write((char *)&cterminal, sizeof(bool)); - os.write((char *)&parent, sizeof(fragmentIonType)); + os.write((char *)&parent, sizeof(eFragmentIonType)); os.write((char *)&positive, sizeof(bool)); os.write((char *)&multiplier, sizeof(int)); + os.write((char *)&numberofisotopes, sizeof(int)); } @@ -149,9 +150,10 @@ void fragmentDescription::load(ifstream& is) { is.read((char *)&massdifference, sizeof(double)); is.read((char *)&nterminal, sizeof(bool)); is.read((char *)&cterminal, sizeof(bool)); - is.read((char *)&parent, sizeof(fragmentIonType)); + is.read((char *)&parent, sizeof(eFragmentIonType)); is.read((char *)&positive, sizeof(bool)); is.read((char *)&multiplier, sizeof(int)); + is.read((char *)&numberofisotopes, sizeof(int)); } @@ -166,7 +168,7 @@ cFragmentIons::cFragmentIons(bool cyclicnterminus, bool cycliccterminus, string& } -fragmentDescription &cFragmentIons::operator[](fragmentIonType iontype) { +fragmentDescription &cFragmentIons::operator[](eFragmentIonType iontype) { return fragmentions[iontype]; } @@ -515,6 +517,7 @@ void cFragmentIons::recalculateFragments(bool cyclicnterminus, bool cyclicctermi fragmentions[ms_hplus].parent = ms_hplus; fragmentions[ms_hplus].positive = true; fragmentions[ms_hplus].multiplier = 1; + fragmentions[ms_hplus].numberofisotopes = 0; // initialize ion Na+ fragmentions[ms_naplus].nterminal = true; @@ -524,6 +527,7 @@ void cFragmentIons::recalculateFragments(bool cyclicnterminus, bool cyclicctermi fragmentions[ms_naplus].parent = ms_naplus; fragmentions[ms_naplus].positive = true; fragmentions[ms_naplus].multiplier = 1; + fragmentions[ms_naplus].numberofisotopes = 0; // initialize ion K+ fragmentions[ms_kplus].nterminal = true; @@ -533,6 +537,7 @@ void cFragmentIons::recalculateFragments(bool cyclicnterminus, bool cyclicctermi fragmentions[ms_kplus].parent = ms_kplus; fragmentions[ms_kplus].positive = true; fragmentions[ms_kplus].multiplier = 1; + fragmentions[ms_kplus].numberofisotopes = 1; // initialize ion H- fragmentions[ms_hminus].nterminal = true; @@ -542,33 +547,7 @@ void cFragmentIons::recalculateFragments(bool cyclicnterminus, bool cyclicctermi fragmentions[ms_hminus].parent = ms_hminus; fragmentions[ms_hminus].positive = false; fragmentions[ms_hminus].multiplier = 1; - - // initialize ion [3M+2Fe-5H]+ - fragmentions[ms_3M2Fe5H].nterminal = true; - fragmentions[ms_3M2Fe5H].cterminal = true; - fragmentions[ms_3M2Fe5H].name = "[3M+2Fe-5H]+"; - fragmentions[ms_3M2Fe5H].massdifference = 2*Fe - 6*H + Hplus; - fragmentions[ms_3M2Fe5H].parent = ms_3M2Fe5H; - fragmentions[ms_3M2Fe5H].positive = true; - fragmentions[ms_3M2Fe5H].multiplier = 3; - - // initialize ion [2M+Fe-2H]+ - fragmentions[ms_2MFe2H].nterminal = true; - fragmentions[ms_2MFe2H].cterminal = true; - fragmentions[ms_2MFe2H].name = "[2M+Fe-2H]+"; - fragmentions[ms_2MFe2H].massdifference = Fe - 3*H + Hplus; - fragmentions[ms_2MFe2H].parent = ms_2MFe2H; - fragmentions[ms_2MFe2H].positive = true; - fragmentions[ms_2MFe2H].multiplier = 2; - - // initialize ion [3M+Fe-2H]+ - fragmentions[ms_3MFe2H].nterminal = true; - fragmentions[ms_3MFe2H].cterminal = true; - fragmentions[ms_3MFe2H].name = "[3M+Fe-2H]+"; - fragmentions[ms_3MFe2H].massdifference = Fe - 3*H + Hplus; - fragmentions[ms_3MFe2H].parent = ms_3MFe2H; - fragmentions[ms_3MFe2H].positive = true; - fragmentions[ms_3MFe2H].multiplier = 3; + fragmentions[ms_hminus].numberofisotopes = 0; // initialize ion [M+Fe-2H]+ fragmentions[ms_MFe2H].nterminal = true; @@ -578,15 +557,27 @@ void cFragmentIons::recalculateFragments(bool cyclicnterminus, bool cyclicctermi fragmentions[ms_MFe2H].parent = ms_MFe2H; fragmentions[ms_MFe2H].positive = true; fragmentions[ms_MFe2H].multiplier = 1; + fragmentions[ms_MFe2H].numberofisotopes = 1; - // initialize ion [3M+2Fe-6H+Na]+ - fragmentions[ms_3M2Fe6HNa].nterminal = true; - fragmentions[ms_3M2Fe6HNa].cterminal = true; - fragmentions[ms_3M2Fe6HNa].name = "[3M+2Fe-6H+Na]+"; - fragmentions[ms_3M2Fe6HNa].massdifference = 2*Fe - 6*H + Naplus; - fragmentions[ms_3M2Fe6HNa].parent = ms_3M2Fe6HNa; - fragmentions[ms_3M2Fe6HNa].positive = true; - fragmentions[ms_3M2Fe6HNa].multiplier = 3; + // initialize ion [M+Fe-3H+Na]+ + fragmentions[ms_MFe3HNa].nterminal = true; + fragmentions[ms_MFe3HNa].cterminal = true; + fragmentions[ms_MFe3HNa].name = "[M+Fe-3H+Na]+"; + fragmentions[ms_MFe3HNa].massdifference = Fe - 3*H + Naplus; + fragmentions[ms_MFe3HNa].parent = ms_MFe3HNa; + fragmentions[ms_MFe3HNa].positive = true; + fragmentions[ms_MFe3HNa].multiplier = 1; + fragmentions[ms_MFe3HNa].numberofisotopes = 1; + + // initialize ion [2M+Fe-2H]+ + fragmentions[ms_2MFe2H].nterminal = true; + fragmentions[ms_2MFe2H].cterminal = true; + fragmentions[ms_2MFe2H].name = "[2M+Fe-2H]+"; + fragmentions[ms_2MFe2H].massdifference = Fe - 3*H + Hplus; + fragmentions[ms_2MFe2H].parent = ms_2MFe2H; + fragmentions[ms_2MFe2H].positive = true; + fragmentions[ms_2MFe2H].multiplier = 2; + fragmentions[ms_2MFe2H].numberofisotopes = 1; // initialize ion [2M+Fe-3H+Na]+ fragmentions[ms_2MFe3HNa].nterminal = true; @@ -596,6 +587,17 @@ void cFragmentIons::recalculateFragments(bool cyclicnterminus, bool cyclicctermi fragmentions[ms_2MFe3HNa].parent = ms_2MFe3HNa; fragmentions[ms_2MFe3HNa].positive = true; fragmentions[ms_2MFe3HNa].multiplier = 2; + fragmentions[ms_2MFe3HNa].numberofisotopes = 1; + + // initialize ion [3M+Fe-2H]+ + fragmentions[ms_3MFe2H].nterminal = true; + fragmentions[ms_3MFe2H].cterminal = true; + fragmentions[ms_3MFe2H].name = "[3M+Fe-2H]+"; + fragmentions[ms_3MFe2H].massdifference = Fe - 3*H + Hplus; + fragmentions[ms_3MFe2H].parent = ms_3MFe2H; + fragmentions[ms_3MFe2H].positive = true; + fragmentions[ms_3MFe2H].multiplier = 3; + fragmentions[ms_3MFe2H].numberofisotopes = 1; // initialize ion [3M+Fe-3H+Na]+ fragmentions[ms_3MFe3HNa].nterminal = true; @@ -605,24 +607,37 @@ void cFragmentIons::recalculateFragments(bool cyclicnterminus, bool cyclicctermi fragmentions[ms_3MFe3HNa].parent = ms_3MFe3HNa; fragmentions[ms_3MFe3HNa].positive = true; fragmentions[ms_3MFe3HNa].multiplier = 3; + fragmentions[ms_3MFe3HNa].numberofisotopes = 1; - // initialize ion [M+Fe-3H+Na]+ - fragmentions[ms_MFe3HNa].nterminal = true; - fragmentions[ms_MFe3HNa].cterminal = true; - fragmentions[ms_MFe3HNa].name = "[M+Fe-3H+Na]+"; - fragmentions[ms_MFe3HNa].massdifference = Fe - 3*H + Naplus; - fragmentions[ms_MFe3HNa].parent = ms_MFe3HNa; - fragmentions[ms_MFe3HNa].positive = true; - fragmentions[ms_MFe3HNa].multiplier = 1; + // initialize ion [3M+2Fe-5H]+ + fragmentions[ms_3M2Fe5H].nterminal = true; + fragmentions[ms_3M2Fe5H].cterminal = true; + fragmentions[ms_3M2Fe5H].name = "[3M+2Fe-5H]+"; + fragmentions[ms_3M2Fe5H].massdifference = 2*Fe - 6*H + Hplus; + fragmentions[ms_3M2Fe5H].parent = ms_3M2Fe5H; + fragmentions[ms_3M2Fe5H].positive = true; + fragmentions[ms_3M2Fe5H].multiplier = 3; + fragmentions[ms_3M2Fe5H].numberofisotopes = 1; - // initialize ion [3M+2Fe-7H]- - fragmentions[ms_3M2Fe7H].nterminal = true; - fragmentions[ms_3M2Fe7H].cterminal = true; - fragmentions[ms_3M2Fe7H].name = "[3M+2Fe-7H]-"; - fragmentions[ms_3M2Fe7H].massdifference = 2*Fe - 6*H - Hplus; - fragmentions[ms_3M2Fe7H].parent = ms_3M2Fe7H; - fragmentions[ms_3M2Fe7H].positive = false; - fragmentions[ms_3M2Fe7H].multiplier = 3; + // initialize ion [3M+2Fe-6H+Na]+ + fragmentions[ms_3M2Fe6HNa].nterminal = true; + fragmentions[ms_3M2Fe6HNa].cterminal = true; + fragmentions[ms_3M2Fe6HNa].name = "[3M+2Fe-6H+Na]+"; + fragmentions[ms_3M2Fe6HNa].massdifference = 2*Fe - 6*H + Naplus; + fragmentions[ms_3M2Fe6HNa].parent = ms_3M2Fe6HNa; + fragmentions[ms_3M2Fe6HNa].positive = true; + fragmentions[ms_3M2Fe6HNa].multiplier = 3; + fragmentions[ms_3M2Fe6HNa].numberofisotopes = 1; + + // initialize ion [M+Fe-4H]- + fragmentions[ms_MFe4H].nterminal = true; + fragmentions[ms_MFe4H].cterminal = true; + fragmentions[ms_MFe4H].name = "[M+Fe-4H]-"; + fragmentions[ms_MFe4H].massdifference = Fe - 3*H - Hplus; + fragmentions[ms_MFe4H].parent = ms_MFe4H; + fragmentions[ms_MFe4H].positive = false; + fragmentions[ms_MFe4H].multiplier = 1; + fragmentions[ms_MFe4H].numberofisotopes = 1; // initialize ion [2M+Fe-4H]- fragmentions[ms_2MFe4H].nterminal = true; @@ -632,6 +647,7 @@ void cFragmentIons::recalculateFragments(bool cyclicnterminus, bool cyclicctermi fragmentions[ms_2MFe4H].parent = ms_2MFe4H; fragmentions[ms_2MFe4H].positive = false; fragmentions[ms_2MFe4H].multiplier = 2; + fragmentions[ms_2MFe4H].numberofisotopes = 1; // initialize ion [3M+Fe-4H]- fragmentions[ms_3MFe4H].nterminal = true; @@ -641,15 +657,1028 @@ void cFragmentIons::recalculateFragments(bool cyclicnterminus, bool cyclicctermi fragmentions[ms_3MFe4H].parent = ms_3MFe4H; fragmentions[ms_3MFe4H].positive = false; fragmentions[ms_3MFe4H].multiplier = 3; + fragmentions[ms_3MFe4H].numberofisotopes = 1; - // initialize ion [M+Fe-4H]- - fragmentions[ms_MFe4H].nterminal = true; - fragmentions[ms_MFe4H].cterminal = true; - fragmentions[ms_MFe4H].name = "[M+Fe-4H]-"; - fragmentions[ms_MFe4H].massdifference = Fe - 3*H - Hplus; - fragmentions[ms_MFe4H].parent = ms_MFe4H; - fragmentions[ms_MFe4H].positive = false; - fragmentions[ms_MFe4H].multiplier = 1; + // initialize ion [3M+2Fe-7H]- + fragmentions[ms_3M2Fe7H].nterminal = true; + fragmentions[ms_3M2Fe7H].cterminal = true; + fragmentions[ms_3M2Fe7H].name = "[3M+2Fe-7H]-"; + fragmentions[ms_3M2Fe7H].massdifference = 2*Fe - 6*H - Hplus; + fragmentions[ms_3M2Fe7H].parent = ms_3M2Fe7H; + fragmentions[ms_3M2Fe7H].positive = false; + fragmentions[ms_3M2Fe7H].multiplier = 3; + fragmentions[ms_3M2Fe7H].numberofisotopes = 1; + + // initialize ion Li+ + fragmentions[ms_liplus].nterminal = true; + fragmentions[ms_liplus].cterminal = true; + fragmentions[ms_liplus].name = "[M+Li]+"; + fragmentions[ms_liplus].massdifference = Liplus; + fragmentions[ms_liplus].parent = ms_liplus; + fragmentions[ms_liplus].positive = true; + fragmentions[ms_liplus].multiplier = 1; + fragmentions[ms_liplus].numberofisotopes = 1; + + // initialize ion [M+Mg-H]+ + fragmentions[ms_MMgH].nterminal = true; + fragmentions[ms_MMgH].cterminal = true; + fragmentions[ms_MMgH].name = "[M+Mg-H]+"; + fragmentions[ms_MMgH].massdifference = Mg - 2*H + Hplus; + fragmentions[ms_MMgH].parent = ms_MMgH; + fragmentions[ms_MMgH].positive = true; + fragmentions[ms_MMgH].multiplier = 1; + fragmentions[ms_MMgH].numberofisotopes = 2; + + // initialize ion [M+Mg-2H+Na]+ + fragmentions[ms_MMg2HNa].nterminal = true; + fragmentions[ms_MMg2HNa].cterminal = true; + fragmentions[ms_MMg2HNa].name = "[M+Mg-2H+Na]+"; + fragmentions[ms_MMg2HNa].massdifference = Mg - 2*H + Naplus; + fragmentions[ms_MMg2HNa].parent = ms_MMg2HNa; + fragmentions[ms_MMg2HNa].positive = true; + fragmentions[ms_MMg2HNa].multiplier = 1; + fragmentions[ms_MMg2HNa].numberofisotopes = 2; + + // initialize ion [M+Mg-3H]- + fragmentions[ms_MMg3H].nterminal = true; + fragmentions[ms_MMg3H].cterminal = true; + fragmentions[ms_MMg3H].name = "[M+Mg-3H]-"; + fragmentions[ms_MMg3H].massdifference = Mg - 2*H - Hplus; + fragmentions[ms_MMg3H].parent = ms_MMg3H; + fragmentions[ms_MMg3H].positive = false; + fragmentions[ms_MMg3H].multiplier = 1; + fragmentions[ms_MMg3H].numberofisotopes = 2; + + // initialize ion [M+Al-2H]+ + fragmentions[ms_MAl2H].nterminal = true; + fragmentions[ms_MAl2H].cterminal = true; + fragmentions[ms_MAl2H].name = "[M+Al-2H]+"; + fragmentions[ms_MAl2H].massdifference = Al - 3*H + Hplus; + fragmentions[ms_MAl2H].parent = ms_MAl2H; + fragmentions[ms_MAl2H].positive = true; + fragmentions[ms_MAl2H].multiplier = 1; + fragmentions[ms_MAl2H].numberofisotopes = 0; + + // initialize ion [M+Al-3H+Na]+ + fragmentions[ms_MAl3HNa].nterminal = true; + fragmentions[ms_MAl3HNa].cterminal = true; + fragmentions[ms_MAl3HNa].name = "[M+Al-3H+Na]+"; + fragmentions[ms_MAl3HNa].massdifference = Al - 3*H + Naplus; + fragmentions[ms_MAl3HNa].parent = ms_MAl3HNa; + fragmentions[ms_MAl3HNa].positive = true; + fragmentions[ms_MAl3HNa].multiplier = 1; + fragmentions[ms_MAl3HNa].numberofisotopes = 0; + + // initialize ion [M+Al-4H]- + fragmentions[ms_MAl4H].nterminal = true; + fragmentions[ms_MAl4H].cterminal = true; + fragmentions[ms_MAl4H].name = "[M+Al-4H]-"; + fragmentions[ms_MAl4H].massdifference = Al - 3*H - Hplus; + fragmentions[ms_MAl4H].parent = ms_MAl4H; + fragmentions[ms_MAl4H].positive = false; + fragmentions[ms_MAl4H].multiplier = 1; + fragmentions[ms_MAl4H].numberofisotopes = 0; + + // initialize ion [M+Ca-H]+ + fragmentions[ms_MCaH].nterminal = true; + fragmentions[ms_MCaH].cterminal = true; + fragmentions[ms_MCaH].name = "[M+Ca-H]+"; + fragmentions[ms_MCaH].massdifference = Ca - 2*H + Hplus; + fragmentions[ms_MCaH].parent = ms_MCaH; + fragmentions[ms_MCaH].positive = true; + fragmentions[ms_MCaH].multiplier = 1; + fragmentions[ms_MCaH].numberofisotopes = 1; + + // initialize ion [M+Ca-2H+Na]+ + fragmentions[ms_MCa2HNa].nterminal = true; + fragmentions[ms_MCa2HNa].cterminal = true; + fragmentions[ms_MCa2HNa].name = "[M+Ca-2H+Na]+"; + fragmentions[ms_MCa2HNa].massdifference = Ca - 2*H + Naplus; + fragmentions[ms_MCa2HNa].parent = ms_MCa2HNa; + fragmentions[ms_MCa2HNa].positive = true; + fragmentions[ms_MCa2HNa].multiplier = 1; + fragmentions[ms_MCa2HNa].numberofisotopes = 1; + + // initialize ion [M+Ca-3H]- + fragmentions[ms_MCa3H].nterminal = true; + fragmentions[ms_MCa3H].cterminal = true; + fragmentions[ms_MCa3H].name = "[M+Ca-3H]-"; + fragmentions[ms_MCa3H].massdifference = Ca - 2*H - Hplus; + fragmentions[ms_MCa3H].parent = ms_MCa3H; + fragmentions[ms_MCa3H].positive = false; + fragmentions[ms_MCa3H].multiplier = 1; + fragmentions[ms_MCa3H].numberofisotopes = 1; + + // initialize ion [M+Mn-H]+ + fragmentions[ms_MMnH].nterminal = true; + fragmentions[ms_MMnH].cterminal = true; + fragmentions[ms_MMnH].name = "[M+Mn-H]+"; + fragmentions[ms_MMnH].massdifference = Mn - 2*H + Hplus; + fragmentions[ms_MMnH].parent = ms_MMnH; + fragmentions[ms_MMnH].positive = true; + fragmentions[ms_MMnH].multiplier = 1; + fragmentions[ms_MMnH].numberofisotopes = 0; + + // initialize ion [M+Mn-2H+Na]+ + fragmentions[ms_MMn2HNa].nterminal = true; + fragmentions[ms_MMn2HNa].cterminal = true; + fragmentions[ms_MMn2HNa].name = "[M+Mn-2H+Na]+"; + fragmentions[ms_MMn2HNa].massdifference = Mn - 2*H + Naplus; + fragmentions[ms_MMn2HNa].parent = ms_MMn2HNa; + fragmentions[ms_MMn2HNa].positive = true; + fragmentions[ms_MMn2HNa].multiplier = 1; + fragmentions[ms_MMn2HNa].numberofisotopes = 0; + + // initialize ion [M+Mn-3H]- + fragmentions[ms_MMn3H].nterminal = true; + fragmentions[ms_MMn3H].cterminal = true; + fragmentions[ms_MMn3H].name = "[M+Mn-3H]-"; + fragmentions[ms_MMn3H].massdifference = Mn - 2*H - Hplus; + fragmentions[ms_MMn3H].parent = ms_MMn3H; + fragmentions[ms_MMn3H].positive = false; + fragmentions[ms_MMn3H].multiplier = 1; + fragmentions[ms_MMn3H].numberofisotopes = 0; + + // initialize ion [M+Co-H]+ + fragmentions[ms_MCoH].nterminal = true; + fragmentions[ms_MCoH].cterminal = true; + fragmentions[ms_MCoH].name = "[M+Co-H]+"; + fragmentions[ms_MCoH].massdifference = Co - 2*H + Hplus; + fragmentions[ms_MCoH].parent = ms_MCoH; + fragmentions[ms_MCoH].positive = true; + fragmentions[ms_MCoH].multiplier = 1; + fragmentions[ms_MCoH].numberofisotopes = 0; + + // initialize ion [M+Co-2H+Na]+ + fragmentions[ms_MCo2HNa].nterminal = true; + fragmentions[ms_MCo2HNa].cterminal = true; + fragmentions[ms_MCo2HNa].name = "[M+Co-2H+Na]+"; + fragmentions[ms_MCo2HNa].massdifference = Co - 2*H + Naplus; + fragmentions[ms_MCo2HNa].parent = ms_MCo2HNa; + fragmentions[ms_MCo2HNa].positive = true; + fragmentions[ms_MCo2HNa].multiplier = 1; + fragmentions[ms_MCo2HNa].numberofisotopes = 0; + + // initialize ion [M+Co-3H]- + fragmentions[ms_MCo3H].nterminal = true; + fragmentions[ms_MCo3H].cterminal = true; + fragmentions[ms_MCo3H].name = "[M+Co-3H]-"; + fragmentions[ms_MCo3H].massdifference = Co - 2*H - Hplus; + fragmentions[ms_MCo3H].parent = ms_MCo3H; + fragmentions[ms_MCo3H].positive = false; + fragmentions[ms_MCo3H].multiplier = 1; + fragmentions[ms_MCo3H].numberofisotopes = 0; + + // initialize ion [M+Ni-H]+ + fragmentions[ms_MNiH].nterminal = true; + fragmentions[ms_MNiH].cterminal = true; + fragmentions[ms_MNiH].name = "[M+Ni-H]+"; + fragmentions[ms_MNiH].massdifference = Ni - 2*H + Hplus; + fragmentions[ms_MNiH].parent = ms_MNiH; + fragmentions[ms_MNiH].positive = true; + fragmentions[ms_MNiH].multiplier = 1; + fragmentions[ms_MNiH].numberofisotopes = 3; + + // initialize ion [M+Ni-2H+Na]+ + fragmentions[ms_MNi2HNa].nterminal = true; + fragmentions[ms_MNi2HNa].cterminal = true; + fragmentions[ms_MNi2HNa].name = "[M+Ni-2H+Na]+"; + fragmentions[ms_MNi2HNa].massdifference = Ni - 2*H + Naplus; + fragmentions[ms_MNi2HNa].parent = ms_MNi2HNa; + fragmentions[ms_MNi2HNa].positive = true; + fragmentions[ms_MNi2HNa].multiplier = 1; + fragmentions[ms_MNi2HNa].numberofisotopes = 3; + + // initialize ion [M+Ni-3H]- + fragmentions[ms_MNi3H].nterminal = true; + fragmentions[ms_MNi3H].cterminal = true; + fragmentions[ms_MNi3H].name = "[M+Ni-3H]-"; + fragmentions[ms_MNi3H].massdifference = Ni - 2*H - Hplus; + fragmentions[ms_MNi3H].parent = ms_MNi3H; + fragmentions[ms_MNi3H].positive = false; + fragmentions[ms_MNi3H].multiplier = 1; + fragmentions[ms_MNi3H].numberofisotopes = 3; + + // initialize ion [M+Cu-H]+ + fragmentions[ms_MCuH].nterminal = true; + fragmentions[ms_MCuH].cterminal = true; + fragmentions[ms_MCuH].name = "[M+Cu-H]+"; + fragmentions[ms_MCuH].massdifference = Cu - 2*H + Hplus; + fragmentions[ms_MCuH].parent = ms_MCuH; + fragmentions[ms_MCuH].positive = true; + fragmentions[ms_MCuH].multiplier = 1; + fragmentions[ms_MCuH].numberofisotopes = 1; + + // initialize ion [M+Cu-2H+Na]+ + fragmentions[ms_MCu2HNa].nterminal = true; + fragmentions[ms_MCu2HNa].cterminal = true; + fragmentions[ms_MCu2HNa].name = "[M+Cu-2H+Na]+"; + fragmentions[ms_MCu2HNa].massdifference = Cu - 2*H + Naplus; + fragmentions[ms_MCu2HNa].parent = ms_MCu2HNa; + fragmentions[ms_MCu2HNa].positive = true; + fragmentions[ms_MCu2HNa].multiplier = 1; + fragmentions[ms_MCu2HNa].numberofisotopes = 1; + + // initialize ion [M+Cu-3H]- + fragmentions[ms_MCu3H].nterminal = true; + fragmentions[ms_MCu3H].cterminal = true; + fragmentions[ms_MCu3H].name = "[M+Cu-3H]-"; + fragmentions[ms_MCu3H].massdifference = Cu - 2*H - Hplus; + fragmentions[ms_MCu3H].parent = ms_MCu3H; + fragmentions[ms_MCu3H].positive = false; + fragmentions[ms_MCu3H].multiplier = 1; + fragmentions[ms_MCu3H].numberofisotopes = 1; + + // initialize ion [M+Zn-H]+ + fragmentions[ms_MZnH].nterminal = true; + fragmentions[ms_MZnH].cterminal = true; + fragmentions[ms_MZnH].name = "[M+Zn-H]+"; + fragmentions[ms_MZnH].massdifference = Zn - 2*H + Hplus; + fragmentions[ms_MZnH].parent = ms_MZnH; + fragmentions[ms_MZnH].positive = true; + fragmentions[ms_MZnH].multiplier = 1; + fragmentions[ms_MZnH].numberofisotopes = 3; + + // initialize ion [M+Zn-2H+Na]+ + fragmentions[ms_MZn2HNa].nterminal = true; + fragmentions[ms_MZn2HNa].cterminal = true; + fragmentions[ms_MZn2HNa].name = "[M+Zn-2H+Na]+"; + fragmentions[ms_MZn2HNa].massdifference = Zn - 2*H + Naplus; + fragmentions[ms_MZn2HNa].parent = ms_MZn2HNa; + fragmentions[ms_MZn2HNa].positive = true; + fragmentions[ms_MZn2HNa].multiplier = 1; + fragmentions[ms_MZn2HNa].numberofisotopes = 3; + + // initialize ion [M+Zn-3H]- + fragmentions[ms_MZn3H].nterminal = true; + fragmentions[ms_MZn3H].cterminal = true; + fragmentions[ms_MZn3H].name = "[M+Zn-3H]-"; + fragmentions[ms_MZn3H].massdifference = Zn - 2*H - Hplus; + fragmentions[ms_MZn3H].parent = ms_MZn3H; + fragmentions[ms_MZn3H].positive = false; + fragmentions[ms_MZn3H].multiplier = 1; + fragmentions[ms_MZn3H].numberofisotopes = 3; + + // initialize ion [M+Ga-2H]+ + fragmentions[ms_MGa2H].nterminal = true; + fragmentions[ms_MGa2H].cterminal = true; + fragmentions[ms_MGa2H].name = "[M+Ga-2H]+"; + fragmentions[ms_MGa2H].massdifference = Ga - 3*H + Hplus; + fragmentions[ms_MGa2H].parent = ms_MGa2H; + fragmentions[ms_MGa2H].positive = true; + fragmentions[ms_MGa2H].multiplier = 1; + fragmentions[ms_MGa2H].numberofisotopes = 1; + + // initialize ion [M+Ga-3H+Na]+ + fragmentions[ms_MGa3HNa].nterminal = true; + fragmentions[ms_MGa3HNa].cterminal = true; + fragmentions[ms_MGa3HNa].name = "[M+Ga-3H+Na]+"; + fragmentions[ms_MGa3HNa].massdifference = Ga - 3*H + Naplus; + fragmentions[ms_MGa3HNa].parent = ms_MGa3HNa; + fragmentions[ms_MGa3HNa].positive = true; + fragmentions[ms_MGa3HNa].multiplier = 1; + fragmentions[ms_MGa3HNa].numberofisotopes = 1; + + // initialize ion [M+Ga-4H]- + fragmentions[ms_MGa4H].nterminal = true; + fragmentions[ms_MGa4H].cterminal = true; + fragmentions[ms_MGa4H].name = "[M+Ga-4H]-"; + fragmentions[ms_MGa4H].massdifference = Ga - 3*H - Hplus; + fragmentions[ms_MGa4H].parent = ms_MGa4H; + fragmentions[ms_MGa4H].positive = false; + fragmentions[ms_MGa4H].multiplier = 1; + fragmentions[ms_MGa4H].numberofisotopes = 1; + +#if POLYKETIDE_SIDEROPHORES == 1 + + // initialize l0h ion + fragmentions[l0h_ion].nterminal = true; + fragmentions[l0h_ion].cterminal = false; + fragmentions[l0h_ion].name = "LB-2H"; + fragmentions[l0h_ion].massdifference = L0H_ION + adductshift; + fragmentions[l0h_ion].parent = l0h_ion; + + // initialize l0h-H2O ion + fragmentions[l0h_ion_dehydrated].nterminal = true; + fragmentions[l0h_ion_dehydrated].cterminal = false; + fragmentions[l0h_ion_dehydrated].name = "LB*-2H"; + fragmentions[l0h_ion_dehydrated].massdifference = L0H_ION - H2O + adductshift; + fragmentions[l0h_ion_dehydrated].parent = l0h_ion; + + // initialize l0h-NH3 ion + fragmentions[l0h_ion_deamidated].nterminal = true; + fragmentions[l0h_ion_deamidated].cterminal = false; + fragmentions[l0h_ion_deamidated].name = "LBx-2H"; + fragmentions[l0h_ion_deamidated].massdifference = L0H_ION - NH3 + adductshift; + fragmentions[l0h_ion_deamidated].parent = l0h_ion; + + // initialize l0h-H2O-NH3 ion + fragmentions[l0h_ion_dehydrated_and_deamidated].nterminal = true; + fragmentions[l0h_ion_dehydrated_and_deamidated].cterminal = false; + fragmentions[l0h_ion_dehydrated_and_deamidated].name = "LB*x-2H"; + fragmentions[l0h_ion_dehydrated_and_deamidated].massdifference = L0H_ION - H2O - NH3 + adductshift; + fragmentions[l0h_ion_dehydrated_and_deamidated].parent = l0h_ion; + + // initialize l0h-CO ion + fragmentions[l0h_ion_co_loss].nterminal = true; + fragmentions[l0h_ion_co_loss].cterminal = false; + fragmentions[l0h_ion_co_loss].name = "LA-2H"; + fragmentions[l0h_ion_co_loss].massdifference = L0H_ION - CO + adductshift; + fragmentions[l0h_ion_co_loss].parent = l0h_ion; + + // initialize l0h-CO-H2O ion + fragmentions[l0h_ion_co_loss_dehydrated].nterminal = true; + fragmentions[l0h_ion_co_loss_dehydrated].cterminal = false; + fragmentions[l0h_ion_co_loss_dehydrated].name = "LA*-2H"; + fragmentions[l0h_ion_co_loss_dehydrated].massdifference = L0H_ION - CO - H2O + adductshift; + fragmentions[l0h_ion_co_loss_dehydrated].parent = l0h_ion; + + // initialize l0h-CO-NH3 ion + fragmentions[l0h_ion_co_loss_deamidated].nterminal = true; + fragmentions[l0h_ion_co_loss_deamidated].cterminal = false; + fragmentions[l0h_ion_co_loss_deamidated].name = "LAx-2H"; + fragmentions[l0h_ion_co_loss_deamidated].massdifference = L0H_ION - CO - NH3 + adductshift; + fragmentions[l0h_ion_co_loss_deamidated].parent = l0h_ion; + + // initialize l0h-CO-H2O-NH3 ion + fragmentions[l0h_ion_co_loss_dehydrated_and_deamidated].nterminal = true; + fragmentions[l0h_ion_co_loss_dehydrated_and_deamidated].cterminal = false; + fragmentions[l0h_ion_co_loss_dehydrated_and_deamidated].name = "LA*x-2H"; + fragmentions[l0h_ion_co_loss_dehydrated_and_deamidated].massdifference = L0H_ION - CO - H2O - NH3 + adductshift; + fragmentions[l0h_ion_co_loss_dehydrated_and_deamidated].parent = l0h_ion; + + // initialize l1h ion + fragmentions[l1h_ion].nterminal = true; + fragmentions[l1h_ion].cterminal = false; + fragmentions[l1h_ion].name = "LB"; + fragmentions[l1h_ion].massdifference = L1H_ION + adductshift; + fragmentions[l1h_ion].parent = l1h_ion; + + // initialize l1h-H2O ion + fragmentions[l1h_ion_dehydrated].nterminal = true; + fragmentions[l1h_ion_dehydrated].cterminal = false; + fragmentions[l1h_ion_dehydrated].name = "LB*"; + fragmentions[l1h_ion_dehydrated].massdifference = L1H_ION - H2O + adductshift; + fragmentions[l1h_ion_dehydrated].parent = l1h_ion; + + // initialize l1h-NH3 ion + fragmentions[l1h_ion_deamidated].nterminal = true; + fragmentions[l1h_ion_deamidated].cterminal = false; + fragmentions[l1h_ion_deamidated].name = "LBx"; + fragmentions[l1h_ion_deamidated].massdifference = L1H_ION - NH3 + adductshift; + fragmentions[l1h_ion_deamidated].parent = l1h_ion; + + // initialize l1h-H2O-NH3 ion + fragmentions[l1h_ion_dehydrated_and_deamidated].nterminal = true; + fragmentions[l1h_ion_dehydrated_and_deamidated].cterminal = false; + fragmentions[l1h_ion_dehydrated_and_deamidated].name = "LB*x"; + fragmentions[l1h_ion_dehydrated_and_deamidated].massdifference = L1H_ION - H2O - NH3 + adductshift; + fragmentions[l1h_ion_dehydrated_and_deamidated].parent = l1h_ion; + + // initialize l1h-CO ion + fragmentions[l1h_ion_co_loss].nterminal = true; + fragmentions[l1h_ion_co_loss].cterminal = false; + fragmentions[l1h_ion_co_loss].name = "LA"; + fragmentions[l1h_ion_co_loss].massdifference = L1H_ION - CO + adductshift; + fragmentions[l1h_ion_co_loss].parent = l1h_ion; + + // initialize l1h-CO-H2O ion + fragmentions[l1h_ion_co_loss_dehydrated].nterminal = true; + fragmentions[l1h_ion_co_loss_dehydrated].cterminal = false; + fragmentions[l1h_ion_co_loss_dehydrated].name = "LA*"; + fragmentions[l1h_ion_co_loss_dehydrated].massdifference = L1H_ION - CO - H2O + adductshift; + fragmentions[l1h_ion_co_loss_dehydrated].parent = l1h_ion; + + // initialize l1h-CO-NH3 ion + fragmentions[l1h_ion_co_loss_deamidated].nterminal = true; + fragmentions[l1h_ion_co_loss_deamidated].cterminal = false; + fragmentions[l1h_ion_co_loss_deamidated].name = "LAx"; + fragmentions[l1h_ion_co_loss_deamidated].massdifference = L1H_ION - CO - NH3 + adductshift; + fragmentions[l1h_ion_co_loss_deamidated].parent = l1h_ion; + + // initialize l1h-CO-H2O-NH3 ion + fragmentions[l1h_ion_co_loss_dehydrated_and_deamidated].nterminal = true; + fragmentions[l1h_ion_co_loss_dehydrated_and_deamidated].cterminal = false; + fragmentions[l1h_ion_co_loss_dehydrated_and_deamidated].name = "LA*x"; + fragmentions[l1h_ion_co_loss_dehydrated_and_deamidated].massdifference = L1H_ION - CO - H2O - NH3 + adductshift; + fragmentions[l1h_ion_co_loss_dehydrated_and_deamidated].parent = l1h_ion; + + // initialize l2h ion + fragmentions[l2h_ion].nterminal = true; + fragmentions[l2h_ion].cterminal = false; + fragmentions[l2h_ion].name = "LB+2H"; + fragmentions[l2h_ion].massdifference = L2H_ION + adductshift; + fragmentions[l2h_ion].parent = l2h_ion; + + // initialize l2h-H2O ion + fragmentions[l2h_ion_dehydrated].nterminal = true; + fragmentions[l2h_ion_dehydrated].cterminal = false; + fragmentions[l2h_ion_dehydrated].name = "LB*+2H"; + fragmentions[l2h_ion_dehydrated].massdifference = L2H_ION - H2O + adductshift; + fragmentions[l2h_ion_dehydrated].parent = l2h_ion; + + // initialize l2h-NH3 ion + fragmentions[l2h_ion_deamidated].nterminal = true; + fragmentions[l2h_ion_deamidated].cterminal = false; + fragmentions[l2h_ion_deamidated].name = "LBx+2H"; + fragmentions[l2h_ion_deamidated].massdifference = L2H_ION - NH3 + adductshift; + fragmentions[l2h_ion_deamidated].parent = l2h_ion; + + // initialize l2h-H2O-NH3 ion + fragmentions[l2h_ion_dehydrated_and_deamidated].nterminal = true; + fragmentions[l2h_ion_dehydrated_and_deamidated].cterminal = false; + fragmentions[l2h_ion_dehydrated_and_deamidated].name = "LB*x+2H"; + fragmentions[l2h_ion_dehydrated_and_deamidated].massdifference = L2H_ION - H2O - NH3 + adductshift; + fragmentions[l2h_ion_dehydrated_and_deamidated].parent = l2h_ion; + + // initialize l2h-CO ion + fragmentions[l2h_ion_co_loss].nterminal = true; + fragmentions[l2h_ion_co_loss].cterminal = false; + fragmentions[l2h_ion_co_loss].name = "LA+2H"; + fragmentions[l2h_ion_co_loss].massdifference = L2H_ION - CO + adductshift; + fragmentions[l2h_ion_co_loss].parent = l2h_ion; + + // initialize l2h-CO-H2O ion + fragmentions[l2h_ion_co_loss_dehydrated].nterminal = true; + fragmentions[l2h_ion_co_loss_dehydrated].cterminal = false; + fragmentions[l2h_ion_co_loss_dehydrated].name = "LA*+2H"; + fragmentions[l2h_ion_co_loss_dehydrated].massdifference = L2H_ION - CO - H2O + adductshift; + fragmentions[l2h_ion_co_loss_dehydrated].parent = l2h_ion; + + // initialize l2h-CO-NH3 ion + fragmentions[l2h_ion_co_loss_deamidated].nterminal = true; + fragmentions[l2h_ion_co_loss_deamidated].cterminal = false; + fragmentions[l2h_ion_co_loss_deamidated].name = "LAx+2H"; + fragmentions[l2h_ion_co_loss_deamidated].massdifference = L2H_ION - CO - NH3 + adductshift; + fragmentions[l2h_ion_co_loss_deamidated].parent = l2h_ion; + + // initialize l2h-CO-H2O-NH3 ion + fragmentions[l2h_ion_co_loss_dehydrated_and_deamidated].nterminal = true; + fragmentions[l2h_ion_co_loss_dehydrated_and_deamidated].cterminal = false; + fragmentions[l2h_ion_co_loss_dehydrated_and_deamidated].name = "LA*x+2H"; + fragmentions[l2h_ion_co_loss_dehydrated_and_deamidated].massdifference = L2H_ION - CO - H2O - NH3 + adductshift; + fragmentions[l2h_ion_co_loss_dehydrated_and_deamidated].parent = l2h_ion; + + // initialize r1h ion + fragmentions[r1h_ion].nterminal = true; + fragmentions[r1h_ion].cterminal = false; + fragmentions[r1h_ion].name = "RB"; + fragmentions[r1h_ion].massdifference = R1H_ION + adductshift; + fragmentions[r1h_ion].parent = r1h_ion; + + // initialize r1h-H2O ion + fragmentions[r1h_ion_dehydrated].nterminal = true; + fragmentions[r1h_ion_dehydrated].cterminal = false; + fragmentions[r1h_ion_dehydrated].name = "RB*"; + fragmentions[r1h_ion_dehydrated].massdifference = R1H_ION - H2O + adductshift; + fragmentions[r1h_ion_dehydrated].parent = r1h_ion; + + // initialize r1h-NH3 ion + fragmentions[r1h_ion_deamidated].nterminal = true; + fragmentions[r1h_ion_deamidated].cterminal = false; + fragmentions[r1h_ion_deamidated].name = "RBx"; + fragmentions[r1h_ion_deamidated].massdifference = R1H_ION - NH3 + adductshift; + fragmentions[r1h_ion_deamidated].parent = r1h_ion; + + // initialize r1h-H2O-NH3 ion + fragmentions[r1h_ion_dehydrated_and_deamidated].nterminal = true; + fragmentions[r1h_ion_dehydrated_and_deamidated].cterminal = false; + fragmentions[r1h_ion_dehydrated_and_deamidated].name = "RB*x"; + fragmentions[r1h_ion_dehydrated_and_deamidated].massdifference = R1H_ION - H2O - NH3 + adductshift; + fragmentions[r1h_ion_dehydrated_and_deamidated].parent = r1h_ion; + + // initialize r1h-CO ion + fragmentions[r1h_ion_co_loss].nterminal = true; + fragmentions[r1h_ion_co_loss].cterminal = false; + fragmentions[r1h_ion_co_loss].name = "RA"; + fragmentions[r1h_ion_co_loss].massdifference = R1H_ION - CO + adductshift; + fragmentions[r1h_ion_co_loss].parent = r1h_ion; + + // initialize r1h-CO-H2O ion + fragmentions[r1h_ion_co_loss_dehydrated].nterminal = true; + fragmentions[r1h_ion_co_loss_dehydrated].cterminal = false; + fragmentions[r1h_ion_co_loss_dehydrated].name = "RA*"; + fragmentions[r1h_ion_co_loss_dehydrated].massdifference = R1H_ION - CO - H2O + adductshift; + fragmentions[r1h_ion_co_loss_dehydrated].parent = r1h_ion; + + // initialize r1h-CO-NH3 ion + fragmentions[r1h_ion_co_loss_deamidated].nterminal = true; + fragmentions[r1h_ion_co_loss_deamidated].cterminal = false; + fragmentions[r1h_ion_co_loss_deamidated].name = "RAx"; + fragmentions[r1h_ion_co_loss_deamidated].massdifference = R1H_ION - CO - NH3 + adductshift; + fragmentions[r1h_ion_co_loss_deamidated].parent = r1h_ion; + + // initialize r1h-CO-H2O-NH3 ion + fragmentions[r1h_ion_co_loss_dehydrated_and_deamidated].nterminal = true; + fragmentions[r1h_ion_co_loss_dehydrated_and_deamidated].cterminal = false; + fragmentions[r1h_ion_co_loss_dehydrated_and_deamidated].name = "RA*x"; + fragmentions[r1h_ion_co_loss_dehydrated_and_deamidated].massdifference = R1H_ION - CO - H2O - NH3 + adductshift; + fragmentions[r1h_ion_co_loss_dehydrated_and_deamidated].parent = r1h_ion; + + // initialize r2h ion + fragmentions[r2h_ion].nterminal = true; + fragmentions[r2h_ion].cterminal = false; + fragmentions[r2h_ion].name = "RB+2H"; + fragmentions[r2h_ion].massdifference = R2H_ION + adductshift; + fragmentions[r2h_ion].parent = r2h_ion; + + // initialize r2h-H2O ion + fragmentions[r2h_ion_dehydrated].nterminal = true; + fragmentions[r2h_ion_dehydrated].cterminal = false; + fragmentions[r2h_ion_dehydrated].name = "RB*+2H"; + fragmentions[r2h_ion_dehydrated].massdifference = R2H_ION - H2O + adductshift; + fragmentions[r2h_ion_dehydrated].parent = r2h_ion; + + // initialize r2h-NH3 ion + fragmentions[r2h_ion_deamidated].nterminal = true; + fragmentions[r2h_ion_deamidated].cterminal = false; + fragmentions[r2h_ion_deamidated].name = "RBx+2H"; + fragmentions[r2h_ion_deamidated].massdifference = R2H_ION - NH3 + adductshift; + fragmentions[r2h_ion_deamidated].parent = r2h_ion; + + // initialize r2h-H2O-NH3 ion + fragmentions[r2h_ion_dehydrated_and_deamidated].nterminal = true; + fragmentions[r2h_ion_dehydrated_and_deamidated].cterminal = false; + fragmentions[r2h_ion_dehydrated_and_deamidated].name = "RB*x+2H"; + fragmentions[r2h_ion_dehydrated_and_deamidated].massdifference = R2H_ION - H2O - NH3 + adductshift; + fragmentions[r2h_ion_dehydrated_and_deamidated].parent = r2h_ion; + + // initialize r2h-CO ion + fragmentions[r2h_ion_co_loss].nterminal = true; + fragmentions[r2h_ion_co_loss].cterminal = false; + fragmentions[r2h_ion_co_loss].name = "RA+2H"; + fragmentions[r2h_ion_co_loss].massdifference = R2H_ION - CO + adductshift; + fragmentions[r2h_ion_co_loss].parent = r2h_ion; + + // initialize r2h-CO-H2O ion + fragmentions[r2h_ion_co_loss_dehydrated].nterminal = true; + fragmentions[r2h_ion_co_loss_dehydrated].cterminal = false; + fragmentions[r2h_ion_co_loss_dehydrated].name = "RA*+2H"; + fragmentions[r2h_ion_co_loss_dehydrated].massdifference = R2H_ION - CO - H2O + adductshift; + fragmentions[r2h_ion_co_loss_dehydrated].parent = r2h_ion; + + // initialize r2h-CO-NH3 ion + fragmentions[r2h_ion_co_loss_deamidated].nterminal = true; + fragmentions[r2h_ion_co_loss_deamidated].cterminal = false; + fragmentions[r2h_ion_co_loss_deamidated].name = "RAx+2H"; + fragmentions[r2h_ion_co_loss_deamidated].massdifference = R2H_ION - CO - NH3 + adductshift; + fragmentions[r2h_ion_co_loss_deamidated].parent = r2h_ion; + + // initialize r2h-CO-H2O-NH3 ion + fragmentions[r2h_ion_co_loss_dehydrated_and_deamidated].nterminal = true; + fragmentions[r2h_ion_co_loss_dehydrated_and_deamidated].cterminal = false; + fragmentions[r2h_ion_co_loss_dehydrated_and_deamidated].name = "RA*x+2H"; + fragmentions[r2h_ion_co_loss_dehydrated_and_deamidated].massdifference = R2H_ION - CO - H2O - NH3 + adductshift; + fragmentions[r2h_ion_co_loss_dehydrated_and_deamidated].parent = r2h_ion; + + // initialize l1oh ion + fragmentions[l1oh_ion].nterminal = false; + fragmentions[l1oh_ion].cterminal = true; + fragmentions[l1oh_ion].name = "LY-2H"; + fragmentions[l1oh_ion].massdifference = L1OH_ION + adductshift; + fragmentions[l1oh_ion].parent = l1oh_ion; + + // initialize l1oh-H2O ion + fragmentions[l1oh_ion_dehydrated].nterminal = false; + fragmentions[l1oh_ion_dehydrated].cterminal = true; + fragmentions[l1oh_ion_dehydrated].name = "LY*-2H"; + fragmentions[l1oh_ion_dehydrated].massdifference = L1OH_ION - H2O + adductshift; + fragmentions[l1oh_ion_dehydrated].parent = l1oh_ion; + + // initialize l1oh-NH3 ion + fragmentions[l1oh_ion_deamidated].nterminal = false; + fragmentions[l1oh_ion_deamidated].cterminal = true; + fragmentions[l1oh_ion_deamidated].name = "LYx-2H"; + fragmentions[l1oh_ion_deamidated].massdifference = L1OH_ION - NH3 + adductshift; + fragmentions[l1oh_ion_deamidated].parent = l1oh_ion; + + // initialize l1oh-H2O-NH3 ion + fragmentions[l1oh_ion_dehydrated_and_deamidated].nterminal = false; + fragmentions[l1oh_ion_dehydrated_and_deamidated].cterminal = true; + fragmentions[l1oh_ion_dehydrated_and_deamidated].name = "LY*x-2H"; + fragmentions[l1oh_ion_dehydrated_and_deamidated].massdifference = L1OH_ION - H2O - NH3 + adductshift; + fragmentions[l1oh_ion_dehydrated_and_deamidated].parent = l1oh_ion; + + // initialize l1oh-CO ion + fragmentions[l1oh_ion_co_loss].nterminal = false; + fragmentions[l1oh_ion_co_loss].cterminal = true; + fragmentions[l1oh_ion_co_loss].name = "LY-2H-CO"; + fragmentions[l1oh_ion_co_loss].massdifference = L1OH_ION - CO + adductshift; + fragmentions[l1oh_ion_co_loss].parent = l1oh_ion; + + // initialize l1oh-CO-H2O ion + fragmentions[l1oh_ion_co_loss_dehydrated].nterminal = false; + fragmentions[l1oh_ion_co_loss_dehydrated].cterminal = true; + fragmentions[l1oh_ion_co_loss_dehydrated].name = "LY*-2H-CO"; + fragmentions[l1oh_ion_co_loss_dehydrated].massdifference = L1OH_ION - CO - H2O + adductshift; + fragmentions[l1oh_ion_co_loss_dehydrated].parent = l1oh_ion; + + // initialize l1oh-CO-NH3 ion + fragmentions[l1oh_ion_co_loss_deamidated].nterminal = false; + fragmentions[l1oh_ion_co_loss_deamidated].cterminal = true; + fragmentions[l1oh_ion_co_loss_deamidated].name = "LYx-2H-CO"; + fragmentions[l1oh_ion_co_loss_deamidated].massdifference = L1OH_ION - CO - NH3 + adductshift; + fragmentions[l1oh_ion_co_loss_deamidated].parent = l1oh_ion; + + // initialize l1oh-CO-H2O-NH3 ion + fragmentions[l1oh_ion_co_loss_dehydrated_and_deamidated].nterminal = false; + fragmentions[l1oh_ion_co_loss_dehydrated_and_deamidated].cterminal = true; + fragmentions[l1oh_ion_co_loss_dehydrated_and_deamidated].name = "LY*x-2H-CO"; + fragmentions[l1oh_ion_co_loss_dehydrated_and_deamidated].massdifference = L1OH_ION - CO - H2O - NH3 + adductshift; + fragmentions[l1oh_ion_co_loss_dehydrated_and_deamidated].parent = l1oh_ion; + + // initialize l2oh ion + fragmentions[l2oh_ion].nterminal = false; + fragmentions[l2oh_ion].cterminal = true; + fragmentions[l2oh_ion].name = "LY"; + fragmentions[l2oh_ion].massdifference = L2OH_ION + adductshift; + fragmentions[l2oh_ion].parent = l2oh_ion; + + // initialize l2oh-H2O ion + fragmentions[l2oh_ion_dehydrated].nterminal = false; + fragmentions[l2oh_ion_dehydrated].cterminal = true; + fragmentions[l2oh_ion_dehydrated].name = "LY*"; + fragmentions[l2oh_ion_dehydrated].massdifference = L2OH_ION - H2O + adductshift; + fragmentions[l2oh_ion_dehydrated].parent = l2oh_ion; + + // initialize l2oh-NH3 ion + fragmentions[l2oh_ion_deamidated].nterminal = false; + fragmentions[l2oh_ion_deamidated].cterminal = true; + fragmentions[l2oh_ion_deamidated].name = "LYx"; + fragmentions[l2oh_ion_deamidated].massdifference = L2OH_ION - NH3 + adductshift; + fragmentions[l2oh_ion_deamidated].parent = l2oh_ion; + + // initialize l2oh-H2O-NH3 ion + fragmentions[l2oh_ion_dehydrated_and_deamidated].nterminal = false; + fragmentions[l2oh_ion_dehydrated_and_deamidated].cterminal = true; + fragmentions[l2oh_ion_dehydrated_and_deamidated].name = "LY*x"; + fragmentions[l2oh_ion_dehydrated_and_deamidated].massdifference = L2OH_ION - H2O - NH3 + adductshift; + fragmentions[l2oh_ion_dehydrated_and_deamidated].parent = l2oh_ion; + + // initialize l2oh-CO ion + fragmentions[l2oh_ion_co_loss].nterminal = false; + fragmentions[l2oh_ion_co_loss].cterminal = true; + fragmentions[l2oh_ion_co_loss].name = "LY-CO"; + fragmentions[l2oh_ion_co_loss].massdifference = L2OH_ION - CO + adductshift; + fragmentions[l2oh_ion_co_loss].parent = l2oh_ion; + + // initialize l2oh-CO-H2O ion + fragmentions[l2oh_ion_co_loss_dehydrated].nterminal = false; + fragmentions[l2oh_ion_co_loss_dehydrated].cterminal = true; + fragmentions[l2oh_ion_co_loss_dehydrated].name = "LY*-CO"; + fragmentions[l2oh_ion_co_loss_dehydrated].massdifference = L2OH_ION - CO - H2O + adductshift; + fragmentions[l2oh_ion_co_loss_dehydrated].parent = l2oh_ion; + + // initialize l2oh-CO-NH3 ion + fragmentions[l2oh_ion_co_loss_deamidated].nterminal = false; + fragmentions[l2oh_ion_co_loss_deamidated].cterminal = true; + fragmentions[l2oh_ion_co_loss_deamidated].name = "LYx-CO"; + fragmentions[l2oh_ion_co_loss_deamidated].massdifference = L2OH_ION - CO - NH3 + adductshift; + fragmentions[l2oh_ion_co_loss_deamidated].parent = l2oh_ion; + + // initialize l2oh-CO-H2O-NH3 ion + fragmentions[l2oh_ion_co_loss_dehydrated_and_deamidated].nterminal = false; + fragmentions[l2oh_ion_co_loss_dehydrated_and_deamidated].cterminal = true; + fragmentions[l2oh_ion_co_loss_dehydrated_and_deamidated].name = "LY*x-CO"; + fragmentions[l2oh_ion_co_loss_dehydrated_and_deamidated].massdifference = L2OH_ION - CO - H2O - NH3 + adductshift; + fragmentions[l2oh_ion_co_loss_dehydrated_and_deamidated].parent = l2oh_ion; + + // initialize r1oh ion + fragmentions[r1oh_ion].nterminal = false; + fragmentions[r1oh_ion].cterminal = true; + fragmentions[r1oh_ion].name = "RY-2H"; + fragmentions[r1oh_ion].massdifference = R1OH_ION + adductshift; + fragmentions[r1oh_ion].parent = r1oh_ion; + + // initialize r1oh-H2O ion + fragmentions[r1oh_ion_dehydrated].nterminal = false; + fragmentions[r1oh_ion_dehydrated].cterminal = true; + fragmentions[r1oh_ion_dehydrated].name = "RY*-2H"; + fragmentions[r1oh_ion_dehydrated].massdifference = R1OH_ION - H2O + adductshift; + fragmentions[r1oh_ion_dehydrated].parent = r1oh_ion; + + // initialize r1oh-NH3 ion + fragmentions[r1oh_ion_deamidated].nterminal = false; + fragmentions[r1oh_ion_deamidated].cterminal = true; + fragmentions[r1oh_ion_deamidated].name = "RYx-2H"; + fragmentions[r1oh_ion_deamidated].massdifference = R1OH_ION - NH3 + adductshift; + fragmentions[r1oh_ion_deamidated].parent = r1oh_ion; + + // initialize r1oh-H2O-NH3 ion + fragmentions[r1oh_ion_dehydrated_and_deamidated].nterminal = false; + fragmentions[r1oh_ion_dehydrated_and_deamidated].cterminal = true; + fragmentions[r1oh_ion_dehydrated_and_deamidated].name = "RY*x-2H"; + fragmentions[r1oh_ion_dehydrated_and_deamidated].massdifference = R1OH_ION - H2O - NH3 + adductshift; + fragmentions[r1oh_ion_dehydrated_and_deamidated].parent = r1oh_ion; + + // initialize r1oh-CO ion + fragmentions[r1oh_ion_co_loss].nterminal = false; + fragmentions[r1oh_ion_co_loss].cterminal = true; + fragmentions[r1oh_ion_co_loss].name = "RY-2H-CO"; + fragmentions[r1oh_ion_co_loss].massdifference = R1OH_ION - CO + adductshift; + fragmentions[r1oh_ion_co_loss].parent = r1oh_ion; + + // initialize r1oh-CO-H2O ion + fragmentions[r1oh_ion_co_loss_dehydrated].nterminal = false; + fragmentions[r1oh_ion_co_loss_dehydrated].cterminal = true; + fragmentions[r1oh_ion_co_loss_dehydrated].name = "RY*-2H-CO"; + fragmentions[r1oh_ion_co_loss_dehydrated].massdifference = R1OH_ION - CO - H2O + adductshift; + fragmentions[r1oh_ion_co_loss_dehydrated].parent = r1oh_ion; + + // initialize r1oh-CO-NH3 ion + fragmentions[r1oh_ion_co_loss_deamidated].nterminal = false; + fragmentions[r1oh_ion_co_loss_deamidated].cterminal = true; + fragmentions[r1oh_ion_co_loss_deamidated].name = "RYx-2H-CO"; + fragmentions[r1oh_ion_co_loss_deamidated].massdifference = R1OH_ION - CO - NH3 + adductshift; + fragmentions[r1oh_ion_co_loss_deamidated].parent = r1oh_ion; + + // initialize r1oh-CO-H2O-NH3 ion + fragmentions[r1oh_ion_co_loss_dehydrated_and_deamidated].nterminal = false; + fragmentions[r1oh_ion_co_loss_dehydrated_and_deamidated].cterminal = true; + fragmentions[r1oh_ion_co_loss_dehydrated_and_deamidated].name = "RY*x-2H-CO"; + fragmentions[r1oh_ion_co_loss_dehydrated_and_deamidated].massdifference = R1OH_ION - CO - H2O - NH3 + adductshift; + fragmentions[r1oh_ion_co_loss_dehydrated_and_deamidated].parent = r1oh_ion; + + // initialize r2oh ion + fragmentions[r2oh_ion].nterminal = false; + fragmentions[r2oh_ion].cterminal = true; + fragmentions[r2oh_ion].name = "RY"; + fragmentions[r2oh_ion].massdifference = R2OH_ION + adductshift; + fragmentions[r2oh_ion].parent = r2oh_ion; + + // initialize r2oh-H2O ion + fragmentions[r2oh_ion_dehydrated].nterminal = false; + fragmentions[r2oh_ion_dehydrated].cterminal = true; + fragmentions[r2oh_ion_dehydrated].name = "RY*"; + fragmentions[r2oh_ion_dehydrated].massdifference = R2OH_ION - H2O + adductshift; + fragmentions[r2oh_ion_dehydrated].parent = r2oh_ion; + + // initialize r2oh-NH3 ion + fragmentions[r2oh_ion_deamidated].nterminal = false; + fragmentions[r2oh_ion_deamidated].cterminal = true; + fragmentions[r2oh_ion_deamidated].name = "RYx"; + fragmentions[r2oh_ion_deamidated].massdifference = R2OH_ION - NH3 + adductshift; + fragmentions[r2oh_ion_deamidated].parent = r2oh_ion; + + // initialize r2oh-H2O-NH3 ion + fragmentions[r2oh_ion_dehydrated_and_deamidated].nterminal = false; + fragmentions[r2oh_ion_dehydrated_and_deamidated].cterminal = true; + fragmentions[r2oh_ion_dehydrated_and_deamidated].name = "RY*x"; + fragmentions[r2oh_ion_dehydrated_and_deamidated].massdifference = R2OH_ION - H2O - NH3 + adductshift; + fragmentions[r2oh_ion_dehydrated_and_deamidated].parent = r2oh_ion; + + // initialize r2oh-CO ion + fragmentions[r2oh_ion_co_loss].nterminal = false; + fragmentions[r2oh_ion_co_loss].cterminal = true; + fragmentions[r2oh_ion_co_loss].name = "RY-CO"; + fragmentions[r2oh_ion_co_loss].massdifference = R2OH_ION - CO + adductshift; + fragmentions[r2oh_ion_co_loss].parent = r2oh_ion; + + // initialize r2oh-CO-H2O ion + fragmentions[r2oh_ion_co_loss_dehydrated].nterminal = false; + fragmentions[r2oh_ion_co_loss_dehydrated].cterminal = true; + fragmentions[r2oh_ion_co_loss_dehydrated].name = "RY*-CO"; + fragmentions[r2oh_ion_co_loss_dehydrated].massdifference = R2OH_ION - CO - H2O + adductshift; + fragmentions[r2oh_ion_co_loss_dehydrated].parent = r2oh_ion; + + // initialize r2oh-CO-NH3 ion + fragmentions[r2oh_ion_co_loss_deamidated].nterminal = false; + fragmentions[r2oh_ion_co_loss_deamidated].cterminal = true; + fragmentions[r2oh_ion_co_loss_deamidated].name = "RYx-CO"; + fragmentions[r2oh_ion_co_loss_deamidated].massdifference = R2OH_ION - CO - NH3 + adductshift; + fragmentions[r2oh_ion_co_loss_deamidated].parent = r2oh_ion; + + // initialize r2oh-CO-H2O-NH3 ion + fragmentions[r2oh_ion_co_loss_dehydrated_and_deamidated].nterminal = false; + fragmentions[r2oh_ion_co_loss_dehydrated_and_deamidated].cterminal = true; + fragmentions[r2oh_ion_co_loss_dehydrated_and_deamidated].name = "RY*x-CO"; + fragmentions[r2oh_ion_co_loss_dehydrated_and_deamidated].massdifference = R2OH_ION - CO - H2O - NH3 + adductshift; + fragmentions[r2oh_ion_co_loss_dehydrated_and_deamidated].parent = r2oh_ion; + + // initialize linear polyketide siderophore precursor ion (H-...-H) + fragmentions[linear_polyketide_precursor_ion_h_h].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_h].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_h].name = "[M+zH]+ (H-...-H)"; + fragmentions[linear_polyketide_precursor_ion_h_h].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_H_H + adductshift; + fragmentions[linear_polyketide_precursor_ion_h_h].parent = linear_polyketide_precursor_ion_h_h; + + // initialize linear polyketide siderophore precursor ion (H-...-H) - H2O + fragmentions[linear_polyketide_precursor_ion_h_h_dehydrated].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_h_dehydrated].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_h_dehydrated].name = "[M+zH]+ * (H-...-H)"; + fragmentions[linear_polyketide_precursor_ion_h_h_dehydrated].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_H_H - H2O + adductshift; + fragmentions[linear_polyketide_precursor_ion_h_h_dehydrated].parent = linear_polyketide_precursor_ion_h_h; + + // initialize linear polyketide siderophore precursor ion (H-...-H) - NH3 + fragmentions[linear_polyketide_precursor_ion_h_h_deamidated].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_h_deamidated].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_h_deamidated].name = "[M+zH]+ x (H-...-H)"; + fragmentions[linear_polyketide_precursor_ion_h_h_deamidated].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_H_H - NH3 + adductshift; + fragmentions[linear_polyketide_precursor_ion_h_h_deamidated].parent = linear_polyketide_precursor_ion_h_h; + + // initialize linear polyketide siderophore precursor ion (H-...-H) - H2O - NH3 + fragmentions[linear_polyketide_precursor_ion_h_h_dehydrated_and_deamidated].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_h_dehydrated_and_deamidated].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_h_dehydrated_and_deamidated].name = "[M+zH]+ *x (H-...-H)"; + fragmentions[linear_polyketide_precursor_ion_h_h_dehydrated_and_deamidated].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_H_H - H2O - NH3 + adductshift; + fragmentions[linear_polyketide_precursor_ion_h_h_dehydrated_and_deamidated].parent = linear_polyketide_precursor_ion_h_h; + + // initialize linear polyketide siderophore precursor ion (H-...-H) - CO + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss].name = "[M+zH]+ -CO (H-...-H)"; + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_H_H - CO + adductshift; + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss].parent = linear_polyketide_precursor_ion_h_h_co_loss; + + // initialize linear polyketide siderophore precursor ion (H-...-H) - CO - H2O + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss_dehydrated].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss_dehydrated].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss_dehydrated].name = "[M+zH]+ *-CO (H-...-H)"; + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss_dehydrated].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_H_H - CO - H2O + adductshift; + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss_dehydrated].parent = linear_polyketide_precursor_ion_h_h_co_loss; + + // initialize linear polyketide siderophore precursor ion (H-...-H) - CO - NH3 + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss_deamidated].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss_deamidated].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss_deamidated].name = "[M+zH]+ x-CO (H-...-H)"; + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss_deamidated].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_H_H - CO - NH3 + adductshift; + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss_deamidated].parent = linear_polyketide_precursor_ion_h_h_co_loss; + + // initialize linear polyketide siderophore precursor ion (H-...-H) - CO - H2O - NH3 + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss_dehydrated_and_deamidated].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss_dehydrated_and_deamidated].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss_dehydrated_and_deamidated].name = "[M+zH]+ *x-CO (H-...-H)"; + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss_dehydrated_and_deamidated].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_H_H - CO - H2O - NH3 + adductshift; + fragmentions[linear_polyketide_precursor_ion_h_h_co_loss_dehydrated_and_deamidated].parent = linear_polyketide_precursor_ion_h_h_co_loss; + + // initialize linear polyketide siderophore precursor ion (H-...-OH) + fragmentions[linear_polyketide_precursor_ion_h_oh].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_oh].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_oh].name = "[M+zH]+ (H-...-OH)"; + fragmentions[linear_polyketide_precursor_ion_h_oh].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_H_OH + adductshift; + fragmentions[linear_polyketide_precursor_ion_h_oh].parent = linear_polyketide_precursor_ion_h_oh; + + // initialize linear polyketide siderophore precursor ion (H-...-OH) - H2O + fragmentions[linear_polyketide_precursor_ion_h_oh_dehydrated].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_oh_dehydrated].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_oh_dehydrated].name = "[M+zH]+ * (H-...-OH)"; + fragmentions[linear_polyketide_precursor_ion_h_oh_dehydrated].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_H_OH - H2O + adductshift; + fragmentions[linear_polyketide_precursor_ion_h_oh_dehydrated].parent = linear_polyketide_precursor_ion_h_oh; + + // initialize linear polyketide siderophore precursor ion (H-...-OH) - NH3 + fragmentions[linear_polyketide_precursor_ion_h_oh_deamidated].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_oh_deamidated].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_oh_deamidated].name = "[M+zH]+ x (H-...-OH)"; + fragmentions[linear_polyketide_precursor_ion_h_oh_deamidated].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_H_OH - NH3 + adductshift; + fragmentions[linear_polyketide_precursor_ion_h_oh_deamidated].parent = linear_polyketide_precursor_ion_h_oh; + + // initialize linear polyketide siderophore precursor ion (H-...-OH) - H2O - NH3 + fragmentions[linear_polyketide_precursor_ion_h_oh_dehydrated_and_deamidated].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_oh_dehydrated_and_deamidated].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_oh_dehydrated_and_deamidated].name = "[M+zH]+ *x (H-...-OH)"; + fragmentions[linear_polyketide_precursor_ion_h_oh_dehydrated_and_deamidated].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_H_OH - H2O - NH3 + adductshift; + fragmentions[linear_polyketide_precursor_ion_h_oh_dehydrated_and_deamidated].parent = linear_polyketide_precursor_ion_h_oh; + + // initialize linear polyketide siderophore precursor ion (H-...-OH) - CO + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss].name = "[M+zH]+ -CO (H-...-OH)"; + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_H_OH - CO + adductshift; + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss].parent = linear_polyketide_precursor_ion_h_oh_co_loss; + + // initialize linear polyketide siderophore precursor ion (H-...-OH) - CO - H2O + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss_dehydrated].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss_dehydrated].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss_dehydrated].name = "[M+zH]+ *-CO (H-...-OH)"; + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss_dehydrated].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_H_OH - CO - H2O + adductshift; + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss_dehydrated].parent = linear_polyketide_precursor_ion_h_oh_co_loss; + + // initialize linear polyketide siderophore precursor ion (H-...-OH) - CO - NH3 + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss_deamidated].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss_deamidated].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss_deamidated].name = "[M+zH]+ x-CO (H-...-OH)"; + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss_deamidated].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_H_OH - CO - NH3 + adductshift; + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss_deamidated].parent = linear_polyketide_precursor_ion_h_oh_co_loss; + + // initialize linear polyketide siderophore precursor ion (H-...-OH) - CO - H2O - NH3 + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss_dehydrated_and_deamidated].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss_dehydrated_and_deamidated].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss_dehydrated_and_deamidated].name = "[M+zH]+ *x-CO (H-...-OH)"; + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss_dehydrated_and_deamidated].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_H_OH - CO - H2O - NH3 + adductshift; + fragmentions[linear_polyketide_precursor_ion_h_oh_co_loss_dehydrated_and_deamidated].parent = linear_polyketide_precursor_ion_h_oh_co_loss; + + // initialize linear polyketide siderophore precursor ion (HO-...-OH) + fragmentions[linear_polyketide_precursor_ion_oh_oh].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_oh_oh].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_oh_oh].name = "[M+zH]+ (HO-...-OH)"; + fragmentions[linear_polyketide_precursor_ion_oh_oh].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_OH_OH + adductshift; + fragmentions[linear_polyketide_precursor_ion_oh_oh].parent = linear_polyketide_precursor_ion_oh_oh; + + // initialize linear polyketide siderophore precursor ion (HO-...-OH) - H2O + fragmentions[linear_polyketide_precursor_ion_oh_oh_dehydrated].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_oh_oh_dehydrated].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_oh_oh_dehydrated].name = "[M+zH]+ * (HO-...-OH)"; + fragmentions[linear_polyketide_precursor_ion_oh_oh_dehydrated].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_OH_OH - H2O + adductshift; + fragmentions[linear_polyketide_precursor_ion_oh_oh_dehydrated].parent = linear_polyketide_precursor_ion_oh_oh; + + // initialize linear polyketide siderophore precursor ion (HO-...-OH) - NH3 + fragmentions[linear_polyketide_precursor_ion_oh_oh_deamidated].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_oh_oh_deamidated].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_oh_oh_deamidated].name = "[M+zH]+ x (HO-...-OH)"; + fragmentions[linear_polyketide_precursor_ion_oh_oh_deamidated].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_OH_OH - NH3 + adductshift; + fragmentions[linear_polyketide_precursor_ion_oh_oh_deamidated].parent = linear_polyketide_precursor_ion_oh_oh; + + // initialize linear polyketide siderophore precursor ion (HO-...-OH) - H2O - NH3 + fragmentions[linear_polyketide_precursor_ion_oh_oh_dehydrated_and_deamidated].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_oh_oh_dehydrated_and_deamidated].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_oh_oh_dehydrated_and_deamidated].name = "[M+zH]+ *x (HO-...-OH)"; + fragmentions[linear_polyketide_precursor_ion_oh_oh_dehydrated_and_deamidated].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_OH_OH - H2O - NH3 + adductshift; + fragmentions[linear_polyketide_precursor_ion_oh_oh_dehydrated_and_deamidated].parent = linear_polyketide_precursor_ion_oh_oh; + + // initialize linear polyketide siderophore precursor ion (HO-...-OH) - CO + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss].name = "[M+zH]+ -CO (HO-...-OH)"; + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_OH_OH - CO + adductshift; + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss].parent = linear_polyketide_precursor_ion_oh_oh_co_loss; + + // initialize linear polyketide siderophore precursor ion (HO-...-OH) - CO - H2O + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss_dehydrated].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss_dehydrated].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss_dehydrated].name = "[M+zH]+ *-CO (HO-...-OH)"; + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss_dehydrated].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_OH_OH - CO - H2O + adductshift; + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss_dehydrated].parent = linear_polyketide_precursor_ion_oh_oh_co_loss; + + // initialize linear polyketide siderophore precursor ion (HO-...-OH) - CO - NH3 + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss_deamidated].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss_deamidated].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss_deamidated].name = "[M+zH]+ x-CO (HO-...-OH)"; + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss_deamidated].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_OH_OH - CO - NH3 + adductshift; + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss_deamidated].parent = linear_polyketide_precursor_ion_oh_oh_co_loss; + + // initialize linear polyketide siderophore precursor ion (HO-...-OH) - CO - H2O - NH3 + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss_dehydrated_and_deamidated].nterminal = false; + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss_dehydrated_and_deamidated].cterminal = false; + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss_dehydrated_and_deamidated].name = "[M+zH]+ *x-CO (HO-...-OH)"; + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss_dehydrated_and_deamidated].massdifference = PRECURSOR_ION_LINEAR_POLYKETIDE_OH_OH - CO - H2O - NH3 + adductshift; + fragmentions[linear_polyketide_precursor_ion_oh_oh_co_loss_dehydrated_and_deamidated].parent = linear_polyketide_precursor_ion_oh_oh_co_loss; + + // initialize cyclic polyketide siderophore precursor ion + fragmentions[cyclic_polyketide_precursor_ion].nterminal = false; + fragmentions[cyclic_polyketide_precursor_ion].cterminal = false; + fragmentions[cyclic_polyketide_precursor_ion].name = "[M+zH]+"; + fragmentions[cyclic_polyketide_precursor_ion].massdifference = Hplus + adductshift; + fragmentions[cyclic_polyketide_precursor_ion].parent = cyclic_polyketide_precursor_ion; + + // initialize cyclic polyketide siderophore precursor ion - H2O + fragmentions[cyclic_polyketide_precursor_ion_dehydrated].nterminal = false; + fragmentions[cyclic_polyketide_precursor_ion_dehydrated].cterminal = false; + fragmentions[cyclic_polyketide_precursor_ion_dehydrated].name = "[M+zH]+ *"; + fragmentions[cyclic_polyketide_precursor_ion_dehydrated].massdifference = Hplus - H2O + adductshift; + fragmentions[cyclic_polyketide_precursor_ion_dehydrated].parent = cyclic_polyketide_precursor_ion; + + // initialize cyclic polyketide siderophore precursor ion - NH3 + fragmentions[cyclic_polyketide_precursor_ion_deamidated].nterminal = false; + fragmentions[cyclic_polyketide_precursor_ion_deamidated].cterminal = false; + fragmentions[cyclic_polyketide_precursor_ion_deamidated].name = "[M+zH]+ x"; + fragmentions[cyclic_polyketide_precursor_ion_deamidated].massdifference = Hplus - NH3 + adductshift; + fragmentions[cyclic_polyketide_precursor_ion_deamidated].parent = cyclic_polyketide_precursor_ion; + + // initialize cyclic polyketide siderophore precursor ion - H2O - NH3 + fragmentions[cyclic_polyketide_precursor_ion_dehydrated_and_deamidated].nterminal = false; + fragmentions[cyclic_polyketide_precursor_ion_dehydrated_and_deamidated].cterminal = false; + fragmentions[cyclic_polyketide_precursor_ion_dehydrated_and_deamidated].name = "[M+zH]+ *x"; + fragmentions[cyclic_polyketide_precursor_ion_dehydrated_and_deamidated].massdifference = Hplus - H2O - NH3 + adductshift; + fragmentions[cyclic_polyketide_precursor_ion_dehydrated_and_deamidated].parent = cyclic_polyketide_precursor_ion; + + // initialize cyclic polyketide siderophore precursor ion - CO + fragmentions[cyclic_polyketide_precursor_ion_co_loss].nterminal = false; + fragmentions[cyclic_polyketide_precursor_ion_co_loss].cterminal = false; + fragmentions[cyclic_polyketide_precursor_ion_co_loss].name = "[M+zH]+ -CO"; + fragmentions[cyclic_polyketide_precursor_ion_co_loss].massdifference = Hplus - CO + adductshift; + fragmentions[cyclic_polyketide_precursor_ion_co_loss].parent = cyclic_polyketide_precursor_ion_co_loss; + + // initialize cyclic polyketide siderophore precursor ion - CO - H2O + fragmentions[cyclic_polyketide_precursor_ion_co_loss_dehydrated].nterminal = false; + fragmentions[cyclic_polyketide_precursor_ion_co_loss_dehydrated].cterminal = false; + fragmentions[cyclic_polyketide_precursor_ion_co_loss_dehydrated].name = "[M+zH]+ *-CO"; + fragmentions[cyclic_polyketide_precursor_ion_co_loss_dehydrated].massdifference = Hplus - CO - H2O + adductshift; + fragmentions[cyclic_polyketide_precursor_ion_co_loss_dehydrated].parent = cyclic_polyketide_precursor_ion_co_loss; + + // initialize cyclic polyketide siderophore precursor ion - CO - NH3 + fragmentions[cyclic_polyketide_precursor_ion_co_loss_deamidated].nterminal = false; + fragmentions[cyclic_polyketide_precursor_ion_co_loss_deamidated].cterminal = false; + fragmentions[cyclic_polyketide_precursor_ion_co_loss_deamidated].name = "[M+zH]+ x-CO"; + fragmentions[cyclic_polyketide_precursor_ion_co_loss_deamidated].massdifference = Hplus - CO - NH3 + adductshift; + fragmentions[cyclic_polyketide_precursor_ion_co_loss_deamidated].parent = cyclic_polyketide_precursor_ion_co_loss; + + // initialize cyclic polyketide siderophore precursor ion - CO - H2O - NH3 + fragmentions[cyclic_polyketide_precursor_ion_co_loss_dehydrated_and_deamidated].nterminal = false; + fragmentions[cyclic_polyketide_precursor_ion_co_loss_dehydrated_and_deamidated].cterminal = false; + fragmentions[cyclic_polyketide_precursor_ion_co_loss_dehydrated_and_deamidated].name = "[M+zH]+ *x-CO"; + fragmentions[cyclic_polyketide_precursor_ion_co_loss_dehydrated_and_deamidated].massdifference = Hplus - CO - H2O - NH3 + adductshift; + fragmentions[cyclic_polyketide_precursor_ion_co_loss_dehydrated_and_deamidated].parent = cyclic_polyketide_precursor_ion_co_loss; +#endif // initialize B-2H ion //fragmentions[b_ion_2H_loss].nterminal = true; @@ -665,8 +1694,8 @@ void cFragmentIons::store(ofstream& os) { size = (int)fragmentions.size(); os.write((char *)&size, sizeof(int)); - for (map::iterator it = fragmentions.begin(); it != fragmentions.end(); ++it) { - os.write((char *)&it->first, sizeof(fragmentIonType)); + for (map::iterator it = fragmentions.begin(); it != fragmentions.end(); ++it) { + os.write((char *)&it->first, sizeof(eFragmentIonType)); it->second.store(os); } } @@ -674,45 +1703,70 @@ void cFragmentIons::store(ofstream& os) { void cFragmentIons::load(ifstream& is) { int size; - fragmentIonType iontype; + eFragmentIonType iontype; fragmentDescription description; is.read((char *)&size, sizeof(int)); fragmentions.clear(); for (int i = 0; i < size; i++) { - is.read((char *)&iontype, sizeof(fragmentIonType)); + is.read((char *)&iontype, sizeof(eFragmentIonType)); description.load(is); fragmentions[iontype] = description; } } -void initializeFragmentIonsForDeNovoGraphOfCyclicPeptides(vector& fragmentions) { +void initializeFragmentIonsForDeNovoGraphOfCyclicPeptides(vector& fragmentions) { fragmentions.push_back(b_ion); } -void initializeFragmentIonsForDeNovoGraphOfLinearPeptides(vector& fragmentions) { +void initializeFragmentIonsForDeNovoGraphOfLinearPeptides(vector& fragmentions) { fragmentions.push_back(y_ion); fragmentions.push_back(b_ion); fragmentions.push_back(precursor_ion); } -void initializeFragmentIonsForDeNovoGraphOfTPeptides(vector& fragmentions) { +void initializeFragmentIonsForDeNovoGraphOfTPeptides(vector& fragmentions) { fragmentions.push_back(y_ion); fragmentions.push_back(b_ion); fragmentions.push_back(precursor_ion); } -void initializeFragmentIonsForDeNovoGraphOfLassoPeptides(vector& fragmentions) { +void initializeFragmentIonsForDeNovoGraphOfBranchCyclicPeptides(vector& fragmentions) { fragmentions.push_back(b_ion); //fragmentions.push_back(y_ion); } -void initializeFragmentIonsForDeNovoGraphOfLinearPolysaccharide(vector& fragmentions) { +#if POLYKETIDE_SIDEROPHORES == 1 + + +void initializeFragmentIonsForDeNovoGraphOfLinearPolyketideSiderophore(vector& fragmentions) { + fragmentions.push_back(l1h_ion); + fragmentions.push_back(l2h_ion); + fragmentions.push_back(l1oh_ion); + fragmentions.push_back(l2oh_ion); + fragmentions.push_back(linear_polyketide_precursor_ion_h_h); + fragmentions.push_back(linear_polyketide_precursor_ion_h_oh); + fragmentions.push_back(linear_polyketide_precursor_ion_oh_oh); +} + + +void initializeFragmentIonsForDeNovoGraphOfCyclicPolyketideSiderophore(vector& fragmentions) { + fragmentions.push_back(l0h_ion); + fragmentions.push_back(l1h_ion); + fragmentions.push_back(l2h_ion); + fragmentions.push_back(cyclic_polyketide_precursor_ion); +} + + +#endif + + +void initializeFragmentIonsForDeNovoGraphOfLinearPolysaccharide(vector& fragmentions) { fragmentions.push_back(ms_nterminal_ion_hplus); //fragmentions.push_back(ms_cterminal_ion_hplus); fragmentions.push_back(precursor_ion); diff --git a/CycloBranch/core/cFragmentIons.h b/CycloBranch/core/cFragmentIons.h index 728e596..b3fb3e5 100644 --- a/CycloBranch/core/cFragmentIons.h +++ b/CycloBranch/core/cFragmentIons.h @@ -24,6 +24,7 @@ using namespace std; const double Hplus = 1.00727645; const double Naplus = 22.989222; const double Kplus = 38.963158; +const double Liplus = 7.015455; const double H = 1.0078250321; const double D = 2.014102; const double Li = 7.016004; @@ -127,9 +128,29 @@ const double Fm = 257.095093; const double Md = 258.098419; const double No = 259.101044; const double Lr = 262.109802; + const double R = 0; const double X = 0; const double e = H - Hplus; +//const double neutron = D - H; + +const double Li6 = 6.015122; +const double Mg25 = 24.985837; +const double Mg26 = 25.982593; +const double K41 = 40.961825; +const double Ca44 = 43.95548; +const double Fe54 = 53.939612; +//const double Fe57 = 56.935396; +//const double Fe58 = 57.933278; +const double Ni60 = 59.930788; +const double Ni61 = 60.931061; +const double Ni62 = 61.928346; +//const double Ni64 = 63.927968; +const double Cu65 = 64.927793; +const double Zn66 = 65.926034; +const double Zn67 = 66.927129; +const double Zn68 = 67.924846; +const double Ga71 = 70.9247; /** @@ -180,11 +201,27 @@ const double Z_ION = Hplus + O - N - H; const double PRECURSOR_ION = Hplus + H2O; const double PRECURSOR_ION_CYCLIC = Hplus; +#if POLYKETIDE_SIDEROPHORES == 1 + const double L0H_ION = Hplus - 2 * H; + const double L1H_ION = Hplus; + const double L2H_ION = Hplus + 2 * H; + const double R1H_ION = Hplus; + const double R2H_ION = Hplus + 2 * H; + const double L1OH_ION = Hplus + O; + const double L2OH_ION = Hplus + H2O; + const double R1OH_ION = Hplus + O; + const double R2OH_ION = Hplus + H2O; + + const double PRECURSOR_ION_LINEAR_POLYKETIDE_H_H = Hplus + 2 * H; + const double PRECURSOR_ION_LINEAR_POLYKETIDE_H_OH = Hplus + H2O; + const double PRECURSOR_ION_LINEAR_POLYKETIDE_OH_OH = Hplus + 2 * H + 2 * O; +#endif + /** - \brief Register peptideType by Qt. + \brief Register ePeptideType by Qt. */ -Q_DECLARE_METATYPE(peptideType); +Q_DECLARE_METATYPE(ePeptideType); /** @@ -208,78 +245,212 @@ double uncharge(double mzratio, int charge); /** \brief The types of supported fragment ions. */ -enum fragmentIonType { +enum eFragmentIonType { a_ion = 1, - a_ion_dehydrated = 2, - a_ion_deamidated = 3, - a_ion_dehydrated_and_deamidated = 4, - b_ion = 5, - b_ion_dehydrated = 6, - b_ion_deamidated = 7, - b_ion_dehydrated_and_deamidated = 8, - c_ion = 9, - c_ion_dehydrated = 10, - c_ion_deamidated = 11, - c_ion_dehydrated_and_deamidated = 12, - x_ion = 13, - x_ion_dehydrated = 14, - x_ion_deamidated = 15, - x_ion_dehydrated_and_deamidated = 16, - y_ion = 17, - y_ion_dehydrated = 18, - y_ion_deamidated = 19, - y_ion_dehydrated_and_deamidated = 20, - z_ion = 21, - z_ion_dehydrated = 22, - z_ion_deamidated = 23, - z_ion_dehydrated_and_deamidated = 24, - precursor_ion = 25, - precursor_ion_dehydrated = 26, - precursor_ion_deamidated = 27, - precursor_ion_dehydrated_and_deamidated = 28, - precursor_ion_co_loss = 29, - precursor_ion_co_loss_and_dehydrated = 30, - precursor_ion_co_loss_and_deamidated = 31, - precursor_ion_co_loss_and_dehydrated_and_deamidated = 32, - cyclic_precursor_ion = 33, - cyclic_precursor_ion_dehydrated = 34, - cyclic_precursor_ion_deamidated = 35, - cyclic_precursor_ion_dehydrated_and_deamidated = 36, - cyclic_precursor_ion_co_loss = 37, - cyclic_precursor_ion_co_loss_and_dehydrated = 38, - cyclic_precursor_ion_co_loss_and_deamidated = 39, - cyclic_precursor_ion_co_loss_and_dehydrated_and_deamidated = 40, - ms_nterminal_ion_hplus = 41, - ms_nterminal_ion_naplus = 42, - ms_nterminal_ion_kplus = 43, - ms_cterminal_ion_hplus = 44, - ms_cterminal_ion_naplus = 45, - ms_cterminal_ion_kplus = 46, - ms_hplus = 47, - ms_naplus = 48, - ms_kplus = 49, - ms_hminus = 50, - ms_3M2Fe5H = 51, - ms_2MFe2H = 52, - ms_3MFe2H = 53, - ms_MFe2H = 54, - ms_3M2Fe6HNa = 55, - ms_2MFe3HNa = 56, - ms_3MFe3HNa = 57, - ms_MFe3HNa = 58, - ms_3M2Fe7H = 59, - ms_2MFe4H = 60, - ms_3MFe4H = 61, - ms_MFe4H = 62, - //b_ion_2H_loss = 63, + a_ion_dehydrated, + a_ion_deamidated, + a_ion_dehydrated_and_deamidated, + b_ion, + b_ion_dehydrated, + b_ion_deamidated, + b_ion_dehydrated_and_deamidated, + c_ion, + c_ion_dehydrated, + c_ion_deamidated, + c_ion_dehydrated_and_deamidated, + x_ion, + x_ion_dehydrated, + x_ion_deamidated, + x_ion_dehydrated_and_deamidated, + y_ion, + y_ion_dehydrated, + y_ion_deamidated, + y_ion_dehydrated_and_deamidated, + z_ion, + z_ion_dehydrated, + z_ion_deamidated, + z_ion_dehydrated_and_deamidated, + precursor_ion, + precursor_ion_dehydrated, + precursor_ion_deamidated, + precursor_ion_dehydrated_and_deamidated, + precursor_ion_co_loss, + precursor_ion_co_loss_and_dehydrated, + precursor_ion_co_loss_and_deamidated, + precursor_ion_co_loss_and_dehydrated_and_deamidated, + cyclic_precursor_ion, + cyclic_precursor_ion_dehydrated, + cyclic_precursor_ion_deamidated, + cyclic_precursor_ion_dehydrated_and_deamidated, + cyclic_precursor_ion_co_loss, + cyclic_precursor_ion_co_loss_and_dehydrated, + cyclic_precursor_ion_co_loss_and_deamidated, + cyclic_precursor_ion_co_loss_and_dehydrated_and_deamidated, + ms_nterminal_ion_hplus, + ms_nterminal_ion_naplus, + ms_nterminal_ion_kplus, + ms_cterminal_ion_hplus, + ms_cterminal_ion_naplus, + ms_cterminal_ion_kplus, + ms_hplus, + ms_naplus, + ms_kplus, + ms_hminus, + ms_MFe2H, + ms_MFe3HNa, + ms_2MFe2H, + ms_2MFe3HNa, + ms_3MFe2H, + ms_3MFe3HNa, + ms_3M2Fe5H, + ms_3M2Fe6HNa, + ms_MFe4H, + ms_2MFe4H, + ms_3MFe4H, + ms_3M2Fe7H, + ms_liplus, + ms_MMgH, + ms_MMg2HNa, + ms_MMg3H, + ms_MAl2H, + ms_MAl3HNa, + ms_MAl4H, + ms_MCaH, + ms_MCa2HNa, + ms_MCa3H, + ms_MMnH, + ms_MMn2HNa, + ms_MMn3H, + ms_MCoH, + ms_MCo2HNa, + ms_MCo3H, + ms_MNiH, + ms_MNi2HNa, + ms_MNi3H, + ms_MCuH, + ms_MCu2HNa, + ms_MCu3H, + ms_MZnH, + ms_MZn2HNa, + ms_MZn3H, + ms_MGa2H, + ms_MGa3HNa, + ms_MGa4H, +#if POLYKETIDE_SIDEROPHORES == 1 + l0h_ion, + l0h_ion_dehydrated, + l0h_ion_deamidated, + l0h_ion_dehydrated_and_deamidated, + l0h_ion_co_loss, + l0h_ion_co_loss_dehydrated, + l0h_ion_co_loss_deamidated, + l0h_ion_co_loss_dehydrated_and_deamidated, + l1h_ion, + l1h_ion_dehydrated, + l1h_ion_deamidated, + l1h_ion_dehydrated_and_deamidated, + l1h_ion_co_loss, + l1h_ion_co_loss_dehydrated, + l1h_ion_co_loss_deamidated, + l1h_ion_co_loss_dehydrated_and_deamidated, + l2h_ion, + l2h_ion_dehydrated, + l2h_ion_deamidated, + l2h_ion_dehydrated_and_deamidated, + l2h_ion_co_loss, + l2h_ion_co_loss_dehydrated, + l2h_ion_co_loss_deamidated, + l2h_ion_co_loss_dehydrated_and_deamidated, + r1h_ion, + r1h_ion_dehydrated, + r1h_ion_deamidated, + r1h_ion_dehydrated_and_deamidated, + r1h_ion_co_loss, + r1h_ion_co_loss_dehydrated, + r1h_ion_co_loss_deamidated, + r1h_ion_co_loss_dehydrated_and_deamidated, + r2h_ion, + r2h_ion_dehydrated, + r2h_ion_deamidated, + r2h_ion_dehydrated_and_deamidated, + r2h_ion_co_loss, + r2h_ion_co_loss_dehydrated, + r2h_ion_co_loss_deamidated, + r2h_ion_co_loss_dehydrated_and_deamidated, + l1oh_ion, + l1oh_ion_dehydrated, + l1oh_ion_deamidated, + l1oh_ion_dehydrated_and_deamidated, + l1oh_ion_co_loss, + l1oh_ion_co_loss_dehydrated, + l1oh_ion_co_loss_deamidated, + l1oh_ion_co_loss_dehydrated_and_deamidated, + l2oh_ion, + l2oh_ion_dehydrated, + l2oh_ion_deamidated, + l2oh_ion_dehydrated_and_deamidated, + l2oh_ion_co_loss, + l2oh_ion_co_loss_dehydrated, + l2oh_ion_co_loss_deamidated, + l2oh_ion_co_loss_dehydrated_and_deamidated, + r1oh_ion, + r1oh_ion_dehydrated, + r1oh_ion_deamidated, + r1oh_ion_dehydrated_and_deamidated, + r1oh_ion_co_loss, + r1oh_ion_co_loss_dehydrated, + r1oh_ion_co_loss_deamidated, + r1oh_ion_co_loss_dehydrated_and_deamidated, + r2oh_ion, + r2oh_ion_dehydrated, + r2oh_ion_deamidated, + r2oh_ion_dehydrated_and_deamidated, + r2oh_ion_co_loss, + r2oh_ion_co_loss_dehydrated, + r2oh_ion_co_loss_deamidated, + r2oh_ion_co_loss_dehydrated_and_deamidated, + linear_polyketide_precursor_ion_h_h, + linear_polyketide_precursor_ion_h_h_dehydrated, + linear_polyketide_precursor_ion_h_h_deamidated, + linear_polyketide_precursor_ion_h_h_dehydrated_and_deamidated, + linear_polyketide_precursor_ion_h_h_co_loss, + linear_polyketide_precursor_ion_h_h_co_loss_dehydrated, + linear_polyketide_precursor_ion_h_h_co_loss_deamidated, + linear_polyketide_precursor_ion_h_h_co_loss_dehydrated_and_deamidated, + linear_polyketide_precursor_ion_h_oh, + linear_polyketide_precursor_ion_h_oh_dehydrated, + linear_polyketide_precursor_ion_h_oh_deamidated, + linear_polyketide_precursor_ion_h_oh_dehydrated_and_deamidated, + linear_polyketide_precursor_ion_h_oh_co_loss, + linear_polyketide_precursor_ion_h_oh_co_loss_dehydrated, + linear_polyketide_precursor_ion_h_oh_co_loss_deamidated, + linear_polyketide_precursor_ion_h_oh_co_loss_dehydrated_and_deamidated, + linear_polyketide_precursor_ion_oh_oh, + linear_polyketide_precursor_ion_oh_oh_dehydrated, + linear_polyketide_precursor_ion_oh_oh_deamidated, + linear_polyketide_precursor_ion_oh_oh_dehydrated_and_deamidated, + linear_polyketide_precursor_ion_oh_oh_co_loss, + linear_polyketide_precursor_ion_oh_oh_co_loss_dehydrated, + linear_polyketide_precursor_ion_oh_oh_co_loss_deamidated, + linear_polyketide_precursor_ion_oh_oh_co_loss_dehydrated_and_deamidated, + cyclic_polyketide_precursor_ion, + cyclic_polyketide_precursor_ion_dehydrated, + cyclic_polyketide_precursor_ion_deamidated, + cyclic_polyketide_precursor_ion_dehydrated_and_deamidated, + cyclic_polyketide_precursor_ion_co_loss, + cyclic_polyketide_precursor_ion_co_loss_dehydrated, + cyclic_polyketide_precursor_ion_co_loss_deamidated, + cyclic_polyketide_precursor_ion_co_loss_dehydrated_and_deamidated, +#endif + //b_ion_2H_loss, fragmentIonTypeEnd }; /** - \brief Register vector by Qt. + \brief Register vector by Qt. */ -Q_DECLARE_METATYPE(vector); +Q_DECLARE_METATYPE(vector); /** @@ -320,7 +491,7 @@ struct fragmentDescription { /** \brief Parent fragment type. */ - fragmentIonType parent; + eFragmentIonType parent; /** @@ -335,6 +506,12 @@ struct fragmentDescription { int multiplier; + /** + \brief Number of implemented isotopes of a biometal ion. + */ + int numberofisotopes; + + /** \brief The default constructor. */ @@ -355,6 +532,7 @@ struct fragmentDescription { parent = fragmentIonTypeEnd; positive = true; multiplier = 1; + numberofisotopes = 0; } @@ -367,9 +545,10 @@ struct fragmentDescription { \param cterminal true when the fragment is C-terminal \param positive true when the fragment is charged positively; false when the fragment is charged negatively \param multiplier the multiplier (n) of M in [nM + H]+ + \param numberofisotopes the number of implemented isotopes of a biometal ion \param parent parent fragment type */ - fragmentDescription(string name, double massdifference, string summary, bool nterminal, bool cterminal, fragmentIonType parent = fragmentIonTypeEnd, bool positive = true, int multiplier = 1) { + fragmentDescription(string name, double massdifference, string summary, bool nterminal, bool cterminal, eFragmentIonType parent = fragmentIonTypeEnd, bool positive = true, int multiplier = 1, int numberofisotopes = 0) { this->name = name; this->massdifference = massdifference; this->summary = summary; @@ -378,6 +557,7 @@ struct fragmentDescription { this->parent = parent; this->positive = positive; this->multiplier = multiplier; + this->numberofisotopes = numberofisotopes; } @@ -408,7 +588,7 @@ Q_DECLARE_METATYPE(vector); */ class cFragmentIons { - map fragmentions; + map fragmentions; public: @@ -433,7 +613,7 @@ class cFragmentIons { \param iontype type of fragment ion \retval fragmentDescription reference to a structure with detailed information about the fragment ion */ - fragmentDescription &operator[](fragmentIonType iontype); + fragmentDescription &operator[](eFragmentIonType iontype); /** @@ -471,35 +651,55 @@ Q_DECLARE_METATYPE(cFragmentIons); \brief Initialize fragment ion types for the de novo graph of cyclic peptides. \param fragmentions reference to a vector of fragment ion types */ -void initializeFragmentIonsForDeNovoGraphOfCyclicPeptides(vector& fragmentions); +void initializeFragmentIonsForDeNovoGraphOfCyclicPeptides(vector& fragmentions); /** \brief Initialize fragment ion types for the de novo graph of linear peptides. \param fragmentions reference to a vector of fragment ion types */ -void initializeFragmentIonsForDeNovoGraphOfLinearPeptides(vector& fragmentions); +void initializeFragmentIonsForDeNovoGraphOfLinearPeptides(vector& fragmentions); /** \brief Initialize fragment ion types for the de novo graph of branched peptides. \param fragmentions reference to a vector of fragment ion types */ -void initializeFragmentIonsForDeNovoGraphOfTPeptides(vector& fragmentions); +void initializeFragmentIonsForDeNovoGraphOfTPeptides(vector& fragmentions); /** \brief Initialize fragment ion types for the de novo graph of branch-cyclic peptides. \param fragmentions reference to a vector of fragment ion types */ -void initializeFragmentIonsForDeNovoGraphOfLassoPeptides(vector& fragmentions); +void initializeFragmentIonsForDeNovoGraphOfBranchCyclicPeptides(vector& fragmentions); + + +#if POLYKETIDE_SIDEROPHORES == 1 + + +/** + \brief Initialize fragment ion types for the de novo graph of a linear polyketide siderophore + \param fragmentions reference to a vector of fragment ion types +*/ +void initializeFragmentIonsForDeNovoGraphOfLinearPolyketideSiderophore(vector& fragmentions); + + +/** + \brief Initialize fragment ion types for the de novo graph of a cyclic polyketide siderophore + \param fragmentions reference to a vector of fragment ion types +*/ +void initializeFragmentIonsForDeNovoGraphOfCyclicPolyketideSiderophore(vector& fragmentions); + + +#endif /** - \brief Initialize fragment ion types for the de novo graph of a custom linear type. + \brief Initialize fragment ion types for the de novo graph of a linear polysaccharide \param fragmentions reference to a vector of fragment ion types */ -void initializeFragmentIonsForDeNovoGraphOfLinearPolysaccharide(vector& fragmentions); +void initializeFragmentIonsForDeNovoGraphOfLinearPolysaccharide(vector& fragmentions); /** diff --git a/CycloBranch/core/cImzML.cpp b/CycloBranch/core/cImzML.cpp new file mode 100644 index 0000000..80476fe --- /dev/null +++ b/CycloBranch/core/cImzML.cpp @@ -0,0 +1,228 @@ +#include "core/cImzML.h" + + +cImzML::cImzML() { + XMLPlatformUtils::Initialize(); + parser = new XercesDOMParser(); +} + + +cImzML::~cImzML() { + delete parser; + XMLPlatformUtils::Terminate(); +} + + +void cImzML::parse(string filename) { + + parser->parse(filename.c_str()); + DOMDocument* document = parser->getDocument(); + + DOMElement* root = document->getDocumentElement(); + if (!root) { + return; + } + + + // childrens of mzML + for (XMLSize_t i = 0; i < root->getChildNodes()->getLength(); i++) { + + DOMNode* currentNode1 = root->getChildNodes()->item(i); + if (currentNode1->getNodeType() && currentNode1->getNodeType() == DOMNode::ELEMENT_NODE) { + + DOMElement* currentElement1 = dynamic_cast(currentNode1); + if (XMLString::equals(currentElement1->getTagName(), XMLString::transcode("run"))) { + + + // childrens of run + for (XMLSize_t j = 0; j < currentNode1->getChildNodes()->getLength(); j++) { + + DOMNode* currentNode2 = currentNode1->getChildNodes()->item(j); + if (currentNode2->getNodeType() && currentNode2->getNodeType() == DOMNode::ELEMENT_NODE) { + + DOMElement* currentElement2 = dynamic_cast(currentNode2); + if (XMLString::equals(currentElement2->getTagName(), XMLString::transcode("spectrumList"))) { + + + // childrens of spectrumList + for (XMLSize_t k = 0; k < currentNode2->getChildNodes()->getLength(); k++) { + + DOMNode* currentNode3 = currentNode2->getChildNodes()->item(k); + if (currentNode3->getNodeType() && currentNode3->getNodeType() == DOMNode::ELEMENT_NODE) { + + DOMElement* currentElement3 = dynamic_cast(currentNode3); + if (XMLString::equals(currentElement3->getTagName(), XMLString::transcode("spectrum"))) { + + + cImzMLItem imzmlitem; + + + // childrens of spectrum + for (XMLSize_t l = 0; l < currentNode3->getChildNodes()->getLength(); l++) { + + DOMNode* currentNode4 = currentNode3->getChildNodes()->item(l); + if (currentNode4->getNodeType() && currentNode4->getNodeType() == DOMNode::ELEMENT_NODE) { + + DOMElement* currentElement4 = dynamic_cast(currentNode4); + if (XMLString::equals(currentElement4->getTagName(), XMLString::transcode("scanList"))) { + + + // childrens of scanList + for (XMLSize_t m = 0; m < currentNode4->getChildNodes()->getLength(); m++) { + + DOMNode* currentNode5 = currentNode4->getChildNodes()->item(m); + if (currentNode5->getNodeType() && currentNode5->getNodeType() == DOMNode::ELEMENT_NODE) { + + DOMElement* currentElement5 = dynamic_cast(currentNode5); + if (XMLString::equals(currentElement5->getTagName(), XMLString::transcode("scan"))) { + + + // childrens of scan + for (XMLSize_t n = 0; n < currentNode5->getChildNodes()->getLength(); n++) { + + DOMNode* currentNode6 = currentNode5->getChildNodes()->item(n); + if (currentNode6->getNodeType() && currentNode6->getNodeType() == DOMNode::ELEMENT_NODE) { + + DOMElement* currentElement6 = dynamic_cast(currentNode6); + if (XMLString::equals(currentElement6->getTagName(), XMLString::transcode("cvParam"))) { + + + const XMLCh* xmlch_name = currentElement6->getAttribute(XMLString::transcode("name")); + string name = XMLString::transcode(xmlch_name); + + const XMLCh* xmlch_value = currentElement6->getAttribute(XMLString::transcode("value")); + if (name.compare("position x") == 0) { + imzmlitem.x = atoi(XMLString::transcode(xmlch_value)); + } + + if (name.compare("position y") == 0) { + imzmlitem.y = atoi(XMLString::transcode(xmlch_value)); + } + + + } + + } + + } + + + } + + } + + } + + + } + + if (XMLString::equals(currentElement4->getTagName(), XMLString::transcode("binaryDataArrayList"))) { + + + // childrens of binaryDataArrayList + for (XMLSize_t m = 0; m < currentNode4->getChildNodes()->getLength(); m++) { + + DOMNode* currentNode5 = currentNode4->getChildNodes()->item(m); + if (currentNode5->getNodeType() && currentNode5->getNodeType() == DOMNode::ELEMENT_NODE) { + + DOMElement* currentElement5 = dynamic_cast(currentNode5); + if (XMLString::equals(currentElement5->getTagName(), XMLString::transcode("binaryDataArray"))) { + + + // childrens of binaryDataArray + bool mzarray = false; + unsigned long long offset = 0; + unsigned long long length = 0; + for (XMLSize_t n = 0; n < currentNode5->getChildNodes()->getLength(); n++) { + + DOMNode* currentNode6 = currentNode5->getChildNodes()->item(n); + if (currentNode6->getNodeType() && currentNode6->getNodeType() == DOMNode::ELEMENT_NODE) { + + DOMElement* currentElement6 = dynamic_cast(currentNode6); + if (XMLString::equals(currentElement6->getTagName(), XMLString::transcode("cvParam"))) { + + + const XMLCh* xmlch_name = currentElement6->getAttribute(XMLString::transcode("name")); + string name = XMLString::transcode(xmlch_name); + + const XMLCh* xmlch_value = currentElement6->getAttribute(XMLString::transcode("value")); + if (name.compare("external offset") == 0) { + offset = stoull(XMLString::transcode(xmlch_value)); + } + + if (name.compare("external encoded length") == 0) { + length = stoull(XMLString::transcode(xmlch_value)); + } + + + } + + if (XMLString::equals(currentElement6->getTagName(), XMLString::transcode("referenceableParamGroupRef"))) { + + const XMLCh* xmlch_ref = currentElement6->getAttribute(XMLString::transcode("ref")); + string ref = XMLString::transcode(xmlch_ref); + + if (ref.compare("mzArray") == 0) { + mzarray = true; + } + + } + + } + + } + + if (mzarray) { + imzmlitem.mzstart = offset; + imzmlitem.mzlength = length; + } + else { + imzmlitem.intensitystart = offset; + imzmlitem.intensitylength = length; + } + + } + + } + + } + + + } + + } + + } + + + imzmlitems.push_back(imzmlitem); + + + } + + } + + } + + + } + + } + + } + + + } + + } + + } + + +} + + +vector& cImzML::getItems() { + return imzmlitems; +} + diff --git a/CycloBranch/core/cImzML.h b/CycloBranch/core/cImzML.h new file mode 100644 index 0000000..468d6df --- /dev/null +++ b/CycloBranch/core/cImzML.h @@ -0,0 +1,117 @@ +/** + \file cImzML.h + \brief imzML parser. +*/ + + +#ifndef _CIMZML_H +#define _CIMZML_H + + +#include +#include +#include +#include + +using namespace std; +using namespace xercesc; + + +/** + \brief A parsed imzML item. +*/ +struct cImzMLItem { + + /** + \brief Image - x coordinate. + */ + int x; + + + /** + \brief Image - y coordinate. + */ + int y; + + + /** + \brief Starting offset of m/z values array in ibd file (in bytes). + */ + long long unsigned mzstart; + + + /** + \brief Length of m/z values array in ibd file (in bytes). + */ + long long unsigned mzlength; + + + /** + \brief Starting offset of intensity values array in ibd file (in bytes). + */ + long long unsigned intensitystart; + + + /** + \brief Length of intensity values array in ibd file (in bytes). + */ + long long unsigned intensitylength; + + + /** + \brief The constructor. + */ + cImzMLItem() { + x = 0; + y = 0; + mzstart = 0; + mzlength = 0; + intensitystart = 0; + intensitylength = 0; + } + +}; + + + +/** + \brief imzML parser. +*/ +class cImzML { + + XercesDOMParser* parser; + vector imzmlitems; + +public: + + + /** + \brief The constructor. + */ + cImzML(); + + + /** + \brief The destructor. + */ + ~cImzML(); + + + /** + \brief Parse an imzml file. + \param filename imzml filename + */ + void parse(string filename); + + + /** + \brief Get a vector of cImzML items. + \retval vector a vector of cImzML items + */ + vector& getItems(); + +}; + + +#endif + diff --git a/CycloBranch/core/cParameters.cpp b/CycloBranch/core/cParameters.cpp index c0eb7db..e1aeac7 100644 --- a/CycloBranch/core/cParameters.cpp +++ b/CycloBranch/core/cParameters.cpp @@ -14,9 +14,23 @@ void cParameters::clear() { fragmentdefinitions.recalculateFragments(false, false, s); peptidetype = linear; peaklistfilename = ""; - peaklist.clear(); + peaklistfileformat = txt; + peaklistseries.clear(); precursormass = 0; precursoradduct = ""; + precursorAdductHasLi = false; + precursorAdductHasNa = false; + precursorAdductHasMg = false; + precursorAdductHasAl = false; + precursorAdductHasK = false; + precursorAdductHasCa = false; + precursorAdductHasMn = false; + precursorAdductHasFe = false; + precursorAdductHasCo = false; + precursorAdductHasNi = false; + precursorAdductHasCu = false; + precursorAdductHasZn = false; + precursorAdductHasGa = false; precursormasserrortolerance = 5; precursorcharge = 1; fragmentmasserrortolerance = 5; @@ -59,18 +73,20 @@ void cParameters::clear() { } -int cParameters::checkAndPrepare() { +int cParameters::checkAndPrepare(bool& terminatecomputation) { bool error = false; string errormessage = ""; ifstream peakliststream; + ifstream spotliststream; ifstream bricksdatabasestream; ifstream modificationsstream; ifstream sequencedatabasestream; regex rx; - ePeakListFileFormat peaklistfileformat; string s; int i; + string foldername; + string ibdfilename; if (peaklistfilename.empty()) { error = true; @@ -108,8 +124,20 @@ int cParameters::checkAndPrepare() { if (regex_search(peaklistfilename, rx)) { peaklistfileformat = baf; } + + rx = "\\.[mM][iI][sS]$"; + // flexImaging File + if (regex_search(peaklistfilename, rx)) { + peaklistfileformat = mis; + } #endif + rx = "\\.[iI][mM][zZ][mM][lL]$"; + // flexImaging File + if (regex_search(peaklistfilename, rx)) { + peaklistfileformat = imzML; + } + } catch (regex_error& e) { error = true; @@ -192,6 +220,33 @@ int cParameters::checkAndPrepare() { } #endif break; + case mis: + #if OS_TYPE == WIN + foldername = peaklistfilename.substr(0, peaklistfilename.rfind('.')); + *os << "Converting flexImaging data folder " + foldername + " ... "; + s = "External\\windows\\mis2csv.bat \"" + foldername + "\""; + if (system(s.c_str()) != 0) { + error = true; + errormessage = "The folder cannot be converted.\n"; + errormessage += "Does the folder '" + foldername + "' exist ?\n"; + errormessage += "Do you have Bruker Daltonik's CompassXport installed ?\n"; + errormessage += "Do you have path to the CompassXport.exe in your PATH variable ?\n"; + errormessage += "Is the directory with the file '" + peaklistfilename + "' writable ?\n"; + errormessage += "Do you have 'mis2csv.bat' file located in the 'External/windows' folder ?\n"; + } + + if (!error) { + *os << "ok" << endl << endl; + peakliststream.open(foldername + ".baf.csv"); + spotliststream.open(foldername + ".baf.txt"); + } + #endif + break; + case imzML: + ibdfilename = peaklistfilename.substr(0, (int)peaklistfilename.size() - 5); + ibdfilename += "ibd"; + peakliststream.open(ibdfilename, std::ifstream::binary); + break; default: break; } @@ -202,7 +257,12 @@ int cParameters::checkAndPrepare() { if (!error) { if (!peakliststream.good()) { error = true; - errormessage = "Cannot open the file '" + peaklistfilename + "'."; + if (peaklistfileformat == mis) { + errormessage = "Cannot open the folder '" + foldername + "'."; + } + else { + errormessage = "Cannot open the file '" + peaklistfilename + "'."; + } } else { if (os) { @@ -210,18 +270,31 @@ int cParameters::checkAndPrepare() { } switch (peaklistfileformat) { case txt: - peaklist.loadFromPlainTextStream(peakliststream); + peaklistseries.loadFromPlainTextStream(peakliststream); break; case mzML: case mzXML: case mgf: - peaklist.loadFromMGFStream(peakliststream); + peaklistseries.loadFromMGFStream(peakliststream); break; case baf: #if OS_TYPE == WIN - peaklist.loadFromBAFStream(peakliststream); + peaklistseries.loadFromBAFStream(peakliststream); #endif break; + case mis: + #if OS_TYPE == WIN + peaklistseries.loadFromBAFStream(peakliststream); + peaklistseries.loadSpotList(spotliststream); + spotliststream.close(); + #endif + break; + case imzML: + if (peaklistseries.loadFromIMZMLStream(peaklistfilename, peakliststream, minimumrelativeintensitythreshold, os, terminatecomputation) == -1) { + error = true; + errormessage = "Aborted by user."; + } + break; default: break; } @@ -330,6 +403,73 @@ int cParameters::checkAndPrepare() { error = true; errormessage = "Precursor Ion Adduct: " + errormessage; } + + precursorAdductHasLi = false; + precursorAdductHasNa = false; + precursorAdductHasMg = false; + precursorAdductHasAl = false; + precursorAdductHasK = false; + precursorAdductHasCa = false; + precursorAdductHasMn = false; + precursorAdductHasFe = false; + precursorAdductHasCo = false; + precursorAdductHasNi = false; + precursorAdductHasCu = false; + precursorAdductHasZn = false; + precursorAdductHasGa = false; + + rx = "Li"; + if (regex_search(precursoradduct, rx)) { + precursorAdductHasLi = true; + } + rx = "Na"; + if (regex_search(precursoradduct, rx)) { + precursorAdductHasNa = true; + } + rx = "Mg"; + if (regex_search(precursoradduct, rx)) { + precursorAdductHasMg = true; + } + rx = "Al"; + if (regex_search(precursoradduct, rx)) { + precursorAdductHasAl = true; + } + rx = "K"; + if (regex_search(precursoradduct, rx)) { + precursorAdductHasK = true; + } + rx = "Ca"; + if (regex_search(precursoradduct, rx)) { + precursorAdductHasCa = true; + } + rx = "Mn"; + if (regex_search(precursoradduct, rx)) { + precursorAdductHasMn = true; + } + rx = "Fe"; + if (regex_search(precursoradduct, rx)) { + precursorAdductHasFe = true; + } + rx = "Co"; + if (regex_search(precursoradduct, rx)) { + precursorAdductHasCo = true; + } + rx = "Ni"; + if (regex_search(precursoradduct, rx)) { + precursorAdductHasNi = true; + } + rx = "Cu"; + if (regex_search(precursoradduct, rx)) { + precursorAdductHasCu = true; + } + rx = "Zn"; + if (regex_search(precursoradduct, rx)) { + precursorAdductHasZn = true; + } + rx = "Ga"; + if (regex_search(precursoradduct, rx)) { + precursorAdductHasGa = true; + } } @@ -369,9 +509,17 @@ int cParameters::checkAndPrepare() { case branched: initializeFragmentIonsForDeNovoGraphOfTPeptides(fragmentionsfordenovograph); break; - case lasso: - initializeFragmentIonsForDeNovoGraphOfLassoPeptides(fragmentionsfordenovograph); + case branchcyclic: + initializeFragmentIonsForDeNovoGraphOfBranchCyclicPeptides(fragmentionsfordenovograph); + break; +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + initializeFragmentIonsForDeNovoGraphOfLinearPolyketideSiderophore(fragmentionsfordenovograph); + break; + case cyclicpolyketide: + initializeFragmentIonsForDeNovoGraphOfCyclicPolyketideSiderophore(fragmentionsfordenovograph); break; +#endif case linearpolysaccharide: initializeFragmentIonsForDeNovoGraphOfLinearPolysaccharide(fragmentionsfordenovograph); break; @@ -430,9 +578,17 @@ string cParameters::printToString() { case branched: s += "Branched\n"; break; - case lasso: + case branchcyclic: s += "Branch-cyclic\n"; break; +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + s += "Linear oligoketide siderophore\n"; + break; + case cyclicpolyketide: + s += "Cyclic oligoketide siderophore\n"; + break; +#endif case linearpolysaccharide: s += "Linear polysaccharide (beta version)\n"; break; @@ -494,7 +650,7 @@ string cParameters::printToString() { s += "\n"; s += "Mode: "; - switch ((modeType)mode) { + switch ((eModeType)mode) { case denovoengine: s += "De Novo Search Engine"; break; @@ -551,7 +707,7 @@ string cParameters::printToString() { s += "Maximum Number of Candidate Peptides Reported: " + to_string(hitsreported) + "\n"; s += "Peptide Sequence Tag: " + originalsequencetag + "\n"; - s += "Fragment Ion Types in Theoretical Spectra: "; + s += "Ion Types in Theoretical Spectra: "; for (int i = 0; i < (int)fragmentionsfortheoreticalspectra.size(); i++) { s += fragmentdefinitions[fragmentionsfortheoreticalspectra[i]].name; if (i < (int)fragmentionsfortheoreticalspectra.size() - 1) { @@ -595,14 +751,28 @@ void cParameters::store(ofstream& os) { fragmentdefinitions.store(os); - os.write((char *)&peptidetype, sizeof(peptideType)); + os.write((char *)&peptidetype, sizeof(ePeptideType)); storeString(peaklistfilename, os); - peaklist.store(os); + os.write((char *)&peaklistfileformat, sizeof(ePeakListFileFormat)); + peaklistseries.store(os); os.write((char *)&precursormass, sizeof(double)); storeString(precursoradduct, os); + os.write((char *)&precursorAdductHasLi, sizeof(bool)); + os.write((char *)&precursorAdductHasNa, sizeof(bool)); + os.write((char *)&precursorAdductHasMg, sizeof(bool)); + os.write((char *)&precursorAdductHasAl, sizeof(bool)); + os.write((char *)&precursorAdductHasK, sizeof(bool)); + os.write((char *)&precursorAdductHasCa, sizeof(bool)); + os.write((char *)&precursorAdductHasMn, sizeof(bool)); + os.write((char *)&precursorAdductHasFe, sizeof(bool)); + os.write((char *)&precursorAdductHasCo, sizeof(bool)); + os.write((char *)&precursorAdductHasNi, sizeof(bool)); + os.write((char *)&precursorAdductHasCu, sizeof(bool)); + os.write((char *)&precursorAdductHasZn, sizeof(bool)); + os.write((char *)&precursorAdductHasGa, sizeof(bool)); os.write((char *)&precursormasserrortolerance, sizeof(double)); os.write((char *)&precursorcharge, sizeof(int)); @@ -629,8 +799,8 @@ void cParameters::store(ofstream& os) { } os.write((char *)&maximumnumberofthreads, sizeof(int)); - os.write((char *)&mode, sizeof(modeType)); - os.write((char *)&scoretype, sizeof(scoreType)); + os.write((char *)&mode, sizeof(eModeType)); + os.write((char *)&scoretype, sizeof(eScoreType)); os.write((char *)&clearhitswithoutparent, sizeof(bool)); os.write((char *)&cyclicnterminus, sizeof(bool)); os.write((char *)&cycliccterminus, sizeof(bool)); @@ -655,13 +825,13 @@ void cParameters::store(ofstream& os) { size = (int)fragmentionsfordenovograph.size(); os.write((char *)&size, sizeof(int)); for (int i = 0; i < (int)fragmentionsfordenovograph.size(); i++) { - os.write((char *)&fragmentionsfordenovograph[i], sizeof(fragmentIonType)); + os.write((char *)&fragmentionsfordenovograph[i], sizeof(eFragmentIonType)); } size = (int)fragmentionsfortheoreticalspectra.size(); os.write((char *)&size, sizeof(int)); for (int i = 0; i < (int)fragmentionsfortheoreticalspectra.size(); i++) { - os.write((char *)&fragmentionsfortheoreticalspectra[i], sizeof(fragmentIonType)); + os.write((char *)&fragmentionsfortheoreticalspectra[i], sizeof(eFragmentIonType)); } } @@ -673,14 +843,28 @@ void cParameters::load(ifstream& is) { fragmentdefinitions.load(is); - is.read((char *)&peptidetype, sizeof(peptideType)); + is.read((char *)&peptidetype, sizeof(ePeptideType)); loadString(peaklistfilename, is); - peaklist.load(is); + is.read((char *)&peaklistfileformat, sizeof(ePeakListFileFormat)); + peaklistseries.load(is); is.read((char *)&precursormass, sizeof(double)); loadString(precursoradduct, is); + is.read((char *)&precursorAdductHasLi, sizeof(bool)); + is.read((char *)&precursorAdductHasNa, sizeof(bool)); + is.read((char *)&precursorAdductHasMg, sizeof(bool)); + is.read((char *)&precursorAdductHasAl, sizeof(bool)); + is.read((char *)&precursorAdductHasK, sizeof(bool)); + is.read((char *)&precursorAdductHasCa, sizeof(bool)); + is.read((char *)&precursorAdductHasMn, sizeof(bool)); + is.read((char *)&precursorAdductHasFe, sizeof(bool)); + is.read((char *)&precursorAdductHasCo, sizeof(bool)); + is.read((char *)&precursorAdductHasNi, sizeof(bool)); + is.read((char *)&precursorAdductHasCu, sizeof(bool)); + is.read((char *)&precursorAdductHasZn, sizeof(bool)); + is.read((char *)&precursorAdductHasGa, sizeof(bool)); is.read((char *)&precursormasserrortolerance, sizeof(double)); is.read((char *)&precursorcharge, sizeof(int)); @@ -707,8 +891,8 @@ void cParameters::load(ifstream& is) { } is.read((char *)&maximumnumberofthreads, sizeof(int)); - is.read((char *)&mode, sizeof(modeType)); - is.read((char *)&scoretype, sizeof(scoreType)); + is.read((char *)&mode, sizeof(eModeType)); + is.read((char *)&scoretype, sizeof(eScoreType)); is.read((char *)&clearhitswithoutparent, sizeof(bool)); is.read((char *)&cyclicnterminus, sizeof(bool)); is.read((char *)&cycliccterminus, sizeof(bool)); @@ -733,13 +917,13 @@ void cParameters::load(ifstream& is) { is.read((char *)&size, sizeof(int)); fragmentionsfordenovograph.resize(size); for (int i = 0; i < (int)fragmentionsfordenovograph.size(); i++) { - is.read((char *)&fragmentionsfordenovograph[i], sizeof(fragmentIonType)); + is.read((char *)&fragmentionsfordenovograph[i], sizeof(eFragmentIonType)); } is.read((char *)&size, sizeof(int)); fragmentionsfortheoreticalspectra.resize(size); for (int i = 0; i < (int)fragmentionsfortheoreticalspectra.size(); i++) { - is.read((char *)&fragmentionsfortheoreticalspectra[i], sizeof(fragmentIonType)); + is.read((char *)&fragmentionsfortheoreticalspectra[i], sizeof(eFragmentIonType)); } } diff --git a/CycloBranch/core/cParameters.h b/CycloBranch/core/cParameters.h index 9bafc84..242b149 100644 --- a/CycloBranch/core/cParameters.h +++ b/CycloBranch/core/cParameters.h @@ -10,12 +10,14 @@ #include #include #include "core/utilities.h" -#include "core/cPeaksList.h" +#include "core/cPeakListSeries.h" #include "core/cBricksDatabase.h" #include "core/cSequenceDatabase.h" + class cMainThread; + using namespace std; using namespace boost; @@ -23,7 +25,7 @@ using namespace boost; /** \brief Running modes of the application. */ -enum modeType { +enum eModeType { denovoengine = 0, singlecomparison = 1, databasesearch = 2, @@ -39,7 +41,9 @@ enum ePeakListFileFormat { mgf = 1, mzML = 2, mzXML = 3, - baf = 4 + baf = 4, + mis = 5, + imzML = 6 }; @@ -62,7 +66,7 @@ class cParameters { /** \brief The type of analyzed peptide. */ - peptideType peptidetype; + ePeptideType peptidetype; /** @@ -72,9 +76,15 @@ class cParameters { /** - \brief A structure representing a peak list. + \brief A file format of peak list. + */ + ePeakListFileFormat peaklistfileformat; + + + /** + \brief A structure representing a series of peaklists. */ - cPeaksList peaklist; + cPeakListSeries peaklistseries; /** @@ -89,6 +99,84 @@ class cParameters { string precursoradduct; + /** + \brief True when the precursor adduct contains Li. + */ + bool precursorAdductHasLi; + + + /** + \brief True when the precursor adduct contains Na. + */ + bool precursorAdductHasNa; + + + /** + \brief True when the precursor adduct contains Mg. + */ + bool precursorAdductHasMg; + + + /** + \brief True when the precursor adduct contains Al. + */ + bool precursorAdductHasAl; + + + /** + \brief True when the precursor adduct contains K. + */ + bool precursorAdductHasK; + + + /** + \brief True when the precursor adduct contains Ca. + */ + bool precursorAdductHasCa; + + + /** + \brief True when the precursor adduct contains Mn. + */ + bool precursorAdductHasMn; + + + /** + \brief True when the precursor adduct contains Fe. + */ + bool precursorAdductHasFe; + + + /** + \brief True when the precursor adduct contains Co. + */ + bool precursorAdductHasCo; + + + /** + \brief True when the precursor adduct contains Ni. + */ + bool precursorAdductHasNi; + + + /** + \brief True when the precursor adduct contains Cu. + */ + bool precursorAdductHasCu; + + + /** + \brief True when the precursor adduct contains Zn. + */ + bool precursorAdductHasZn; + + + /** + \brief True when the precursor adduct contains Ga. + */ + bool precursorAdductHasGa; + + /** \brief Precursor mass error tolerance (ppm). */ @@ -188,13 +276,13 @@ class cParameters { /** \brief Program mode. */ - modeType mode; + eModeType mode; /** \brief A type of score. */ - scoreType scoretype; + eScoreType scoretype; /** @@ -308,13 +396,13 @@ class cParameters { /** \brief A vector of fragment ion types generated in the de novo graph. */ - vector fragmentionsfordenovograph; + vector fragmentionsfordenovograph; /** \brief A vector of fragment ion types generated in theoretical spectra. */ - vector fragmentionsfortheoreticalspectra; + vector fragmentionsfortheoreticalspectra; /** @@ -331,9 +419,10 @@ class cParameters { /** \brief Test restrictions of parameters and prepare some internal parameters. + \param terminatecomputation reference to a variable determining that the computation must be stopped \retval int -1 when an error occurred, 0 otherwise */ - int checkAndPrepare(); + int checkAndPrepare(bool& terminatecomputation); /** diff --git a/CycloBranch/core/cPeak.cpp b/CycloBranch/core/cPeak.cpp index 605ced6..2cb4dd1 100644 --- a/CycloBranch/core/cPeak.cpp +++ b/CycloBranch/core/cPeak.cpp @@ -10,7 +10,9 @@ void cPeak::clear() { mzratio = 0; intensity = 0; description = ""; - matchdescription = ""; + matchedmz = 0; + matchedintensity = 0; + matchedppm = 0; iontype = fragmentIonTypeEnd; matched = 0; matchedid = -1; @@ -34,8 +36,10 @@ void cPeak::store(ofstream& os) { os.write((char *)&mzratio, sizeof(double)); os.write((char *)&intensity, sizeof(double)); storeString(description, os); - storeString(matchdescription, os); - os.write((char *)&iontype, sizeof(fragmentIonType)); + os.write((char *)&matchedmz, sizeof(double)); + os.write((char *)&matchedintensity, sizeof(double)); + os.write((char *)&matchedppm, sizeof(double)); + os.write((char *)&iontype, sizeof(eFragmentIonType)); os.write((char *)&matched, sizeof(int)); os.write((char *)&matchedid, sizeof(int)); os.write((char *)&charge, sizeof(int)); @@ -51,8 +55,10 @@ void cPeak::load(ifstream& is) { is.read((char *)&mzratio, sizeof(double)); is.read((char *)&intensity, sizeof(double)); loadString(description, is); - loadString(matchdescription, is); - is.read((char *)&iontype, sizeof(fragmentIonType)); + is.read((char *)&matchedmz, sizeof(double)); + is.read((char *)&matchedintensity, sizeof(double)); + is.read((char *)&matchedppm, sizeof(double)); + is.read((char *)&iontype, sizeof(eFragmentIonType)); is.read((char *)&matched, sizeof(int)); is.read((char *)&matchedid, sizeof(int)); is.read((char *)&charge, sizeof(int)); diff --git a/CycloBranch/core/cPeak.h b/CycloBranch/core/cPeak.h index cf38944..e4226b4 100644 --- a/CycloBranch/core/cPeak.h +++ b/CycloBranch/core/cPeak.h @@ -18,7 +18,7 @@ using namespace std; /** \brief The types of scores between an experimental and a theoretical spectrum. */ -enum scoreType { +enum eScoreType { b_ions = 0, b_ions_and_b_dehydrated_ions = 1, b_ions_and_b_deamidated_ions = 2, @@ -54,15 +54,27 @@ struct cPeak { /** - \brief An extended description when the peak is matched. + \brief A m/z ratio when the peak was matched with an experimental peak. */ - string matchdescription; + double matchedmz; + + + /** + \brief An intensity when the peak was matched with an experimental peak. + */ + double matchedintensity; + + + /** + \brief An error in ppm when the peak was matched with an experimental peak. + */ + double matchedppm; /** \brief The type of a fragment ion corresponding to the peak. */ - fragmentIonType iontype; + eFragmentIonType iontype; /** diff --git a/CycloBranch/core/cPeakListSeries.cpp b/CycloBranch/core/cPeakListSeries.cpp new file mode 100644 index 0000000..fb2ee2e --- /dev/null +++ b/CycloBranch/core/cPeakListSeries.cpp @@ -0,0 +1,171 @@ +#include "core/cPeakListSeries.h" + +#include "gui/cMainThread.h" + + +cPeakListSeries::cPeakListSeries() { + clear(); +} + + +cPeakListSeries::cPeakListSeries(const cPeakListSeries& peaklistseries) { + *this = peaklistseries; +} + + +cPeakListSeries& cPeakListSeries::operator=(const cPeakListSeries& peaklistseries) { + peaklists = peaklistseries.peaklists; + return *this; +} + + +cPeaksList& cPeakListSeries::operator[](int position) { + return peaklists[position]; +} + + +void cPeakListSeries::clear() { + peaklists.clear(); +} + + +void cPeakListSeries::addPeakList(cPeaksList& peaklist) { + peaklists.push_back(peaklist); +} + + +void cPeakListSeries::loadFromPlainTextStream(ifstream &stream) { + while (stream.good()) { + cPeaksList peaklist; + peaklist.loadFromPlainTextStream(stream); + + if (peaklist.size() > 0) { + peaklists.push_back(peaklist); + } + } +} + + +void cPeakListSeries::loadFromBAFStream(ifstream &stream) { + string s; + while (stream.good() && !(strstr(s.c_str(),"M/Z"))) { + getline(stream,s); + } + + while (stream.good()) { + cPeaksList peaklist; + peaklist.loadFromBAFStream(stream); + + if (peaklist.size() > 0) { + peaklists.push_back(peaklist); + } + } +} + + +int cPeakListSeries::loadFromIMZMLStream(string& imzmlfilename, ifstream &ibdstream, double minimumrelativeintensitythreshold, cMainThread* os, bool& terminatecomputation) { + *os << "Parsing the imzML file ..." << endl; + + cImzML imzml; + imzml.parse(imzmlfilename); + + *os << "Loading the peaklist no. : " << endl; + + for (int i = 0; i < (int)imzml.getItems().size(); i++) { + *os << i + 1 << " "; + if ((i + 1) % 25 == 0) { + *os << endl; + } + + cPeaksList peaklist; + peaklist.loadFromIBDStream(imzml.getItems()[i], ibdstream); + if (peaklist.size() > 0) { + peaklist.normalizeIntenzity(); // to do - redundant operation + peaklist.cropIntenzity(minimumrelativeintensitythreshold); // to do - redundant operation + peaklists.push_back(peaklist); + } + + if (terminatecomputation) { + peaklists.clear(); + return -1; + } + } + + *os << " ok" << endl; + + return 0; +} + + +void cPeakListSeries::loadSpotList(ifstream &stream) { + string s; + vector spots; + while (stream.good()) { + getline(stream,s); + if (!s.empty()) { + spots.push_back(s); + } + } + + if (peaklists.size() != spots.size()) { + return; + } + + size_t px, py; + int x, y; + for (int i = 0; i < (int)peaklists.size(); i++) { + px = spots[i].find('X'); + py = spots[i].find('Y'); + + if ((px == string::npos) || (py == string::npos)) { + return; + } + + x = stoi(spots[i].substr(0, py).substr(px + 1)); + y = stoi(spots[i].substr(py + 1)); + + peaklists[i].setCoordinates(x, y); + } +} + + +void cPeakListSeries::loadFromMGFStream(ifstream &stream) { + while (stream.good()) { + cPeaksList peaklist; + peaklist.loadFromMGFStream(stream); + + if (peaklist.size() > 0) { + peaklists.push_back(peaklist); + } + } +} + + +int cPeakListSeries::size() { + return (int)peaklists.size(); +} + + +void cPeakListSeries::store(ofstream& os) { + int size; + + size = (int)peaklists.size(); + os.write((char *)&size, sizeof(int)); + + for (int i = 0; i < size; i++) { + peaklists[i].store(os); + } +} + + +void cPeakListSeries::load(ifstream& is) { + int size; + + is.read((char *)&size, sizeof(int)); + peaklists.resize(size); + + for (int i = 0; i < size; i++) { + peaklists[i].load(is); + } +} + diff --git a/CycloBranch/core/cPeakListSeries.h b/CycloBranch/core/cPeakListSeries.h new file mode 100644 index 0000000..f44c10b --- /dev/null +++ b/CycloBranch/core/cPeakListSeries.h @@ -0,0 +1,130 @@ +/** + \file cPeakListSeries.h + \brief The representation of a series of peaklists. +*/ + + +#ifndef _CPEAKLISTSERIES_H +#define _CPEAKLISTSERIES_H + +#include "core/cPeaksList.h" + + +class cMainThread; + + +/** + \brief The class representing a series of peaklists. +*/ +class cPeakListSeries { + + vector peaklists; + +public: + + + /** + \brief The constructor. + */ + cPeakListSeries(); + + + /** + \brief The copy constructor. + \param peaklistseries reference to a cPeakListSeries + */ + cPeakListSeries(const cPeakListSeries& peaklistseries); + + + /** + \brief Overloaded operator=. + \param peaklistseries reference to a cPeakListSeries + */ + cPeakListSeries& operator=(const cPeakListSeries& peaklistseries); + + + /** + \brief Overloaded operator []. + \param position position of a peaklist + \retval cPeaksList reference to a peaklist + */ + cPeaksList& operator[](int position); + + + /** + \brief Clear the peaklist series. + */ + void clear(); + + + /** + \brief Add a peaklist. + \param peaklist peaklist which will be added + */ + void addPeakList(cPeaksList& peaklist); + + + /** + \brief Load the series of peaklists from a plain text stream. + \param stream source stream + */ + void loadFromPlainTextStream(ifstream &stream); + + + /** + \brief Load the series of peaklists from a .csv file converted from a .baf file by CompassXport. + \param stream source stream + */ + void loadFromBAFStream(ifstream &stream); + + + /** + \brief Load the series of peaklists from .imzML file. + \param imzmlfilename imzML filename + \param ibdstream ibd binary file stream + \param minimumrelativeintensitythreshold a minimum threshold of relative intensity + \param os pointer to the main thread of the application (output stream) + \param terminatecomputation reference to a variable determining that the computation must be stopped + \retval int 0 = success; -1 = error + */ + int loadFromIMZMLStream(string& imzmlfilename, ifstream &ibdstream, double minimumrelativeintensitythreshold, cMainThread* os, bool& terminatecomputation); + + + /** + \brief Load a spot list from a stream. + \param stream source stream + */ + void loadSpotList(ifstream &stream); + + + /** + \brief Load the series of peaklists from a .mgf file. + \param stream source stream + */ + void loadFromMGFStream(ifstream &stream); + + + /** + \brief The number of peaklists in the series. + \retval int number of peaklists in the series + */ + int size(); + + + /** + \brief Store the structure into an output stream. + \param os an output stream + */ + void store(ofstream& os); + + + /** + \brief Load the structure from an input stream. + \param is an input stream + */ + void load(ifstream& is); + +}; + + +#endif diff --git a/CycloBranch/core/cPeaksList.cpp b/CycloBranch/core/cPeaksList.cpp index bef1b46..aef3fe3 100644 --- a/CycloBranch/core/cPeaksList.cpp +++ b/CycloBranch/core/cPeaksList.cpp @@ -40,12 +40,16 @@ cPeaksList::cPeaksList(const cPeaksList& peakslist) { cPeaksList& cPeaksList::operator=(const cPeaksList& peakslist) { peaks = peakslist.peaks; + x = peakslist.x; + y = peakslist.y; return *this; } void cPeaksList::clear() { peaks.clear(); + x = 0; + y = 0; } @@ -85,6 +89,9 @@ void cPeaksList::loadFromPlainTextStream(ifstream &stream) { if (!p.empty()) { peaks.push_back(p); } + else { + break; + } } } @@ -94,19 +101,16 @@ void cPeaksList::loadFromBAFStream(ifstream &stream) { cPeak p; size_t pos; - getline(stream,s); - - while (stream.good()) { - getline(stream,s); + while (stream.good() && !(strstr(s.c_str(),"M/Z"))) { // replaces commas with dots - pos = s.find(','); - while (pos != string::npos) { - s.replace(pos, 1, "."); - pos = s.find(','); - } + //pos = s.find(','); + //while (pos != string::npos) { + // s.replace(pos, 1, "."); + // pos = s.find(','); + //} - pos = s.find(':'); + pos = s.find(','); if (pos != string::npos) { s.replace(pos, 1, "\t"); } @@ -117,10 +121,48 @@ void cPeaksList::loadFromBAFStream(ifstream &stream) { if (!p.empty()) { peaks.push_back(p); } + + getline(stream,s); } } +void cPeaksList::loadFromIBDStream(cImzMLItem& imzmlitem, ifstream &ibdstream) { + unsigned long long start; + unsigned long long end; + double value; + + peaks.resize(imzmlitem.mzlength/8); + + start = imzmlitem.mzstart; + end = imzmlitem.mzstart + imzmlitem.mzlength; + ibdstream.seekg(start, ibdstream.beg); + for (int i = 0; i < (int)imzmlitem.mzlength/8; i++) { + if (ibdstream.good()) { + ibdstream.read((char*)&value, 8); + peaks[i].mzratio = value; + } + } + + if (imzmlitem.mzlength != imzmlitem.intensitylength) { + return; + } + + start = imzmlitem.intensitystart; + end = imzmlitem.intensitystart + imzmlitem.intensitylength; + ibdstream.seekg(start, ibdstream.beg); + for (int i = 0; i < (int)imzmlitem.intensitylength/8; i++) { + if (ibdstream.good()) { + ibdstream.read((char*)&value, 8); + peaks[i].intensity = value; + } + } + + x = imzmlitem.x; + y = imzmlitem.y; +} + + void cPeaksList::loadFromMGFStream(ifstream &stream) { string s; cPeak p; @@ -147,7 +189,7 @@ void cPeaksList::loadFromMGFStream(ifstream &stream) { } } - while ((stream.good()) && !(strstr(s.c_str(),"END IONS"))) { + while (stream.good() && !(strstr(s.c_str(),"END IONS"))) { // replaces commas with dots pos = s.find(','); @@ -189,10 +231,6 @@ string cPeaksList::print(bool htmlterminatelines) { s += to_string(peaks[i].intensity); } - if (peaks[i].matchdescription.compare("") != 0) { - s += peaks[i].matchdescription + " "; - } - if (htmlterminatelines) { s += "
"; } @@ -243,15 +281,15 @@ int cPeaksList::normalizeIntenzity() { void cPeaksList::cropIntenzity(double minimumrelativeintensitythreshold) { - int i = 0; - while (i < (int)peaks.size()) { - if (peaks[i].intensity < minimumrelativeintensitythreshold) { - peaks.erase(peaks.begin()+i); - } - else { - i++; - } + sortbyIntensityDesc(); + + int i = (int)peaks.size() - 1; + while ((i > 0) && (peaks[i].intensity < minimumrelativeintensitythreshold)) { + i--; } + peaks.resize(i + 1); + + sortbyMass(); } @@ -535,6 +573,22 @@ double cPeaksList::getMaximumIntensityFromMZInterval(double minmz, double maxmz) } +void cPeaksList::setCoordinates(int x, int y) { + this->x = x; + this->y = y; +} + + +int cPeaksList::getCoordinateX() { + return x; +} + + +int cPeaksList::getCoordinateY() { + return y; +} + + void cPeaksList::store(ofstream& os) { int size; @@ -544,6 +598,9 @@ void cPeaksList::store(ofstream& os) { for (int i = 0; i < (int)peaks.size(); i++) { peaks[i].store(os); } + + os.write((char *)&x, sizeof(int)); + os.write((char *)&y, sizeof(int)); } @@ -556,5 +613,8 @@ void cPeaksList::load(ifstream& is) { for (int i = 0; i < (int)peaks.size(); i++) { peaks[i].load(is); } + + is.read((char *)&x, sizeof(int)); + is.read((char *)&y, sizeof(int)); } diff --git a/CycloBranch/core/cPeaksList.h b/CycloBranch/core/cPeaksList.h index 2fbb41e..2c5d11a 100644 --- a/CycloBranch/core/cPeaksList.h +++ b/CycloBranch/core/cPeaksList.h @@ -15,6 +15,9 @@ #include "core/cPeak.h" +#include "core/cImzML.h" + + using namespace std; @@ -73,6 +76,7 @@ double ppmError(double experimentalmass, double theoreticalmass); class cPeaksList { vector peaks; + int x, y; public: @@ -131,6 +135,14 @@ class cPeaksList { void loadFromBAFStream(ifstream &stream); + /** + \brief Load the spectrum from .ibd file. + \param imzmlitem cImzML containing the offset in the ibd file + \param ibdstream ibd binary file stream + */ + void loadFromIBDStream(cImzMLItem& imzmlitem, ifstream &ibdstream); + + /** \brief Load the spectrum from a .mgf file. \param stream source stream @@ -297,6 +309,28 @@ class cPeaksList { double getMaximumIntensityFromMZInterval(double minmz, double maxmz); + /** + \brief Set the coordinates. + \param x X coordinate + \param y Y coordinate + */ + void setCoordinates(int x, int y); + + + /** + \brief Get the X coordinate. + \retval int X coordinate + */ + int getCoordinateX(); + + + /** + \brief Get the Y coordinate. + \retval int Y coordinate + */ + int getCoordinateY(); + + /** \brief Store the structure into an output stream. \param os an output stream diff --git a/CycloBranch/core/cSequence.cpp b/CycloBranch/core/cSequence.cpp index c777eea..9eb8ec6 100644 --- a/CycloBranch/core/cSequence.cpp +++ b/CycloBranch/core/cSequence.cpp @@ -19,7 +19,7 @@ void cSequence::clear() { void cSequence::store(ofstream& os) { - os.write((char *)&peptidetype, sizeof(peptideType)); + os.write((char *)&peptidetype, sizeof(ePeptideType)); storeString(sequence, os); storeString(nterminalmodification, os); storeString(cterminalmodification, os); @@ -31,7 +31,7 @@ void cSequence::store(ofstream& os) { void cSequence::load(ifstream& is) { - is.read((char *)&peptidetype, sizeof(peptideType)); + is.read((char *)&peptidetype, sizeof(ePeptideType)); loadString(sequence, is); loadString(nterminalmodification, is); loadString(cterminalmodification, is); @@ -42,7 +42,7 @@ void cSequence::load(ifstream& is) { } -peptideType cSequence::getPeptideType() { +ePeptideType cSequence::getPeptideType() { return peptidetype; } @@ -62,7 +62,7 @@ string& cSequence::getReference() { } -void cSequence::setPeptideType(peptideType peptidetype) { +void cSequence::setPeptideType(ePeptideType peptidetype) { this->peptidetype = peptidetype; } diff --git a/CycloBranch/core/cSequence.h b/CycloBranch/core/cSequence.h index c84c94d..c594c16 100644 --- a/CycloBranch/core/cSequence.h +++ b/CycloBranch/core/cSequence.h @@ -25,7 +25,7 @@ using namespace boost; */ class cSequence { - peptideType peptidetype; + ePeptideType peptidetype; string sequence; string nterminalmodification; string cterminalmodification; @@ -65,9 +65,9 @@ class cSequence { /** \brief Get peptide type. - \retval peptideType peptide type + \retval ePeptideType peptide type */ - peptideType getPeptideType(); + ePeptideType getPeptideType(); /** @@ -95,7 +95,7 @@ class cSequence { \brief Set peptide type. \param peptidetype peptide type */ - void setPeptideType(peptideType peptidetype); + void setPeptideType(ePeptideType peptidetype); /** diff --git a/CycloBranch/core/cSequenceDatabase.cpp b/CycloBranch/core/cSequenceDatabase.cpp index 91ad447..a547ade 100644 --- a/CycloBranch/core/cSequenceDatabase.cpp +++ b/CycloBranch/core/cSequenceDatabase.cpp @@ -29,24 +29,7 @@ void cSequenceDatabase::loadFromPlainTextStream(ifstream &stream) { pos = s.find('\t'); if (pos != string::npos) { type = s.substr(0, pos); - if (type.compare("linear") == 0) { - sequence.setPeptideType(linear); - } - if (type.compare("cyclic") == 0) { - sequence.setPeptideType(cyclic); - } - if (type.compare("branched") == 0) { - sequence.setPeptideType(branched); - } - if (type.compare("branch-cyclic") == 0) { - sequence.setPeptideType(lasso); - } - if (type.compare("linearpolysaccharide") == 0) { - sequence.setPeptideType(linearpolysaccharide); - } - if (type.compare("other") == 0) { - sequence.setPeptideType(other); - } + sequence.setPeptideType(getPeptideTypeFromString(type)); s = s.substr(pos + 1); } else { diff --git a/CycloBranch/core/cTheoreticalSpectrum.cpp b/CycloBranch/core/cTheoreticalSpectrum.cpp index d72487f..e06bd4c 100644 --- a/CycloBranch/core/cTheoreticalSpectrum.cpp +++ b/CycloBranch/core/cTheoreticalSpectrum.cpp @@ -30,7 +30,7 @@ void visualSeries::load(ifstream& is) { } -void cTheoreticalSpectrum::clearFalseHits(map >& series, vector& fragmentions) { +void cTheoreticalSpectrum::clearFalseHits(map >& series, vector& fragmentions) { for (int i = 0; i < (int)fragmentions.size(); i++) { for (int j = 0; j < (int)series[fragmentions[i]].size(); j++) { @@ -75,7 +75,7 @@ void cTheoreticalSpectrum::computeStatistics(bool writedescription) { experimentalpeaksmatched = 0; scrambledpeaksmatched = 0; if (writedescription) { - unmatchedpeaks = ""; + unmatchedpeaks.clear(); } intensityweightedscore = 0; for (int i = 0; i < (int)experimentalpeaks.size(); i++) { @@ -94,7 +94,7 @@ void cTheoreticalSpectrum::computeStatistics(bool writedescription) { } else { if (writedescription) { - unmatchedpeaks += to_string((long double)experimentalpeaks[i].mzratio) + " "; + unmatchedpeaks.add(experimentalpeaks[i]); } } } @@ -106,6 +106,10 @@ void cTheoreticalSpectrum::computeStatistics(bool writedescription) { void cTheoreticalSpectrum::generatePrecursorIon(vector& intcomposition, cBricksDatabase& bricksdatabasewithcombinations, int& theoreticalpeaksrealsize, bool writedescription) { cPeak peak; int starttype, endtype; + set usedmodifications; + usedmodifications.insert(0); + + peak.isotope = false; switch (parameters->peptidetype) { @@ -114,6 +118,8 @@ void cTheoreticalSpectrum::generatePrecursorIon(vector& intcomposition, cBr peak.seriesid = 0; starttype = (int)precursor_ion; endtype = (int)precursor_ion_co_loss_and_dehydrated_and_deamidated; + usedmodifications.insert(candidate.getStartModifID()); + usedmodifications.insert(candidate.getEndModifID()); break; case cyclic: peak.mzratio = 0; @@ -126,18 +132,62 @@ void cTheoreticalSpectrum::generatePrecursorIon(vector& intcomposition, cBr peak.seriesid = 0; starttype = (int)precursor_ion; endtype = (int)precursor_ion_co_loss_and_dehydrated_and_deamidated; + usedmodifications.insert(candidate.getStartModifID()); + usedmodifications.insert(candidate.getEndModifID()); + usedmodifications.insert(candidate.getMiddleModifID()); break; - case lasso: + case branchcyclic: peak.mzratio = parameters->searchedmodifications[candidate.getMiddleModifID()].massdifference; peak.seriesid = 0; starttype = (int)cyclic_precursor_ion; endtype = (int)cyclic_precursor_ion_co_loss_and_dehydrated_and_deamidated; + usedmodifications.insert(candidate.getMiddleModifID()); + break; +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + peak.mzratio = parameters->searchedmodifications[candidate.getStartModifID()].massdifference + parameters->searchedmodifications[candidate.getEndModifID()].massdifference; + peak.seriesid = 0; + + switch (candidate.getResidueLossType(bricksdatabasewithcombinations)) + { + case water: + starttype = (int)linear_polyketide_precursor_ion_h_oh; + endtype = (int)linear_polyketide_precursor_ion_h_oh_co_loss_dehydrated_and_deamidated; + break; + case h2: + starttype = (int)linear_polyketide_precursor_ion_h_h; + endtype = (int)linear_polyketide_precursor_ion_h_h_co_loss_dehydrated_and_deamidated; + break; + case h2o2: + starttype = (int)linear_polyketide_precursor_ion_oh_oh; + endtype = (int)linear_polyketide_precursor_ion_oh_oh_co_loss_dehydrated_and_deamidated; + break; + default: + break; + } + + if (candidate.hasFirstBrickArtificial(bricksdatabasewithcombinations) || candidate.hasLastBrickArtificial(bricksdatabasewithcombinations)) { + starttype = (int)linear_polyketide_precursor_ion_h_h; + endtype = (int)linear_polyketide_precursor_ion_oh_oh_co_loss_dehydrated_and_deamidated; + } + + usedmodifications.insert(candidate.getStartModifID()); + usedmodifications.insert(candidate.getEndModifID()); break; + case cyclicpolyketide: + peak.mzratio = 0; + peak.seriesid = (int)intcomposition.size() - 1; + starttype = (int)cyclic_polyketide_precursor_ion; + endtype = (int)cyclic_polyketide_precursor_ion_co_loss_dehydrated_and_deamidated; + break; +#endif case linearpolysaccharide: peak.mzratio = parameters->searchedmodifications[candidate.getStartModifID()].massdifference + parameters->searchedmodifications[candidate.getEndModifID()].massdifference; peak.seriesid = 0; starttype = (int)precursor_ion; endtype = (int)precursor_ion_co_loss_and_dehydrated_and_deamidated; + usedmodifications.insert(candidate.getStartModifID()); + usedmodifications.insert(candidate.getEndModifID()); break; case other: break; @@ -151,86 +201,86 @@ void cTheoreticalSpectrum::generatePrecursorIon(vector& intcomposition, cBr double tempratio = peak.mzratio; for (int i = starttype; i <= endtype; i++) { - for (int j = 1; j <= abs(parameters->precursorcharge); j++) { - peak.mzratio = tempratio + parameters->fragmentdefinitions[(fragmentIonType)i].massdifference; - peak.iontype = (fragmentIonType)i; - - if (writedescription) { - string str; + for(set::iterator j = usedmodifications.begin(); j != usedmodifications.end(); ++j) { + for (int k = 1; k <= abs(parameters->precursorcharge); k++) { + peak.mzratio = tempratio + parameters->fragmentdefinitions[(eFragmentIonType)i].massdifference - parameters->searchedmodifications[*j].massdifference; + peak.iontype = (eFragmentIonType)i; - if (parameters->precursorcharge > 0) { - peak.description = parameters->fragmentdefinitions[(fragmentIonType)i].name + ":"; + if (writedescription) { + string str; - if (!parameters->precursoradduct.empty()) { - str = "+" + parameters->precursoradduct; - if (j > 1) { - str += "+"; - if (j > 2) { - str += to_string(j - 1); + if (parameters->precursorcharge > 0) { + peak.description = parameters->fragmentdefinitions[(eFragmentIonType)i].name; + if (*j > 0) { + peak.description += " (" + parameters->searchedmodifications[*j].name + " loss)"; + } + peak.description += ":"; + + if (!parameters->precursoradduct.empty()) { + str = "+" + parameters->precursoradduct; + if (k > 1) { + str += "+"; + if (k > 2) { + str += to_string(k - 1); + } + str += "H"; + } + peak.description.replace(peak.description.find("+zH"), 3, str); + } + else { + str = "+"; + if (k > 1) { + str += to_string(k); } str += "H"; + peak.description.replace(peak.description.find("+zH"), 3, str); } - peak.description.replace(peak.description.find("+zH"), 3, str); + + str = "]"; + if (k > 1) { + str += to_string(k); + } + str += "+"; + peak.description.replace(peak.description.find("]+"), 2, str); } else { - str = "+"; - if (j > 1) { - str += to_string(j); + peak.description = parameters->fragmentdefinitions[(eFragmentIonType)i].name + ":"; + + if (!parameters->precursoradduct.empty()) { + str = "-" + parameters->precursoradduct; + if (k > 1) { + str += "-"; + if (k > 2) { + str += to_string(k - 1); + } + str += "H"; + } + peak.description.replace(peak.description.find("+zH"), 3, str); } - str += "H"; - peak.description.replace(peak.description.find("+zH"), 3, str); - } - - str = "]"; - if (j > 1) { - str += to_string(j); - } - str += "+"; - peak.description.replace(peak.description.find("]+"), 2, str); - } - else { - peak.description = parameters->fragmentdefinitions[(fragmentIonType)i].name + ":"; - - if (!parameters->precursoradduct.empty()) { - str = "-" + parameters->precursoradduct; - if (j > 1) { - str += "-"; - if (j > 2) { - str += to_string(j - 1); + else { + str = "-"; + if (k > 1) { + str += to_string(k); } str += "H"; + peak.description.replace(peak.description.find("+zH"), 3, str); } - peak.description.replace(peak.description.find("+zH"), 3, str); - } - else { - str = "-"; - if (j > 1) { - str += to_string(j); - } - str += "H"; - peak.description.replace(peak.description.find("+zH"), 3, str); - } - str = "]"; - if (j > 1) { - str += to_string(j); + str = "]"; + if (k > 1) { + str += to_string(k); + } + str += "-"; + peak.description.replace(peak.description.find("]+"), 2, str); } - str += "-"; - peak.description.replace(peak.description.find("]+"), 2, str); } - } - - peak.mzratio = charge(uncharge(peak.mzratio, 1), (parameters->precursorcharge > 0)?j:-j); - peak.charge = (parameters->precursorcharge > 0)?j:-j; - if (theoreticalpeaks.size() > theoreticalpeaksrealsize) { - theoreticalpeaks[theoreticalpeaksrealsize] = peak; - } - else { - theoreticalpeaks.add(peak); + peak.mzratio = charge(uncharge(peak.mzratio, 1), (parameters->precursorcharge > 0)?k:-k); + peak.charge = (parameters->precursorcharge > 0)?k:-k; + + addPeakToList(peak, theoreticalpeaksrealsize); + addMetalPeaks(peak, theoreticalpeaksrealsize, k, writedescription); } - theoreticalpeaksrealsize++; - } } } @@ -287,7 +337,7 @@ void cTheoreticalSpectrum::generateScrambledIons(cBricksDatabase& bricksdatabase intcomposition.clear(); b.explodeToIntComposition(intcomposition); cPeak peak; - peak.iontype = (fragmentIonType)parameters->fragmentionsfortheoreticalspectra[i]; + peak.iontype = (eFragmentIonType)parameters->fragmentionsfortheoreticalspectra[i]; peak.mzratio = parameters->fragmentdefinitions[peak.iontype].massdifference; peak.removeme = false; peak.scrambled = true; @@ -342,13 +392,7 @@ void cTheoreticalSpectrum::generateScrambledIons(cBricksDatabase& bricksdatabase // attach scrambled peaks to common peaks for (int i = 0; i < (int)scrambledpeaks.size(); i++) { - if (theoreticalpeaks.size() > theoreticalpeaksrealsize) { - theoreticalpeaks[theoreticalpeaksrealsize] = scrambledpeaks[i]; - } - else { - theoreticalpeaks.add(scrambledpeaks[i]); - } - theoreticalpeaksrealsize++; + addPeakToList(scrambledpeaks[i], theoreticalpeaksrealsize); } } @@ -374,7 +418,7 @@ void cTheoreticalSpectrum::selectAndNormalizeScrambledSequences(unordered_set theoreticalpeaksrealsize) { + theoreticalpeaks[theoreticalpeaksrealsize] = peak; + } + else { + theoreticalpeaks.add(peak); + } + theoreticalpeaksrealsize++; +} + + +void cTheoreticalSpectrum::addAdductToDescription(string& description) { + if (parameters->precursorAdductHasLi) { + description += "_Li"; + } + + if (parameters->precursorAdductHasNa) { + description += "_Na"; + } + + if (parameters->precursorAdductHasMg) { + description += "_Mg"; + } + + if (parameters->precursorAdductHasAl) { + description += "_Al"; + } + + if (parameters->precursorAdductHasK) { + description += "_K"; + } + + if (parameters->precursorAdductHasCa) { + description += "_Ca"; + } + + if (parameters->precursorAdductHasMn) { + description += "_Mn"; + } + + if (parameters->precursorAdductHasFe) { + description += "_Fe"; + } + + if (parameters->precursorAdductHasCo) { + description += "_Co"; + } + + if (parameters->precursorAdductHasNi) { + description += "_Ni"; + } + + if (parameters->precursorAdductHasCu) { + description += "_Cu"; + } + + if (parameters->precursorAdductHasZn) { + description += "_Zn"; + } + + if (parameters->precursorAdductHasGa) { + description += "_Ga"; + } +} + + +int cTheoreticalSpectrum::countIsotopesOfMetals(cParameters* parameters) { + int count = 0; + for (int i = 0; i < (int)parameters->fragmentionsfortheoreticalspectra.size(); i++) { + count += parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].numberofisotopes; + } + return count; +} + + +void cTheoreticalSpectrum::addMetalPeaks(cPeak& peak, int& peaklistrealsize, int charge, bool writedescription) { + if (parameters->precursorAdductHasLi) { + cPeak tmppeak; + + tmppeak = peak; + tmppeak.isotope = true; + tmppeak.mzratio -= (Li - Li6)/(double)charge; + if (writedescription) { + tmppeak.description.replace(tmppeak.description.find("Li"), 2, "6Li"); + } + addPeakToList(tmppeak, peaklistrealsize); + } + + if (parameters->precursorAdductHasMg) { + cPeak tmppeak; + + tmppeak = peak; + tmppeak.isotope = true; + tmppeak.mzratio += (Mg25 - Mg)/(double)charge; + if (writedescription) { + tmppeak.description.replace(tmppeak.description.find("Mg"), 2, "25Mg"); + } + addPeakToList(tmppeak, peaklistrealsize); + + tmppeak = peak; + tmppeak.isotope = true; + tmppeak.mzratio += (Mg26 - Mg)/(double)charge; + if (writedescription) { + tmppeak.description.replace(tmppeak.description.find("Mg"), 2, "26Mg"); + } + addPeakToList(tmppeak, peaklistrealsize); + } + + if (parameters->precursorAdductHasK) { + cPeak tmppeak; + + tmppeak = peak; + tmppeak.isotope = true; + tmppeak.mzratio += (K41 - K)/(double)charge; + if (writedescription) { + tmppeak.description.replace(tmppeak.description.find("K"), 1, "41K"); + } + addPeakToList(tmppeak, peaklistrealsize); + } + + if (parameters->precursorAdductHasCa) { + cPeak tmppeak; + + tmppeak = peak; + tmppeak.isotope = true; + tmppeak.mzratio += (Ca44 - Ca)/(double)charge; + if (writedescription) { + tmppeak.description.replace(tmppeak.description.find("Ca"), 2, "44Ca"); + } + addPeakToList(tmppeak, peaklistrealsize); + } + + if (parameters->precursorAdductHasFe) { + cPeak tmppeak; + + tmppeak = peak; + tmppeak.isotope = true; + tmppeak.mzratio -= (Fe - Fe54)/(double)charge; + if (writedescription) { + tmppeak.description.replace(tmppeak.description.find("Fe"), 2, "54Fe"); + } + addPeakToList(tmppeak, peaklistrealsize); + + /*tmppeak = peak; + tmppeak.isotope = true; + tmppeak.mzratio += (Fe57 - Fe)/(double)j; + if (writedescription) { + tmppeak.description.replace(tmppeak.description.find(':') - 2, 2, "57Fe"); + } + addPeakToList(tmppeak, peaklistrealsize);*/ + } + + if (parameters->precursorAdductHasNi) { + cPeak tmppeak; + + tmppeak = peak; + tmppeak.isotope = true; + tmppeak.mzratio += (Ni60 - Ni)/(double)charge; + if (writedescription) { + tmppeak.description.replace(tmppeak.description.find("Ni"), 2, "60Ni"); + } + addPeakToList(tmppeak, peaklistrealsize); + + tmppeak = peak; + tmppeak.isotope = true; + tmppeak.mzratio += (Ni61 - Ni)/(double)charge; + if (writedescription) { + tmppeak.description.replace(tmppeak.description.find("Ni"), 2, "61Ni"); + } + addPeakToList(tmppeak, peaklistrealsize); + + tmppeak = peak; + tmppeak.isotope = true; + tmppeak.mzratio += (Ni62 - Ni)/(double)charge; + if (writedescription) { + tmppeak.description.replace(tmppeak.description.find("Ni"), 2, "62Ni"); + } + addPeakToList(tmppeak, peaklistrealsize); + } + + if (parameters->precursorAdductHasCu) { + cPeak tmppeak; + + tmppeak = peak; + tmppeak.isotope = true; + tmppeak.mzratio += (Cu65 - Cu)/(double)charge; + if (writedescription) { + tmppeak.description.replace(tmppeak.description.find("Cu"), 2, "65Cu"); + } + addPeakToList(tmppeak, peaklistrealsize); + } + + if (parameters->precursorAdductHasZn) { + cPeak tmppeak; + + tmppeak = peak; + tmppeak.isotope = true; + tmppeak.mzratio += (Zn66 - Zn)/(double)charge; + if (writedescription) { + tmppeak.description.replace(tmppeak.description.find("Zn"), 2, "66Zn"); + } + addPeakToList(tmppeak, peaklistrealsize); + + tmppeak = peak; + tmppeak.isotope = true; + tmppeak.mzratio += (Zn67 - Zn)/(double)charge; + if (writedescription) { + tmppeak.description.replace(tmppeak.description.find("Zn"), 2, "67Zn"); + } + addPeakToList(tmppeak, peaklistrealsize); + + tmppeak = peak; + tmppeak.isotope = true; + tmppeak.mzratio += (Zn68 - Zn)/(double)charge; + if (writedescription) { + tmppeak.description.replace(tmppeak.description.find("Zn"), 2, "68Zn"); + } + addPeakToList(tmppeak, peaklistrealsize); + } + + if (parameters->precursorAdductHasGa) { + cPeak tmppeak; + + tmppeak = peak; + tmppeak.isotope = true; + tmppeak.mzratio += (Ga71 - Ga)/(double)charge; + if (writedescription) { + tmppeak.description.replace(tmppeak.description.find("Ga"), 2, "71Ga"); + } + addPeakToList(tmppeak, peaklistrealsize); + } +} + + +void cTheoreticalSpectrum::removeUnmatchedMetalIsotopes(cPeaksList& theoreticalpeaks, int theoreticalpeaksrealsize, cPeaksList& experimentalpeaks, vector >& experimentalpeakmatches) { + int lastparent = 0; + for (int i = 0; i < theoreticalpeaksrealsize; i++) { + if (!theoreticalpeaks[i].isotope) { + lastparent = i; + continue; + } + + if ((theoreticalpeaks[lastparent].matched == 0) && (theoreticalpeaks[i].matched > 0)) { + experimentalpeakmatches[theoreticalpeaks[i].matchedid].erase(experimentalpeakmatches[theoreticalpeaks[i].matchedid].find(i)); + experimentalpeaks[theoreticalpeaks[i].matchedid].matched--; + + theoreticalpeaks[i].matched--; + theoreticalpeaks[i].matchedid = -1; + } + } +} + + cTheoreticalSpectrum::cTheoreticalSpectrum() { clear(); } @@ -505,7 +802,7 @@ void cTheoreticalSpectrum::clear(bool clearpeaklist) { experimentalpeaks.clear(); matchedions.clear(); for (int i = 0; i < fragmentIonTypeEnd; i++) { - matchedions[(fragmentIonType)i] = 0; + matchedions[(eFragmentIonType)i] = 0; } candidate.clear(); @@ -513,7 +810,7 @@ void cTheoreticalSpectrum::clear(bool clearpeaklist) { scrambledpeaksmatched = 0; peakstested = 0; experimentalpeaksmatchedratio = 0; - unmatchedpeaks = ""; + unmatchedpeaks.clear(); coveragebyseries = ""; valid = false; maskscore = 0; @@ -585,10 +882,10 @@ int cTheoreticalSpectrum::compareBranched(cPeaksList& sortedpeaklist, cBricksDat // j == 2, 4, 5 are invalid if ((j == 0) || ((j == 1) && (parameters->searchedmodifications[trotations[j].startmodifID].nterminal)) || ((j == 3) && (parameters->searchedmodifications[trotations[j].endmodifID].cterminal))) { if (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].nterminal) { - generateNTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, trotations[j].bricks, parameters->fragmentionsfortheoreticalspectra[i], bricksdatabasewithcombinations, writedescription, 0, splittingsites, parameters->searchedmodifications, branched, &trotations[j]); + generateNTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, trotations[j].bricks, parameters->fragmentionsfortheoreticalspectra[i], bricksdatabasewithcombinations, writedescription, 0, splittingsites, parameters->searchedmodifications, parameters->peptidetype, &trotations[j]); } if (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].cterminal) { - generateCTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, trotations[j].bricks, parameters->fragmentionsfortheoreticalspectra[i], bricksdatabasewithcombinations, writedescription, 0, splittingsites, parameters->searchedmodifications, branched, &trotations[j]); + generateCTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, trotations[j].bricks, parameters->fragmentionsfortheoreticalspectra[i], bricksdatabasewithcombinations, writedescription, 0, splittingsites, parameters->searchedmodifications, parameters->peptidetype, &trotations[j]); } } @@ -606,7 +903,7 @@ int cTheoreticalSpectrum::compareBranched(cPeaksList& sortedpeaklist, cBricksDat // coverage of series - vector > > series; + vector > > series; series.resize(trotations.size()); for (int i = 0; i < (int)series.size(); i++) { for (int j = 0; j < (int)parameters->fragmentionsfortheoreticalspectra.size(); j++) { @@ -615,7 +912,7 @@ int cTheoreticalSpectrum::compareBranched(cPeaksList& sortedpeaklist, cBricksDat } for (int i = 0; i < theoreticalpeaksrealsize; i++) { - if ((theoreticalpeaks[i].matched > 0) && (theoreticalpeaks[i].rotationid >= 0) && (series[theoreticalpeaks[i].rotationid].count(theoreticalpeaks[i].iontype) == 1)) { + if (!theoreticalpeaks[i].isotope && (theoreticalpeaks[i].matched > 0) && (theoreticalpeaks[i].rotationid >= 0) && (series[theoreticalpeaks[i].rotationid].count(theoreticalpeaks[i].iontype) == 1)) { series[theoreticalpeaks[i].rotationid][theoreticalpeaks[i].iontype][theoreticalpeaks[i].seriesid]++; } } @@ -628,7 +925,7 @@ int cTheoreticalSpectrum::compareBranched(cPeaksList& sortedpeaklist, cBricksDat } for (int i = 0; i < theoreticalpeaksrealsize; i++) { - if ((theoreticalpeaks[i].matched > 0) && (theoreticalpeaks[i].rotationid >= 0) && ((series[theoreticalpeaks[i].rotationid].count(parameters->fragmentdefinitions[theoreticalpeaks[i].iontype].parent) == 0) || (series[theoreticalpeaks[i].rotationid][parameters->fragmentdefinitions[theoreticalpeaks[i].iontype].parent][theoreticalpeaks[i].seriesid] == 0))) { + if (!theoreticalpeaks[i].isotope && (theoreticalpeaks[i].matched > 0) && (theoreticalpeaks[i].rotationid >= 0) && ((series[theoreticalpeaks[i].rotationid].count(parameters->fragmentdefinitions[theoreticalpeaks[i].iontype].parent) == 0) || (series[theoreticalpeaks[i].rotationid][parameters->fragmentdefinitions[theoreticalpeaks[i].iontype].parent][theoreticalpeaks[i].seriesid] == 0))) { experimentalpeakmatches[theoreticalpeaks[i].matchedid].erase(experimentalpeakmatches[theoreticalpeaks[i].matchedid].find(i)); experimentalpeaks[theoreticalpeaks[i].matchedid].matched--; @@ -639,6 +936,9 @@ int cTheoreticalSpectrum::compareBranched(cPeaksList& sortedpeaklist, cBricksDat } + removeUnmatchedMetalIsotopes(theoreticalpeaks, theoreticalpeaksrealsize, experimentalpeaks, experimentalpeakmatches); + + // fill annotations of peaks for (int i = 0; i < (int)experimentalpeaks.size(); i++) { for (set::iterator it = experimentalpeakmatches[i].begin(); it != experimentalpeakmatches[i].end(); ++it) { @@ -647,7 +947,9 @@ int cTheoreticalSpectrum::compareBranched(cPeaksList& sortedpeaklist, cBricksDat experimentalpeaks[i].description += ","; } experimentalpeaks[i].description += theoreticalpeaks[*it].description.substr(0,theoreticalpeaks[*it].description.find(':')); - theoreticalpeaks[*it].matchdescription += ", measured mass " + to_string((long double)experimentalpeaks[i].mzratio) + " matched with " + to_string((long double)ppmError(experimentalpeaks[i].mzratio, theoreticalpeaks[*it].mzratio)) + " ppm error"; + theoreticalpeaks[*it].matchedmz = experimentalpeaks[i].mzratio; + theoreticalpeaks[*it].matchedintensity = experimentalpeaks[i].intensity; + theoreticalpeaks[*it].matchedppm = ppmError(experimentalpeaks[i].mzratio, theoreticalpeaks[*it].mzratio); } experimentalpeaks[i].iontype = selectHigherPriorityIonTypeCID(experimentalpeaks[i].iontype,theoreticalpeaks[*it].iontype); @@ -723,10 +1025,10 @@ int cTheoreticalSpectrum::compareLinear(cPeaksList& sortedpeaklist, cBricksDatab for (int i = 0; i < (int)parameters->fragmentionsfortheoreticalspectra.size(); i++) { if (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].nterminal) { - generateNTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, intcomposition, parameters->fragmentionsfortheoreticalspectra[i], bricksdatabasewithcombinations, writedescription, 0, splittingsites, parameters->searchedmodifications, linear); + generateNTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, intcomposition, parameters->fragmentionsfortheoreticalspectra[i], bricksdatabasewithcombinations, writedescription, 0, splittingsites, parameters->searchedmodifications, parameters->peptidetype); } if (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].cterminal) { - generateCTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, intcomposition, parameters->fragmentionsfortheoreticalspectra[i], bricksdatabasewithcombinations, writedescription, 0, splittingsites, parameters->searchedmodifications, linear); + generateCTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, intcomposition, parameters->fragmentionsfortheoreticalspectra[i], bricksdatabasewithcombinations, writedescription, 0, splittingsites, parameters->searchedmodifications, parameters->peptidetype); } } @@ -740,13 +1042,13 @@ int cTheoreticalSpectrum::compareLinear(cPeaksList& sortedpeaklist, cBricksDatab // coverage of series - map > series; + map > series; for (int i = 0; i < (int)parameters->fragmentionsfortheoreticalspectra.size(); i++) { series[parameters->fragmentionsfortheoreticalspectra[i]].resize(intcomposition.size()); } for (int i = 0; i < theoreticalpeaksrealsize; i++) { - if ((theoreticalpeaks[i].matched > 0) && (series.count(theoreticalpeaks[i].iontype) == 1)) { + if (!theoreticalpeaks[i].isotope && (theoreticalpeaks[i].matched > 0) && (series.count(theoreticalpeaks[i].iontype) == 1)) { series[theoreticalpeaks[i].iontype][theoreticalpeaks[i].seriesid]++; } } @@ -757,7 +1059,7 @@ int cTheoreticalSpectrum::compareLinear(cPeaksList& sortedpeaklist, cBricksDatab clearFalseHits(series, parameters->fragmentionsfortheoreticalspectra); for (int i = 0; i < theoreticalpeaksrealsize; i++) { - if ((theoreticalpeaks[i].matched > 0) && (theoreticalpeaks[i].rotationid != -1) && ((series.count(parameters->fragmentdefinitions[theoreticalpeaks[i].iontype].parent) == 0) || (series[parameters->fragmentdefinitions[theoreticalpeaks[i].iontype].parent][theoreticalpeaks[i].seriesid] == 0))) { + if (!theoreticalpeaks[i].isotope && (theoreticalpeaks[i].matched > 0) && (theoreticalpeaks[i].rotationid != -1) && ((series.count(parameters->fragmentdefinitions[theoreticalpeaks[i].iontype].parent) == 0) || (series[parameters->fragmentdefinitions[theoreticalpeaks[i].iontype].parent][theoreticalpeaks[i].seriesid] == 0))) { experimentalpeakmatches[theoreticalpeaks[i].matchedid].erase(experimentalpeakmatches[theoreticalpeaks[i].matchedid].find(i)); experimentalpeaks[theoreticalpeaks[i].matchedid].matched--; @@ -768,6 +1070,9 @@ int cTheoreticalSpectrum::compareLinear(cPeaksList& sortedpeaklist, cBricksDatab } + removeUnmatchedMetalIsotopes(theoreticalpeaks, theoreticalpeaksrealsize, experimentalpeaks, experimentalpeakmatches); + + // fill annotations of peaks for (int i = 0; i < (int)experimentalpeaks.size(); i++) { for (set::iterator it = experimentalpeakmatches[i].begin(); it != experimentalpeakmatches[i].end(); ++it) { @@ -776,7 +1081,9 @@ int cTheoreticalSpectrum::compareLinear(cPeaksList& sortedpeaklist, cBricksDatab experimentalpeaks[i].description += ","; } experimentalpeaks[i].description += theoreticalpeaks[*it].description.substr(0,theoreticalpeaks[*it].description.find(':')); - theoreticalpeaks[*it].matchdescription += ", measured mass " + to_string((long double)experimentalpeaks[i].mzratio) + " matched with " + to_string((long double)ppmError(experimentalpeaks[i].mzratio, theoreticalpeaks[*it].mzratio)) + " ppm error"; + theoreticalpeaks[*it].matchedmz = experimentalpeaks[i].mzratio; + theoreticalpeaks[*it].matchedintensity = experimentalpeaks[i].intensity; + theoreticalpeaks[*it].matchedppm = ppmError(experimentalpeaks[i].mzratio, theoreticalpeaks[*it].mzratio); } experimentalpeaks[i].iontype = selectHigherPriorityIonTypeCID(experimentalpeaks[i].iontype,theoreticalpeaks[*it].iontype); @@ -907,6 +1214,8 @@ int cTheoreticalSpectrum::compareCyclic(cPeaksList& sortedpeaklist, cBricksDatab return -2; } + eResidueLossType leftresiduelosstype = water; + bool hasfirstblockartificial = false; for (int i = 0; i < 2*r; i++) { @@ -915,8 +1224,13 @@ int cTheoreticalSpectrum::compareCyclic(cPeaksList& sortedpeaklist, cBricksDatab brick.setComposition(rotations[i], false); brick.explodeToIntComposition(intcomposition); +#if POLYKETIDE_SIDEROPHORES == 1 + eResidueLossType leftresiduelosstype = bricksdatabasewithcombinations[intcomposition[0] - 1].getResidueLossType(); + bool hasfirstblockartificial = bricksdatabasewithcombinations[intcomposition[0] - 1].isArtificial(); +#endif + for (int j = 0; j < (int)parameters->fragmentionsfortheoreticalspectra.size(); j++) { - generateNTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, intcomposition, parameters->fragmentionsfortheoreticalspectra[j], bricksdatabasewithcombinations, writedescription, i, splittingsites, parameters->searchedmodifications, cyclic); + generateNTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, intcomposition, parameters->fragmentionsfortheoreticalspectra[j], bricksdatabasewithcombinations, writedescription, i, splittingsites, parameters->searchedmodifications, parameters->peptidetype, 0, leftresiduelosstype, hasfirstblockartificial); } } @@ -935,7 +1249,7 @@ int cTheoreticalSpectrum::compareCyclic(cPeaksList& sortedpeaklist, cBricksDatab // coverage of series - vector > > series; + vector > > series; series.resize(rotations.size()); for (int i = 0; i < (int)series.size(); i++) { for (int j = 0; j < (int)parameters->fragmentionsfortheoreticalspectra.size(); j++) { @@ -944,7 +1258,7 @@ int cTheoreticalSpectrum::compareCyclic(cPeaksList& sortedpeaklist, cBricksDatab } for (int i = 0; i < theoreticalpeaksrealsize; i++) { - if ((theoreticalpeaks[i].matched > 0) && (theoreticalpeaks[i].rotationid >= 0) && (series[theoreticalpeaks[i].rotationid].count(theoreticalpeaks[i].iontype) == 1)) { + if (!theoreticalpeaks[i].isotope && (theoreticalpeaks[i].matched > 0) && (theoreticalpeaks[i].rotationid >= 0) && (series[theoreticalpeaks[i].rotationid].count(theoreticalpeaks[i].iontype) == 1)) { series[theoreticalpeaks[i].rotationid][theoreticalpeaks[i].iontype][theoreticalpeaks[i].seriesid]++; } } @@ -957,7 +1271,7 @@ int cTheoreticalSpectrum::compareCyclic(cPeaksList& sortedpeaklist, cBricksDatab } for (int i = 0; i < theoreticalpeaksrealsize; i++) { - if ((theoreticalpeaks[i].matched > 0) && (theoreticalpeaks[i].rotationid >= 0) && ((series[theoreticalpeaks[i].rotationid].count(parameters->fragmentdefinitions[theoreticalpeaks[i].iontype].parent) == 0) || (series[theoreticalpeaks[i].rotationid][parameters->fragmentdefinitions[theoreticalpeaks[i].iontype].parent][theoreticalpeaks[i].seriesid] == 0))) { + if (!theoreticalpeaks[i].isotope && (theoreticalpeaks[i].matched > 0) && (theoreticalpeaks[i].rotationid >= 0) && ((series[theoreticalpeaks[i].rotationid].count(parameters->fragmentdefinitions[theoreticalpeaks[i].iontype].parent) == 0) || (series[theoreticalpeaks[i].rotationid][parameters->fragmentdefinitions[theoreticalpeaks[i].iontype].parent][theoreticalpeaks[i].seriesid] == 0))) { experimentalpeakmatches[theoreticalpeaks[i].matchedid].erase(experimentalpeakmatches[theoreticalpeaks[i].matchedid].find(i)); experimentalpeaks[theoreticalpeaks[i].matchedid].matched--; @@ -1022,6 +1336,9 @@ int cTheoreticalSpectrum::compareCyclic(cPeaksList& sortedpeaklist, cBricksDatab } + removeUnmatchedMetalIsotopes(theoreticalpeaks, theoreticalpeaksrealsize, experimentalpeaks, experimentalpeakmatches); + + // fill annotations of peaks for (int i = 0; i < (int)experimentalpeakmatches.size(); i++) { for (set::iterator it = experimentalpeakmatches[i].begin(); it != experimentalpeakmatches[i].end(); ++it) { @@ -1030,7 +1347,9 @@ int cTheoreticalSpectrum::compareCyclic(cPeaksList& sortedpeaklist, cBricksDatab experimentalpeaks[i].description += ","; } experimentalpeaks[i].description += theoreticalpeaks[*it].description.substr(0,theoreticalpeaks[*it].description.find(':')); - theoreticalpeaks[*it].matchdescription += ", measured mass " + to_string((long double)experimentalpeaks[i].mzratio) + " matched with " + to_string((long double)ppmError(experimentalpeaks[i].mzratio, theoreticalpeaks[*it].mzratio)) + " ppm error"; + theoreticalpeaks[*it].matchedmz = experimentalpeaks[i].mzratio; + theoreticalpeaks[*it].matchedintensity = experimentalpeaks[i].intensity; + theoreticalpeaks[*it].matchedppm = ppmError(experimentalpeaks[i].mzratio, theoreticalpeaks[*it].mzratio); } if (experimentalpeaks[i].iontype != fragmentIonTypeEnd) { @@ -1149,47 +1468,47 @@ int cTheoreticalSpectrum::compareCyclic(cPeaksList& sortedpeaklist, cBricksDatab } -int cTheoreticalSpectrum::compareLasso(cPeaksList& sortedpeaklist, cBricksDatabase& bricksdatabasewithcombinations, bool writedescription, regex& sequencetag, regex& searchedsequence) { +int cTheoreticalSpectrum::compareBranchCyclic(cPeaksList& sortedpeaklist, cBricksDatabase& bricksdatabasewithcombinations, bool writedescription, regex& sequencetag, regex& searchedsequence) { vector splittingsites; int theoreticalpeaksrealsize = 0; - vector lassorotations; + vector branchcyclicrotations; // normalize the candidate - candidate.getLassoRotations(lassorotations, false); + candidate.getBranchCyclicRotations(branchcyclicrotations, false); int numberofbricks = getNumberOfBricks(candidate.getComposition()); - for (int i = 0; i < (int)lassorotations.size(); i++) { - if (lassorotations[i].getBranchEnd() == numberofbricks - 1) { + for (int i = 0; i < (int)branchcyclicrotations.size(); i++) { + if (branchcyclicrotations[i].getBranchEnd() == numberofbricks - 1) { vector v; - v.push_back(lassorotations[i].getComposition()); + v.push_back(branchcyclicrotations[i].getComposition()); string name = candidate.getName(); vector cpath = candidate.getPath(); - candidate.setCandidate(v, cpath, candidate.getStartModifID(), candidate.getEndModifID(), candidate.getMiddleModifID(), lassorotations[i].getBranchStart(), lassorotations[i].getBranchEnd()); + candidate.setCandidate(v, cpath, candidate.getStartModifID(), candidate.getEndModifID(), candidate.getMiddleModifID(), branchcyclicrotations[i].getBranchStart(), branchcyclicrotations[i].getBranchEnd()); candidate.setName(name); break; } } // get branch-cyclic rotations - candidate.getLassoRotations(lassorotations, true); + candidate.getBranchCyclicRotations(branchcyclicrotations, true); // get T-permutations of branch-cyclic rotations - vector > trotationsoflassorotations; - trotationsoflassorotations.resize(lassorotations.size()); - for (int i = 0; i < (int)lassorotations.size(); i++) { - lassorotations[i].getPermutationsOfBranches(trotationsoflassorotations[i]); + vector > trotationsofbranchcyclicrotations; + trotationsofbranchcyclicrotations.resize(branchcyclicrotations.size()); + for (int i = 0; i < (int)branchcyclicrotations.size(); i++) { + branchcyclicrotations[i].getPermutationsOfBranches(trotationsofbranchcyclicrotations[i]); } - int r = (int)lassorotations.size() / 2; + int r = (int)branchcyclicrotations.size() / 2; validposition = -1; reversevalidposition = -1; try { bool stop = true; - for (int i = 0; i < (int)lassorotations.size(); i++) { - for (int j = 0; j < (int)trotationsoflassorotations[i].size(); j++) { - if (regex_search(trotationsoflassorotations[i][j].tcomposition, sequencetag)) { + for (int i = 0; i < (int)branchcyclicrotations.size(); i++) { + for (int j = 0; j < (int)trotationsofbranchcyclicrotations[i].size(); j++) { + if (regex_search(trotationsofbranchcyclicrotations[i][j].tcomposition, sequencetag)) { stop = false; break; } @@ -1202,8 +1521,8 @@ int cTheoreticalSpectrum::compareLasso(cPeaksList& sortedpeaklist, cBricksDataba if (writedescription) { valid = false; if (parameters->searchedsequence.size() > 0) { - for (int i = 0; i < (int)lassorotations.size(); i++) { - if (regex_search(lassorotations[i].getComposition(), searchedsequence)) { + for (int i = 0; i < (int)branchcyclicrotations.size(); i++) { + if (regex_search(branchcyclicrotations[i].getComposition(), searchedsequence)) { validposition = i; if (validposition == 0) { @@ -1217,9 +1536,9 @@ int cTheoreticalSpectrum::compareLasso(cPeaksList& sortedpeaklist, cBricksDataba } } - for (int j = 0; j < (int)trotationsoflassorotations[i].size(); j++) { - if (regex_search(trotationsoflassorotations[i][j].tcomposition, searchedsequence) && - (parameters->searchedmodifications[trotationsoflassorotations[i][j].middlemodifID].name.compare(parameters->searchedsequenceTmodif) == 0)) { + for (int j = 0; j < (int)trotationsofbranchcyclicrotations[i].size(); j++) { + if (regex_search(trotationsofbranchcyclicrotations[i][j].tcomposition, searchedsequence) && + (parameters->searchedmodifications[trotationsofbranchcyclicrotations[i][j].middlemodifID].name.compare(parameters->searchedsequenceTmodif) == 0)) { valid = true; break; } @@ -1235,7 +1554,7 @@ int cTheoreticalSpectrum::compareLasso(cPeaksList& sortedpeaklist, cBricksDataba if (validposition != -1) { - splittingsites.resize(lassorotations.size()); + splittingsites.resize(branchcyclicrotations.size()); splittingsites[validposition].first = 0; splittingsites[validposition].second = r - 1; splittingsites[reversevalidposition].first = r - 1; @@ -1269,25 +1588,25 @@ int cTheoreticalSpectrum::compareLasso(cPeaksList& sortedpeaklist, cBricksDataba for (int i = 0; i < (int)parameters->fragmentionsfortheoreticalspectra.size(); i++) { - for (int j = 0; j < (int)lassorotations.size(); j++) { + for (int j = 0; j < (int)branchcyclicrotations.size(); j++) { - for (int k = 0; k < (int)trotationsoflassorotations[j].size(); k++) { + for (int k = 0; k < (int)trotationsofbranchcyclicrotations[j].size(); k++) { // we do not know whether the middle branch is n-terminal or c-terminal for an unmodified middle branch // in this case the modifID is 0, and both n-terminal (*) and c-terminal (**) fragments are generated if (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].nterminal) { // if the start modification for k == 1 or k == 4 is nterminal, generate n-terminal ions (*) - if (!(((k == 1) || (k == 4)) && (!parameters->searchedmodifications[trotationsoflassorotations[j][k].startmodifID].nterminal))) { - generateNTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, trotationsoflassorotations[j][k].bricks, parameters->fragmentionsfortheoreticalspectra[i], bricksdatabasewithcombinations, writedescription, j, splittingsites, parameters->searchedmodifications, lasso, &trotationsoflassorotations[j][k]); + if (!(((k == 1) || (k == 4)) && (!parameters->searchedmodifications[trotationsofbranchcyclicrotations[j][k].startmodifID].nterminal))) { + generateNTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, trotationsofbranchcyclicrotations[j][k].bricks, parameters->fragmentionsfortheoreticalspectra[i], bricksdatabasewithcombinations, writedescription, j, splittingsites, parameters->searchedmodifications, parameters->peptidetype, &trotationsofbranchcyclicrotations[j][k]); } } // all permmutations except 3 and 5 have invalid c-terms - if (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].cterminal && ((k == 3) || (k == 5) /*|| ((k == 0) && (trotationsoflassorotations[j][k].endsWithBracket())) || ((k == 2) && (trotationsoflassorotations[j][k].startsWithBracket()))*/)) { + if (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].cterminal && ((k == 3) || (k == 5) /*|| ((k == 0) && (trotationsofbranchcyclicrotations[j][k].endsWithBracket())) || ((k == 2) && (trotationsofbranchcyclicrotations[j][k].startsWithBracket()))*/)) { // if the end modification is cterminal, generate c-terminal ions (**) - if (parameters->searchedmodifications[trotationsoflassorotations[j][k].endmodifID].cterminal) { - generateCTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, trotationsoflassorotations[j][k].bricks, parameters->fragmentionsfortheoreticalspectra[i], bricksdatabasewithcombinations, writedescription, j, splittingsites, parameters->searchedmodifications, lasso, &trotationsoflassorotations[j][k]); + if (parameters->searchedmodifications[trotationsofbranchcyclicrotations[j][k].endmodifID].cterminal) { + generateCTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, trotationsofbranchcyclicrotations[j][k].bricks, parameters->fragmentionsfortheoreticalspectra[i], bricksdatabasewithcombinations, writedescription, j, splittingsites, parameters->searchedmodifications, parameters->peptidetype, &trotationsofbranchcyclicrotations[j][k]); } } @@ -1297,7 +1616,7 @@ int cTheoreticalSpectrum::compareLasso(cPeaksList& sortedpeaklist, cBricksDataba } - generatePrecursorIon(trotationsoflassorotations[0][0].bricks, bricksdatabasewithcombinations, theoreticalpeaksrealsize, writedescription); + generatePrecursorIon(trotationsofbranchcyclicrotations[0][0].bricks, bricksdatabasewithcombinations, theoreticalpeaksrealsize, writedescription); // search the theoretical peaks in the experimental peak list @@ -1307,20 +1626,20 @@ int cTheoreticalSpectrum::compareLasso(cPeaksList& sortedpeaklist, cBricksDataba // coverage of series - vector > > > series; - series.resize(lassorotations.size()); - for (int i = 0; i < (int)lassorotations.size(); i++) { + vector > > > series; + series.resize(branchcyclicrotations.size()); + for (int i = 0; i < (int)branchcyclicrotations.size(); i++) { series[i].resize(6); - //series[i].resize(trotationsoflassorotations[i].size()); // to do - potential bug + //series[i].resize(trotationsofbranchcyclicrotations[i].size()); // to do - potential bug for (int j = 0; j < (int)series[i].size(); j++) { for (int k = 0; k < (int)parameters->fragmentionsfortheoreticalspectra.size(); k++) { - series[i][j][parameters->fragmentionsfortheoreticalspectra[k]].resize(trotationsoflassorotations[i][0].bricks.size()); + series[i][j][parameters->fragmentionsfortheoreticalspectra[k]].resize(trotationsofbranchcyclicrotations[i][0].bricks.size()); } } for (int j = 0; j < theoreticalpeaksrealsize; j++) { - if ((theoreticalpeaks[j].matched > 0) && (theoreticalpeaks[j].rotationid >= 0) && (series[i][theoreticalpeaks[j].rotationid % 6].count(theoreticalpeaks[j].iontype) == 1)) { + if (!theoreticalpeaks[j].isotope && (theoreticalpeaks[j].matched > 0) && (theoreticalpeaks[j].rotationid >= 0) && (series[i][theoreticalpeaks[j].rotationid % 6].count(theoreticalpeaks[j].iontype) == 1)) { if (i == theoreticalpeaks[j].rotationid / 6) { series[i][theoreticalpeaks[j].rotationid % 6][theoreticalpeaks[j].iontype][theoreticalpeaks[j].seriesid]++; } @@ -1331,13 +1650,13 @@ int cTheoreticalSpectrum::compareLasso(cPeaksList& sortedpeaklist, cBricksDataba // peak hits without parents are removed if (parameters->clearhitswithoutparent) { - for (int i = 0; i < (int)lassorotations.size(); i++) { - for (int j = 0; j < (int)trotationsoflassorotations[i].size(); j++) { + for (int i = 0; i < (int)branchcyclicrotations.size(); i++) { + for (int j = 0; j < (int)trotationsofbranchcyclicrotations[i].size(); j++) { clearFalseHits(series[i][j], parameters->fragmentionsfortheoreticalspectra); } for (int j = 0; j < theoreticalpeaksrealsize; j++) { - if ((theoreticalpeaks[j].matched > 0) && (theoreticalpeaks[j].rotationid >= 0) && (i == theoreticalpeaks[j].rotationid / 6) && ((series[i][theoreticalpeaks[j].rotationid % 6].count(parameters->fragmentdefinitions[theoreticalpeaks[j].iontype].parent) == 0) || (series[i][theoreticalpeaks[j].rotationid % 6][parameters->fragmentdefinitions[theoreticalpeaks[j].iontype].parent][theoreticalpeaks[j].seriesid] == 0))) { + if (!theoreticalpeaks[j].isotope && (theoreticalpeaks[j].matched > 0) && (theoreticalpeaks[j].rotationid >= 0) && (i == theoreticalpeaks[j].rotationid / 6) && ((series[i][theoreticalpeaks[j].rotationid % 6].count(parameters->fragmentdefinitions[theoreticalpeaks[j].iontype].parent) == 0) || (series[i][theoreticalpeaks[j].rotationid % 6][parameters->fragmentdefinitions[theoreticalpeaks[j].iontype].parent][theoreticalpeaks[j].seriesid] == 0))) { experimentalpeakmatches[theoreticalpeaks[j].matchedid].erase(experimentalpeakmatches[theoreticalpeaks[j].matchedid].find(j)); experimentalpeaks[theoreticalpeaks[j].matchedid].matched--; @@ -1349,6 +1668,9 @@ int cTheoreticalSpectrum::compareLasso(cPeaksList& sortedpeaklist, cBricksDataba } + removeUnmatchedMetalIsotopes(theoreticalpeaks, theoreticalpeaksrealsize, experimentalpeaks, experimentalpeakmatches); + + // fill annotations of peaks for (int i = 0; i < (int)experimentalpeaks.size(); i++) { for (set::iterator it = experimentalpeakmatches[i].begin(); it != experimentalpeakmatches[i].end(); ++it) { @@ -1357,7 +1679,9 @@ int cTheoreticalSpectrum::compareLasso(cPeaksList& sortedpeaklist, cBricksDataba experimentalpeaks[i].description += ","; } experimentalpeaks[i].description += theoreticalpeaks[*it].description.substr(0,theoreticalpeaks[*it].description.find(':')); - theoreticalpeaks[*it].matchdescription += ", measured mass " + to_string((long double)experimentalpeaks[i].mzratio) + " matched with " + to_string((long double)ppmError(experimentalpeaks[i].mzratio, theoreticalpeaks[*it].mzratio)) + " ppm error"; + theoreticalpeaks[*it].matchedmz = experimentalpeaks[i].mzratio; + theoreticalpeaks[*it].matchedintensity = experimentalpeaks[i].intensity; + theoreticalpeaks[*it].matchedppm = ppmError(experimentalpeaks[i].mzratio, theoreticalpeaks[*it].mzratio); } experimentalpeaks[i].iontype = selectHigherPriorityIonTypeCID(experimentalpeaks[i].iontype,theoreticalpeaks[*it].iontype); @@ -1371,18 +1695,18 @@ int cTheoreticalSpectrum::compareLasso(cPeaksList& sortedpeaklist, cBricksDataba visualcoverage.clear(); coveragebyseries = "Linearized sequences from all ring break up points:
\n"; - for (int i = 0; i < (int)lassorotations.size(); i++) { - for (int j = 0; j < (int)trotationsoflassorotations[i].size(); j++) { + for (int i = 0; i < (int)branchcyclicrotations.size(); i++) { + for (int j = 0; j < (int)trotationsofbranchcyclicrotations[i].size(); j++) { coveragebyseries += to_string(splittingsites[i].first + 1) + "-" + to_string(splittingsites[i].second + 1) + "_"; coveragebyseries += to_string(j + 1) + " ~ "; - coveragebyseries += bricksdatabasewithcombinations.getAcronymNameOfTPeptide(trotationsoflassorotations[i][j].tcomposition, false); + coveragebyseries += bricksdatabasewithcombinations.getAcronymNameOfTPeptide(trotationsofbranchcyclicrotations[i][j].tcomposition, false); coveragebyseries += "
\n"; } coveragebyseries += "
\n"; } coveragebyseries += "Series of matched peaks:
\n"; - for (int i = 0; i < (int)lassorotations.size(); i++) { + for (int i = 0; i < (int)branchcyclicrotations.size(); i++) { for (int j = 0; j < (int)series[i].size(); j++) { for (int k = 0; k < (int)parameters->fragmentionsfortheoreticalspectra.size(); k++) { tempseries.name = to_string(splittingsites[i].first + 1) + "-" + to_string(splittingsites[i].second + 1) + "_"; @@ -1411,6 +1735,187 @@ int cTheoreticalSpectrum::compareLasso(cPeaksList& sortedpeaklist, cBricksDataba } +#if POLYKETIDE_SIDEROPHORES == 1 + + +int cTheoreticalSpectrum::compareLinearPolyketideSiderophore(cPeaksList& sortedpeaklist, cBricksDatabase& bricksdatabasewithcombinations, bool writedescription, regex& sequencetag, regex& searchedsequence) { + + if (!candidate.checkPolyketideSequence(bricksdatabasewithcombinations, parameters->peptidetype)) { + return -2; + } + + cBrick brick; + vector intcomposition; + vector splittingsites; + int theoreticalpeaksrealsize = 0; + + intcomposition.clear(); + brick.clear(); + brick.setComposition(candidate.getComposition(), false); + brick.explodeToIntComposition(intcomposition); + + eResidueLossType leftresiduelosstype = bricksdatabasewithcombinations[intcomposition[0] - 1].getResidueLossType(); + eResidueLossType rightresiduelosstype = bricksdatabasewithcombinations[intcomposition.back() - 1].getResidueLossType(); + + bool hasfirstblockartificial = bricksdatabasewithcombinations[intcomposition[0] - 1].isArtificial(); + bool haslastblockartificial = bricksdatabasewithcombinations[intcomposition.back() - 1].isArtificial(); + + if (!hasfirstblockartificial && !haslastblockartificial) { + if (((leftresiduelosstype == h2) && (candidate.getStartModifID() > 0) && parameters->searchedmodifications[candidate.getStartModifID()].cterminal) + || ((leftresiduelosstype == h2o2) && (candidate.getStartModifID() > 0) && parameters->searchedmodifications[candidate.getStartModifID()].nterminal) + || ((rightresiduelosstype == h2) && (candidate.getEndModifID() > 0) && parameters->searchedmodifications[candidate.getEndModifID()].cterminal) + || ((rightresiduelosstype == h2o2) && (candidate.getEndModifID() > 0) && parameters->searchedmodifications[candidate.getEndModifID()].nterminal)) { + return -2; + } + } + + cCandidate revertedcandidate = candidate; + revertedcandidate.revertComposition(); + + try { + if (!regex_search(candidate.getComposition(), sequencetag) && !regex_search(revertedcandidate.getComposition(), sequencetag)) { + return -2; + } + + if (writedescription) { + valid = false; + if ((parameters->searchedsequence.size() > 0) && + ((regex_search(candidate.getComposition(), searchedsequence) && + (parameters->searchedmodifications[candidate.getStartModifID()].name.compare(parameters->searchedsequenceNtermmodif) == 0) && + (parameters->searchedmodifications[candidate.getEndModifID()].name.compare(parameters->searchedsequenceCtermmodif) == 0)) + || + (regex_search(revertedcandidate.getComposition(), searchedsequence) && + (parameters->searchedmodifications[revertedcandidate.getStartModifID()].name.compare(parameters->searchedsequenceNtermmodif) == 0) && + (parameters->searchedmodifications[revertedcandidate.getEndModifID()].name.compare(parameters->searchedsequenceCtermmodif) == 0))) + ) { + valid = true; + } + } + } + catch (regex_error& /*e*/) { + return -2; + } + + for (int i = 0; i < (int)parameters->fragmentionsfortheoreticalspectra.size(); i++) { + if ( + ((hasfirstblockartificial || (leftresiduelosstype == h2)) && ((parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].parent == l1h_ion) || (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].parent == l2h_ion))) + || + ((hasfirstblockartificial || (leftresiduelosstype == h2o2)) && ((parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].parent == l1oh_ion) || (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].parent == l2oh_ion))) + ) { + generateNTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, intcomposition, parameters->fragmentionsfortheoreticalspectra[i], bricksdatabasewithcombinations, writedescription, 0, splittingsites, parameters->searchedmodifications, parameters->peptidetype, 0, leftresiduelosstype, hasfirstblockartificial); + } + if ( + ((haslastblockartificial || (rightresiduelosstype == h2)) && ((parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].parent == r1h_ion) || (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].parent == r2h_ion))) + || + ((haslastblockartificial || (rightresiduelosstype == h2o2)) && ((parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].parent == r1oh_ion) || (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].parent == r2oh_ion))) + ) { + generateCTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, intcomposition, parameters->fragmentionsfortheoreticalspectra[i], bricksdatabasewithcombinations, writedescription, 0, splittingsites, parameters->searchedmodifications, parameters->peptidetype, 0, rightresiduelosstype, haslastblockartificial); + } + } + + generatePrecursorIon(intcomposition, bricksdatabasewithcombinations, theoreticalpeaksrealsize, writedescription); + + + // search the theoretical peaks in the experimental peak list + experimentalpeaks = sortedpeaklist; + vector > experimentalpeakmatches; + searchForPeakPairs(theoreticalpeaks, theoreticalpeaksrealsize, experimentalpeaks, experimentalpeakmatches, parameters->fragmentmasserrortolerance); + + + // coverage of series + map > series; + for (int i = 0; i < (int)parameters->fragmentionsfortheoreticalspectra.size(); i++) { + series[parameters->fragmentionsfortheoreticalspectra[i]].resize(intcomposition.size()); + } + + for (int i = 0; i < theoreticalpeaksrealsize; i++) { + if (!theoreticalpeaks[i].isotope && (theoreticalpeaks[i].matched > 0) && (series.count(theoreticalpeaks[i].iontype) == 1)) { + series[theoreticalpeaks[i].iontype][theoreticalpeaks[i].seriesid]++; + } + } + + + // peak hits without parents are removed + if (parameters->clearhitswithoutparent) { + clearFalseHits(series, parameters->fragmentionsfortheoreticalspectra); + + for (int i = 0; i < theoreticalpeaksrealsize; i++) { + if (!theoreticalpeaks[i].isotope && (theoreticalpeaks[i].matched > 0) && (theoreticalpeaks[i].rotationid != -1) && ((series.count(parameters->fragmentdefinitions[theoreticalpeaks[i].iontype].parent) == 0) || (series[parameters->fragmentdefinitions[theoreticalpeaks[i].iontype].parent][theoreticalpeaks[i].seriesid] == 0))) { + experimentalpeakmatches[theoreticalpeaks[i].matchedid].erase(experimentalpeakmatches[theoreticalpeaks[i].matchedid].find(i)); + experimentalpeaks[theoreticalpeaks[i].matchedid].matched--; + + theoreticalpeaks[i].matched--; + theoreticalpeaks[i].matchedid = -1; + } + } + } + + + removeUnmatchedMetalIsotopes(theoreticalpeaks, theoreticalpeaksrealsize, experimentalpeaks, experimentalpeakmatches); + + + // fill annotations of peaks + for (int i = 0; i < (int)experimentalpeaks.size(); i++) { + for (set::iterator it = experimentalpeakmatches[i].begin(); it != experimentalpeakmatches[i].end(); ++it) { + if (writedescription) { + if (experimentalpeaks[i].description.compare("") != 0) { + experimentalpeaks[i].description += ","; + } + experimentalpeaks[i].description += theoreticalpeaks[*it].description.substr(0,theoreticalpeaks[*it].description.find(':')); + theoreticalpeaks[*it].matchedmz = experimentalpeaks[i].mzratio; + theoreticalpeaks[*it].matchedintensity = experimentalpeaks[i].intensity; + theoreticalpeaks[*it].matchedppm = ppmError(experimentalpeaks[i].mzratio, theoreticalpeaks[*it].mzratio); + } + + experimentalpeaks[i].iontype = selectHigherPriorityIonTypeCID(experimentalpeaks[i].iontype,theoreticalpeaks[*it].iontype); + } + } + + + if (writedescription) { + + visualcoverage.clear(); + visualSeries tempseries; + + coveragebyseries = "Series of matched peaks:
\n"; + for (int i = 0; i < (int)parameters->fragmentionsfortheoreticalspectra.size(); i++) { + tempseries.name = parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name; + coveragebyseries += tempseries.name + " "; + tempseries.series.clear(); + for (int j = 0; j < (int)series[parameters->fragmentionsfortheoreticalspectra[i]].size() - 1; j++) { + coveragebyseries += to_string(series[parameters->fragmentionsfortheoreticalspectra[i]][j]); + tempseries.series.push_back(series[parameters->fragmentionsfortheoreticalspectra[i]][j]); + if (j < (int)series[parameters->fragmentionsfortheoreticalspectra[i]].size() - 2) { + coveragebyseries += " "; + } + } + coveragebyseries += "
\n"; + visualcoverage.push_back(tempseries); + } + + } + + computeStatistics(writedescription); + + return theoreticalpeaksrealsize; + +} + + +int cTheoreticalSpectrum::compareCyclicPolyketideSiderophore(cPeaksList& sortedpeaklist, cBricksDatabase& bricksdatabasewithcombinations, bool writedescription, regex& sequencetag, regex& searchedsequence) { + + if (!candidate.checkPolyketideSequence(bricksdatabasewithcombinations, parameters->peptidetype)) { + return -2; + } + + return compareCyclic(sortedpeaklist, bricksdatabasewithcombinations, writedescription, sequencetag, searchedsequence); + +} + + +#endif + + int cTheoreticalSpectrum::compareLinearPolysaccharide(cPeaksList& sortedpeaklist, cBricksDatabase& bricksdatabasewithcombinations, bool writedescription, regex& sequencetag, regex& searchedsequence) { cBrick brick; vector intcomposition; @@ -1442,10 +1947,10 @@ int cTheoreticalSpectrum::compareLinearPolysaccharide(cPeaksList& sortedpeaklist for (int i = 0; i < (int)parameters->fragmentionsfortheoreticalspectra.size(); i++) { if (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].nterminal) { - generateNTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, intcomposition, parameters->fragmentionsfortheoreticalspectra[i], bricksdatabasewithcombinations, writedescription, 0, splittingsites, parameters->searchedmodifications, linearpolysaccharide); + generateNTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, intcomposition, parameters->fragmentionsfortheoreticalspectra[i], bricksdatabasewithcombinations, writedescription, 0, splittingsites, parameters->searchedmodifications, parameters->peptidetype); } if (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].cterminal) { - generateCTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, intcomposition, parameters->fragmentionsfortheoreticalspectra[i], bricksdatabasewithcombinations, writedescription, 0, splittingsites, parameters->searchedmodifications, linearpolysaccharide); + generateCTerminalFragmentIons(parameters->precursorcharge, theoreticalpeaksrealsize, intcomposition, parameters->fragmentionsfortheoreticalspectra[i], bricksdatabasewithcombinations, writedescription, 0, splittingsites, parameters->searchedmodifications, parameters->peptidetype); } } @@ -1459,13 +1964,13 @@ int cTheoreticalSpectrum::compareLinearPolysaccharide(cPeaksList& sortedpeaklist // coverage of series - map > series; + map > series; for (int i = 0; i < (int)parameters->fragmentionsfortheoreticalspectra.size(); i++) { series[parameters->fragmentionsfortheoreticalspectra[i]].resize(intcomposition.size()); } for (int i = 0; i < theoreticalpeaksrealsize; i++) { - if ((theoreticalpeaks[i].matched > 0) && (series.count(theoreticalpeaks[i].iontype) == 1)) { + if (!theoreticalpeaks[i].isotope && (theoreticalpeaks[i].matched > 0) && (series.count(theoreticalpeaks[i].iontype) == 1)) { series[theoreticalpeaks[i].iontype][theoreticalpeaks[i].seriesid]++; } } @@ -1476,7 +1981,7 @@ int cTheoreticalSpectrum::compareLinearPolysaccharide(cPeaksList& sortedpeaklist clearFalseHits(series, parameters->fragmentionsfortheoreticalspectra); for (int i = 0; i < theoreticalpeaksrealsize; i++) { - if ((theoreticalpeaks[i].matched > 0) && (theoreticalpeaks[i].rotationid != -1) && ((series.count(parameters->fragmentdefinitions[theoreticalpeaks[i].iontype].parent) == 0) || (series[parameters->fragmentdefinitions[theoreticalpeaks[i].iontype].parent][theoreticalpeaks[i].seriesid] == 0))) { + if (!theoreticalpeaks[i].isotope && (theoreticalpeaks[i].matched > 0) && (theoreticalpeaks[i].rotationid != -1) && ((series.count(parameters->fragmentdefinitions[theoreticalpeaks[i].iontype].parent) == 0) || (series[parameters->fragmentdefinitions[theoreticalpeaks[i].iontype].parent][theoreticalpeaks[i].seriesid] == 0))) { experimentalpeakmatches[theoreticalpeaks[i].matchedid].erase(experimentalpeakmatches[theoreticalpeaks[i].matchedid].find(i)); experimentalpeaks[theoreticalpeaks[i].matchedid].matched--; @@ -1487,6 +1992,9 @@ int cTheoreticalSpectrum::compareLinearPolysaccharide(cPeaksList& sortedpeaklist } + removeUnmatchedMetalIsotopes(theoreticalpeaks, theoreticalpeaksrealsize, experimentalpeaks, experimentalpeakmatches); + + // fill annotations of peaks for (int i = 0; i < (int)experimentalpeaks.size(); i++) { for (set::iterator it = experimentalpeakmatches[i].begin(); it != experimentalpeakmatches[i].end(); ++it) { @@ -1495,7 +2003,9 @@ int cTheoreticalSpectrum::compareLinearPolysaccharide(cPeaksList& sortedpeaklist experimentalpeaks[i].description += ","; } experimentalpeaks[i].description += theoreticalpeaks[*it].description.substr(0,theoreticalpeaks[*it].description.find(':')); - theoreticalpeaks[*it].matchdescription += ", measured mass " + to_string((long double)experimentalpeaks[i].mzratio) + " matched with " + to_string((long double)ppmError(experimentalpeaks[i].mzratio, theoreticalpeaks[*it].mzratio)) + " ppm error"; + theoreticalpeaks[*it].matchedmz = experimentalpeaks[i].mzratio; + theoreticalpeaks[*it].matchedintensity = experimentalpeaks[i].intensity; + theoreticalpeaks[*it].matchedppm = ppmError(experimentalpeaks[i].mzratio, theoreticalpeaks[*it].mzratio); } // to do @@ -1533,22 +2043,78 @@ int cTheoreticalSpectrum::compareLinearPolysaccharide(cPeaksList& sortedpeaklist } -void cTheoreticalSpectrum::compareMSSpectrum(cParameters* parameters) { - experimentalpeaks = parameters->peaklist; - experimentalpeaks.sortbyMass(); - +void cTheoreticalSpectrum::generateMSSpectrum() { cPeak peak; cSummaryFormula formula; - theoreticalpeaks.resize((int)parameters->fragmentionsfortheoreticalspectra.size()*parameters->sequencedatabase.size()*abs(parameters->precursorcharge)); + string metalname; + regex rx; + + theoreticalpeaks.resize(((int)parameters->fragmentionsfortheoreticalspectra.size() + countIsotopesOfMetals(parameters)) * parameters->sequencedatabase.size() * abs(parameters->precursorcharge)); + int theoreticalpeaksrealsize = 0; + for (int i = 0; i < parameters->sequencedatabase.size(); i++) { + peak.clear(); + peak.isotope = false; + formula.clear(); formula.setFormula(parameters->sequencedatabase[i].getSummaryFormula()); for (int j = 0; j < (int)parameters->fragmentionsfortheoreticalspectra.size(); j++) { + parameters->precursorAdductHasLi = false; + parameters->precursorAdductHasMg = false; + parameters->precursorAdductHasK = false; + parameters->precursorAdductHasCa = false; + parameters->precursorAdductHasFe = false; + parameters->precursorAdductHasNi = false; + parameters->precursorAdductHasCu = false; + parameters->precursorAdductHasZn = false; + parameters->precursorAdductHasGa = false; + + metalname = parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[j]].name; + + // to do - optimize performance + rx = "Li"; + if (regex_search(metalname, rx)) { + parameters->precursorAdductHasLi = true; + } + rx = "Mg"; + if (regex_search(metalname, rx)) { + parameters->precursorAdductHasMg = true; + } + rx = "K"; + if (regex_search(metalname, rx)) { + parameters->precursorAdductHasK = true; + } + rx = "Ca"; + if (regex_search(metalname, rx)) { + parameters->precursorAdductHasCa = true; + } + rx = "Fe"; + if (regex_search(metalname, rx)) { + parameters->precursorAdductHasFe = true; + } + rx = "Ni"; + if (regex_search(metalname, rx)) { + parameters->precursorAdductHasNi = true; + } + rx = "Cu"; + if (regex_search(metalname, rx)) { + parameters->precursorAdductHasCu = true; + } + rx = "Zn"; + if (regex_search(metalname, rx)) { + parameters->precursorAdductHasZn = true; + } + rx = "Ga"; + if (regex_search(metalname, rx)) { + parameters->precursorAdductHasGa = true; + } + for (int k = 0; k < abs(parameters->precursorcharge); k++) { + peak.mzratio = (double)parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[j]].multiplier*formula.getMass() + parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[j]].massdifference; if (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[j]].positive) { peak.mzratio += (double)k*Hplus; @@ -1565,19 +2131,30 @@ void cTheoreticalSpectrum::compareMSSpectrum(cParameters* parameters) { else { peak.description += "-"; } - //peak.description += " " + parameters->sequencedatabase[i].getName() + " (" + parameters->sequencedatabase[i].getSummaryFormula() + "): "; peak.description += " " + parameters->sequencedatabase[i].getNameWithReferenceAsHTMLString() + " (" + parameters->sequencedatabase[i].getSummaryFormula() + "): "; + + addPeakToList(peak, theoreticalpeaksrealsize); + addMetalPeaks(peak, theoreticalpeaksrealsize, k + 1, true); - theoreticalpeaks[(i*(int)parameters->fragmentionsfortheoreticalspectra.size() + j)*abs(parameters->precursorcharge) + k] = peak; } - + } + } - theoreticalpeaks.sortbyMass(); + + theoreticalpeaks.resize(theoreticalpeaksrealsize); +} + + +void cTheoreticalSpectrum::compareMSSpectrum(cPeaksList& peaklist) { + experimentalpeaks = peaklist; + experimentalpeaks.sortbyMass(); vector > experimentalpeakmatches; searchForPeakPairs(theoreticalpeaks, theoreticalpeaks.size(), experimentalpeaks, experimentalpeakmatches, parameters->fragmentmasserrortolerance); + removeUnmatchedMetalIsotopes(theoreticalpeaks, theoreticalpeaks.size(), experimentalpeaks, experimentalpeakmatches); + // fill annotations of peaks for (int i = 0; i < (int)experimentalpeaks.size(); i++) { for (set::iterator it = experimentalpeakmatches[i].begin(); it != experimentalpeakmatches[i].end(); ++it) { @@ -1586,11 +2163,25 @@ void cTheoreticalSpectrum::compareMSSpectrum(cParameters* parameters) { experimentalpeaks[i].description += ","; } experimentalpeaks[i].description += theoreticalpeaks[*it].description.substr(0,theoreticalpeaks[*it].description.rfind(':')); - theoreticalpeaks[*it].matchdescription += ", measured mass " + to_string((long double)experimentalpeaks[i].mzratio) + " matched with " + to_string((long double)ppmError(experimentalpeaks[i].mzratio, theoreticalpeaks[*it].mzratio)) + " ppm error"; + theoreticalpeaks[*it].matchedmz = experimentalpeaks[i].mzratio; + theoreticalpeaks[*it].matchedintensity = experimentalpeaks[i].intensity; + theoreticalpeaks[*it].matchedppm = ppmError(experimentalpeaks[i].mzratio, theoreticalpeaks[*it].mzratio); } } computeStatistics(true); + + cPeaksList tmppeaklist = theoreticalpeaks; + theoreticalpeaks.clear(); + + // remove unmatched theoretical peaks to save memory + for (int i = 0; i < tmppeaklist.size(); i++) { + if (tmppeaklist[i].matchedmz > 0) { + theoreticalpeaks.add(tmppeaklist[i]); + } + } + + theoreticalpeaks.sortbyMass(); } @@ -1609,7 +2200,7 @@ int cTheoreticalSpectrum::getNumberOfScrambledPeaks() { } -int cTheoreticalSpectrum::getNumberOfMatchedPeaks(fragmentIonType iontype) const { +int cTheoreticalSpectrum::getNumberOfMatchedPeaks(eFragmentIonType iontype) const { return matchedions.at(iontype); } @@ -1624,138 +2215,26 @@ double cTheoreticalSpectrum::getRatioOfMatchedPeaks() { } -void cTheoreticalSpectrum::printMatch(ofstream& os, peptideType peptidetype) { - vector rotations; - - os << endl << "Peptide: " << candidate.getRealPeptideName() << endl; - - switch (peptidetype) - { - case linear: - case cyclic: - case linearpolysaccharide: - os << "Composition: " << candidate.getComposition(); - break; - case branched: - case lasso: - os << "Composition: " << candidate.getTComposition(); - break; - case other: - break; - default: - break; - } - - if (valid) { - os << " (valid)"; - } - os << endl; - - os << "Number of bricks: " << getNumberOfBricks(candidate.getComposition()) << endl << endl; - - if ((peptidetype == linear) || (peptidetype == linearpolysaccharide)) { - os << "Modifications: "; - os << candidate.getStartModifID() << " " << candidate.getEndModifID() << endl; - } - - if (peptidetype == branched) { - os << "Modifications: "; - os << candidate.getStartModifID() << " " << candidate.getMiddleModifID() << " " << candidate.getEndModifID() << endl; - } - - if (peptidetype == lasso) { - os << "Modifications: "; - os << candidate.getMiddleModifID() << endl; - } - - os << "Coverage by series:" << endl; - os << coveragebyseries << endl; - - switch (peptidetype) - { - case linear: - os << "Matched y-ions and b-ions: " << matchedions[y_ion] + matchedions[b_ion] << endl; - os << "Matched y-ions: " << matchedions[y_ion] << endl; - os << "Matched b-ions: " << matchedions[b_ion] << endl; - os << "Matched a-ions: " << matchedions[a_ion] << endl; - os << "Matched y*-ions: " << matchedions[y_ion_dehydrated] << endl; - os << "Matched b*-ions: " << matchedions[b_ion_dehydrated] << endl; - os << "Matched a*-ions: " << matchedions[a_ion_dehydrated] << endl; - os << "Matched yx-ions: " << matchedions[y_ion_deamidated] << endl; - os << "Matched bx-ions: " << matchedions[b_ion_deamidated] << endl; - os << "Matched ax-ions: " << matchedions[a_ion_deamidated] << endl; - break; - case cyclic: - os << "Matched b-ions: " << matchedions[b_ion] << endl; - os << "Matched a-ions: " << matchedions[a_ion] << endl; - os << "Matched b*-ions: " << matchedions[b_ion_dehydrated] << endl; - os << "Matched a*-ions: " << matchedions[a_ion_dehydrated] << endl; - os << "Matched bx-ions: " << matchedions[b_ion_deamidated] << endl; - os << "Matched ax-ions: " << matchedions[a_ion_deamidated] << endl; - os << "Matched b*x-ions: " << matchedions[b_ion_dehydrated_and_deamidated] << endl; - os << "Matched a*x-ions: " << matchedions[a_ion_dehydrated_and_deamidated] << endl; - os << "Matched b-ions and b*-ions: " << matchedions[b_ion] + matchedions[b_ion_dehydrated] << endl; - os << "Matched b-ions and bx-ions: " << matchedions[b_ion] + matchedions[b_ion_deamidated] << endl << endl; - break; - case branched: - os << "Matched y-ions and b-ions: " << matchedions[y_ion] + matchedions[b_ion] << endl; - os << "Matched y-ions: " << matchedions[y_ion] << endl; - os << "Matched b-ions: " << matchedions[b_ion] << endl; - os << "Matched a-ions: " << matchedions[a_ion] << endl; - os << "Matched y*-ions: " << matchedions[y_ion_dehydrated] << endl; - os << "Matched b*-ions: " << matchedions[b_ion_dehydrated] << endl; - os << "Matched a*-ions: " << matchedions[a_ion_dehydrated] << endl; - os << "Matched yx-ions: " << matchedions[y_ion_deamidated] << endl; - os << "Matched bx-ions: " << matchedions[b_ion_deamidated] << endl; - os << "Matched ax-ions: " << matchedions[a_ion_deamidated] << endl; - break; - case lasso: - os << "Matched y-ions and b-ions: " << matchedions[y_ion] + matchedions[b_ion] << endl; - os << "Matched y-ions: " << matchedions[y_ion] << endl; - os << "Matched b-ions: " << matchedions[b_ion] << endl; - os << "Matched a-ions: " << matchedions[a_ion] << endl; - os << "Matched y*-ions: " << matchedions[y_ion_dehydrated] << endl; - os << "Matched b*-ions: " << matchedions[b_ion_dehydrated] << endl; - os << "Matched a*-ions: " << matchedions[a_ion_dehydrated] << endl; - os << "Matched yx-ions: " << matchedions[y_ion_deamidated] << endl; - os << "Matched bx-ions: " << matchedions[b_ion_deamidated] << endl; - os << "Matched ax-ions: " << matchedions[a_ion_deamidated] << endl; - break; - case linearpolysaccharide: - break; - case other: - break; - default: - break; - } - - os << "Matched peaks: " << experimentalpeaksmatched << endl; - if (peptidetype == cyclic) { - os << "Scrambled peaks matched: " << scrambledpeaksmatched << endl; - } - os << "Total peaks: " << peakstested << endl; - os << "Sum of Relative Intensities: " << intensityweightedscore << endl; - os << "Ratio of matched peaks: " << experimentalpeaksmatchedratio*100.0f << "%" << endl << endl; - os << "Unmatched measured peaks:" << endl << unmatchedpeaks << endl << endl; - os << "Theoretical peaks:" << endl << theoreticalpeaks.print(); -} - - -void cTheoreticalSpectrum::generateNTerminalFragmentIons(int maxcharge, int& peaklistrealsize, vector& intcomposition, fragmentIonType fragmentiontype, cBricksDatabase& bricksdatabase, bool writedescription, int rotationid, vector& splittingsites, vector& searchedmodifications, peptideType peptidetype, TRotationInfo* trotation) { +void cTheoreticalSpectrum::generateNTerminalFragmentIons(int maxcharge, int& peaklistrealsize, vector& intcomposition, eFragmentIonType fragmentiontype, cBricksDatabase& bricksdatabase, bool writedescription, int rotationid, vector& splittingsites, vector& searchedmodifications, ePeptideType peptidetype, TRotationInfo* trotation, eResidueLossType leftresiduelosstype, bool hasfirstblockartificial) { cPeak peak; double tempratio; string tempdescription; peak.mzratio = parameters->fragmentdefinitions[fragmentiontype].massdifference; - if ((peptidetype == linear) || (peptidetype == linearpolysaccharide)) { + if ((peptidetype == linear) || (peptidetype == linearpolysaccharide) +#if POLYKETIDE_SIDEROPHORES == 1 + || (peptidetype == linearpolyketide) +#endif + ) { peak.mzratio += searchedmodifications[candidate.getStartModifID()].massdifference; } - if ((peptidetype == branched) || (peptidetype == lasso)) { + if ((peptidetype == branched) || (peptidetype == branchcyclic)) { peak.mzratio += searchedmodifications[trotation->startmodifID].massdifference; } peak.iontype = fragmentiontype; peak.scrambled = false; + peak.isotope = false; switch (peptidetype) { @@ -1768,12 +2247,20 @@ void cTheoreticalSpectrum::generateNTerminalFragmentIons(int maxcharge, int& pea case branched: peak.rotationid = trotation->id; break; - case lasso: + case branchcyclic: peak.rotationid = rotationid*6 + trotation->id; // to do - potential bug break; case linearpolysaccharide: peak.rotationid = rotationid; break; +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + peak.rotationid = rotationid; + break; + case cyclicpolyketide: + peak.rotationid = rotationid; + break; +#endif case other: break; default: @@ -1784,7 +2271,7 @@ void cTheoreticalSpectrum::generateNTerminalFragmentIons(int maxcharge, int& pea peak.mzratio += bricksdatabase[intcomposition[i] - 1].getMass(); - if ((peptidetype == branched) || (peptidetype == lasso)) { + if ((peptidetype == branched) || (peptidetype == branchcyclic)) { if (i == trotation->middlebranchstart) { peak.mzratio += searchedmodifications[trotation->middlemodifID].massdifference; } @@ -1804,25 +2291,91 @@ void cTheoreticalSpectrum::generateNTerminalFragmentIons(int maxcharge, int& pea //} } +#if POLYKETIDE_SIDEROPHORES == 1 + if (peptidetype == linearpolyketide) { + if (!hasfirstblockartificial && (leftresiduelosstype == h2) && (parameters->fragmentdefinitions[fragmentiontype].parent == l1h_ion) && (i % 2 == 0)) { + continue; + } + + if (!hasfirstblockartificial && (leftresiduelosstype == h2) && (parameters->fragmentdefinitions[fragmentiontype].parent == l2h_ion) && (i % 2 == 1)) { + continue; + } + + if (!hasfirstblockartificial && (leftresiduelosstype == h2o2) && (parameters->fragmentdefinitions[fragmentiontype].parent == l1oh_ion) && (i % 2 == 1)) { + continue; + } + + if (!hasfirstblockartificial && (leftresiduelosstype == h2o2) && (parameters->fragmentdefinitions[fragmentiontype].parent == l2oh_ion) && (i % 2 == 0)) { + continue; + } + } + + if (peptidetype == cyclicpolyketide) { + + if (!hasfirstblockartificial && (leftresiduelosstype == h2) && (parameters->fragmentdefinitions[fragmentiontype].parent == l0h_ion)) { // b+2H ion is generated instead of b-2H + continue; + } + + if (!hasfirstblockartificial && (leftresiduelosstype == h2) && (parameters->fragmentdefinitions[fragmentiontype].parent == l1h_ion) && (i % 2 == 0)) { + continue; + } + + if (!hasfirstblockartificial && (leftresiduelosstype == h2) && (parameters->fragmentdefinitions[fragmentiontype].parent == l2h_ion) && (i % 2 == 1)) { + continue; + } + + if (!hasfirstblockartificial && (leftresiduelosstype == h2o2) && (parameters->fragmentdefinitions[fragmentiontype].parent == l0h_ion) && (i % 2 == 1)) { + continue; + } + + if (!hasfirstblockartificial && (leftresiduelosstype == h2o2) && (parameters->fragmentdefinitions[fragmentiontype].parent == l1h_ion) && (i % 2 == 0)) { // ok - even numbers of blocks => always b-ion + continue; + } + + if (!hasfirstblockartificial && (leftresiduelosstype == h2o2) && (parameters->fragmentdefinitions[fragmentiontype].parent == l2h_ion)) { // b-2H ion is generated instead of b+2H + continue; + } + } +#endif + peak.seriesid = i; if (writedescription) { peak.description = ""; +#if POLYKETIDE_SIDEROPHORES == 1 + if ((peptidetype == cyclic) || (peptidetype == cyclicpolyketide)) { +#else if (peptidetype == cyclic) { +#endif peak.description += to_string(splittingsites[rotationid].first + 1) + "-" + to_string(splittingsites[rotationid].second + 1) + "_"; } if (peptidetype == branched) { peak.description += to_string(peak.rotationid + 1) + "_"; } - if (peptidetype == lasso) { + if (peptidetype == branchcyclic) { peak.description += to_string(splittingsites[rotationid].first + 1) + "-" + to_string(splittingsites[rotationid].second + 1) + "_"; peak.description += to_string(trotation->id + 1) + "_"; } - peak.description += parameters->fragmentdefinitions[fragmentiontype].name.substr(0, 1) + to_string(i + 1); - if (parameters->fragmentdefinitions[fragmentiontype].name.size() > 1) { - peak.description += parameters->fragmentdefinitions[fragmentiontype].name.substr(1, parameters->fragmentdefinitions[fragmentiontype].name.length() - 1); + +#if POLYKETIDE_SIDEROPHORES == 1 + if ((peptidetype == linearpolyketide) || (peptidetype == cyclicpolyketide)) { + peak.description += parameters->fragmentdefinitions[fragmentiontype].name.substr(0, 2) + to_string(i + 1); + if (parameters->fragmentdefinitions[fragmentiontype].name.size() > 2) { + peak.description += parameters->fragmentdefinitions[fragmentiontype].name.substr(2, parameters->fragmentdefinitions[fragmentiontype].name.length() - 2); + } + } + else { +#endif + peak.description += parameters->fragmentdefinitions[fragmentiontype].name.substr(0, 1) + to_string(i + 1); + if (parameters->fragmentdefinitions[fragmentiontype].name.size() > 1) { + peak.description += parameters->fragmentdefinitions[fragmentiontype].name.substr(1, parameters->fragmentdefinitions[fragmentiontype].name.length() - 1); + } +#if POLYKETIDE_SIDEROPHORES == 1 } +#endif + + addAdductToDescription(peak.description); peak.description += ": "; for (int j = 0; j <= i; j++) { peak.description += "[" + bricksdatabase[intcomposition[j] - 1].getAcronymsAsString() + "]"; @@ -1852,13 +2405,8 @@ void cTheoreticalSpectrum::generateNTerminalFragmentIons(int maxcharge, int& pea } } - if (theoreticalpeaks.size() > peaklistrealsize) { - theoreticalpeaks[peaklistrealsize] = peak; - } - else { - theoreticalpeaks.add(peak); - } - peaklistrealsize++; + addPeakToList(peak, peaklistrealsize); + addMetalPeaks(peak, peaklistrealsize, j, writedescription); } peak.mzratio = tempratio; @@ -1873,21 +2421,26 @@ void cTheoreticalSpectrum::generateNTerminalFragmentIons(int maxcharge, int& pea } -void cTheoreticalSpectrum::generateCTerminalFragmentIons(int maxcharge, int& peaklistrealsize, vector& intcomposition, fragmentIonType fragmentiontype, cBricksDatabase& bricksdatabase, bool writedescription, int rotationid, vector& splittingsites, vector& searchedmodifications, peptideType peptidetype, TRotationInfo* trotation) { +void cTheoreticalSpectrum::generateCTerminalFragmentIons(int maxcharge, int& peaklistrealsize, vector& intcomposition, eFragmentIonType fragmentiontype, cBricksDatabase& bricksdatabase, bool writedescription, int rotationid, vector& splittingsites, vector& searchedmodifications, ePeptideType peptidetype, TRotationInfo* trotation, eResidueLossType rightresiduelosstype, bool haslastblockartificial) { cPeak peak; double tempratio; string tempdescription; peak.mzratio = parameters->fragmentdefinitions[fragmentiontype].massdifference; - if ((peptidetype == linear) || (peptidetype == linearpolysaccharide)) { + if ((peptidetype == linear) || (peptidetype == linearpolysaccharide) +#if POLYKETIDE_SIDEROPHORES == 1 + || (peptidetype == linearpolyketide) +#endif + ) { peak.mzratio += searchedmodifications[candidate.getEndModifID()].massdifference; } - if ((peptidetype == branched) || (peptidetype == lasso)) { + if ((peptidetype == branched) || (peptidetype == branchcyclic)) { peak.mzratio += searchedmodifications[trotation->endmodifID].massdifference; } peak.iontype = fragmentiontype; peak.scrambled = false; + peak.isotope = false; switch (peptidetype) { @@ -1900,9 +2453,17 @@ void cTheoreticalSpectrum::generateCTerminalFragmentIons(int maxcharge, int& pea case branched: peak.rotationid = trotation->id; break; - case lasso: + case branchcyclic: peak.rotationid = rotationid*6 + trotation->id; // to do - potential bug break; +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + peak.rotationid = rotationid; + break; + case cyclicpolyketide: + peak.rotationid = rotationid; + break; +#endif case linearpolysaccharide: peak.rotationid = rotationid; break; @@ -1912,10 +2473,12 @@ void cTheoreticalSpectrum::generateCTerminalFragmentIons(int maxcharge, int& pea break; } + int order = -1; for (int i = (int)intcomposition.size() - 1; i > 0; i--) { peak.mzratio += bricksdatabase[intcomposition[i] - 1].getMass(); + order++; - if ((peptidetype == branched) || (peptidetype == lasso)) { + if ((peptidetype == branched) || (peptidetype == branchcyclic)) { if (i == trotation->middlebranchend) { peak.mzratio += searchedmodifications[trotation->middlemodifID].massdifference; } @@ -1935,26 +2498,65 @@ void cTheoreticalSpectrum::generateCTerminalFragmentIons(int maxcharge, int& pea //} } +#if POLYKETIDE_SIDEROPHORES == 1 + if (peptidetype == linearpolyketide) { + if (!haslastblockartificial && (rightresiduelosstype == h2) && (parameters->fragmentdefinitions[fragmentiontype].parent == r1h_ion) && (order % 2 == 0)) { + continue; + } + + if (!haslastblockartificial && (rightresiduelosstype == h2) && (parameters->fragmentdefinitions[fragmentiontype].parent == r2h_ion) && (order % 2 == 1)) { + continue; + } + + if (!haslastblockartificial && (rightresiduelosstype == h2o2) && (parameters->fragmentdefinitions[fragmentiontype].parent == r1oh_ion) && (order % 2 == 1)) { + continue; + } + + if (!haslastblockartificial && (rightresiduelosstype == h2o2) && (parameters->fragmentdefinitions[fragmentiontype].parent == r2oh_ion) && (order % 2 == 0)) { + continue; + } + } +#endif + peak.seriesid = (int)intcomposition.size() - i - 1; if (writedescription) { peak.description = ""; +#if POLYKETIDE_SIDEROPHORES == 1 + if (peptidetype == cyclicpolyketide) { + peak.description += to_string(splittingsites[rotationid].first + 1) + "-" + to_string(splittingsites[rotationid].second + 1) + "_"; + } +#endif //if (peptidetype == cyclic) { - // peak.description += to_string(splittingsites[rotationid].first + 1) + "-" + to_string(splittingsites[rotationid].second + 1); // +"_"; + // peak.description += to_string(splittingsites[rotationid].first + 1) + "-" + to_string(splittingsites[rotationid].second + 1) + "_"; //} if (peptidetype == branched) { peak.description += to_string(peak.rotationid + 1) + "_"; } - if (peptidetype == lasso) { + if (peptidetype == branchcyclic) { peak.description += to_string(splittingsites[rotationid].first + 1) + "-" + to_string(splittingsites[rotationid].second + 1) + "_"; peak.description += to_string(trotation->id + 1) + "_"; } - peak.description += parameters->fragmentdefinitions[fragmentiontype].name.substr(0, 1) + to_string((int)intcomposition.size() - i); - - if (parameters->fragmentdefinitions[fragmentiontype].name.size() > 1) { - peak.description += parameters->fragmentdefinitions[fragmentiontype].name.substr(1, parameters->fragmentdefinitions[fragmentiontype].name.length() - 1); + +#if POLYKETIDE_SIDEROPHORES == 1 + if ((peptidetype == linearpolyketide) || (peptidetype == cyclicpolyketide)) { + peak.description += parameters->fragmentdefinitions[fragmentiontype].name.substr(0, 2) + to_string((int)intcomposition.size() - i); + if (parameters->fragmentdefinitions[fragmentiontype].name.size() > 2) { + peak.description += parameters->fragmentdefinitions[fragmentiontype].name.substr(2, parameters->fragmentdefinitions[fragmentiontype].name.length() - 2); + } + } + else { +#endif + peak.description += parameters->fragmentdefinitions[fragmentiontype].name.substr(0, 1) + to_string((int)intcomposition.size() - i); + if (parameters->fragmentdefinitions[fragmentiontype].name.size() > 1) { + peak.description += parameters->fragmentdefinitions[fragmentiontype].name.substr(1, parameters->fragmentdefinitions[fragmentiontype].name.length() - 1); + } +#if POLYKETIDE_SIDEROPHORES == 1 } +#endif + + addAdductToDescription(peak.description); peak.description += ": "; for (int j = (int)intcomposition.size() - 1; j >= i; j--) { peak.description += "[" + bricksdatabase[intcomposition[j] - 1].getAcronymsAsString() + "]"; @@ -1983,14 +2585,9 @@ void cTheoreticalSpectrum::generateCTerminalFragmentIons(int maxcharge, int& pea peak.description = to_string(j) + "- " + tempdescription; } } - - if (theoreticalpeaks.size() > peaklistrealsize) { - theoreticalpeaks[peaklistrealsize] = peak; - } - else { - theoreticalpeaks.add(peak); - } - peaklistrealsize++; + + addPeakToList(peak, peaklistrealsize); + addMetalPeaks(peak, peaklistrealsize, j, writedescription); } peak.mzratio = tempratio; @@ -2040,8 +2637,8 @@ string cTheoreticalSpectrum::getCoverageBySeries() { } -string cTheoreticalSpectrum::getUnmatchedPeaks() { - return unmatchedpeaks; +cPeaksList* cTheoreticalSpectrum::getUnmatchedPeaks() { + return &unmatchedpeaks; } @@ -2102,15 +2699,15 @@ void cTheoreticalSpectrum::store(ofstream& os) { size = (int)matchedions.size(); os.write((char *)&size, sizeof(int)); - for (map::iterator it = matchedions.begin(); it != matchedions.end(); ++it) { - os.write((char *)&it->first, sizeof(fragmentIonType)); + for (map::iterator it = matchedions.begin(); it != matchedions.end(); ++it) { + os.write((char *)&it->first, sizeof(eFragmentIonType)); os.write((char *)&it->second, sizeof(int)); } os.write((char *)&peakstested, sizeof(int)); os.write((char *)&experimentalpeaksmatchedratio, sizeof(double)); - storeString(unmatchedpeaks, os); + unmatchedpeaks.store(os); storeString(coveragebyseries, os); os.write((char *)&valid, sizeof(bool)); @@ -2131,7 +2728,7 @@ void cTheoreticalSpectrum::store(ofstream& os) { void cTheoreticalSpectrum::load(ifstream& is) { int size; - fragmentIonType iontype; + eFragmentIonType iontype; int number; parameters = 0; @@ -2146,7 +2743,7 @@ void cTheoreticalSpectrum::load(ifstream& is) { is.read((char *)&size, sizeof(int)); matchedions.clear(); for (int i = 0; i < size; i++) { - is.read((char *)&iontype, sizeof(fragmentIonType)); + is.read((char *)&iontype, sizeof(eFragmentIonType)); is.read((char *)&number, sizeof(int)); matchedions[iontype] = number; } @@ -2154,7 +2751,7 @@ void cTheoreticalSpectrum::load(ifstream& is) { is.read((char *)&peakstested, sizeof(int)); is.read((char *)&experimentalpeaksmatchedratio, sizeof(double)); - loadString(unmatchedpeaks, is); + unmatchedpeaks.load(is); loadString(coveragebyseries, is); is.read((char *)&valid, sizeof(bool)); diff --git a/CycloBranch/core/cTheoreticalSpectrum.h b/CycloBranch/core/cTheoreticalSpectrum.h index 3f69e6e..20ae222 100644 --- a/CycloBranch/core/cTheoreticalSpectrum.h +++ b/CycloBranch/core/cTheoreticalSpectrum.h @@ -14,7 +14,7 @@ #include #include "core/cParameters.h" -#include "core/cPeaksList.h" +#include "core/cPeakListSeries.h" #include "core/cCandidateSet.h" class cMainThread; @@ -34,11 +34,13 @@ struct splitSite { */ int first; + /** \brief Order of the second amino acid where a cyclic peptide is split. */ int second; + splitSite() { first = 0; second = 0; @@ -99,10 +101,10 @@ class cTheoreticalSpectrum { cCandidate candidate; int experimentalpeaksmatched; int scrambledpeaksmatched; - map matchedions; + map matchedions; int peakstested; double experimentalpeaksmatchedratio; - string unmatchedpeaks; + cPeaksList unmatchedpeaks; string coveragebyseries; bool valid; double intensityweightedscore; @@ -113,7 +115,7 @@ class cTheoreticalSpectrum { int seriescompleted; // remove false hits, i.e., b-H2O without existing b-ion - void clearFalseHits(map >& series, vector& fragmentions); + void clearFalseHits(map >& series, vector& fragmentions); // search for matches of experimental and theoretical peaks void searchForPeakPairs(cPeaksList& theoreticalpeaks, int theoreticalpeaksrealsize, cPeaksList& experimentalpeaks, vector >& experimentalpeakmatches, double fragmentmasserrortolerance); @@ -131,7 +133,23 @@ class cTheoreticalSpectrum { void selectAndNormalizeScrambledSequences(unordered_set& scrambledsequences); // select a proper fragment ion type for an experimental peak when masses of more theoretical fragment ions collide - fragmentIonType selectHigherPriorityIonTypeCID(fragmentIonType experimentalpeakiontype, fragmentIonType theoreticalpeakiontype); + eFragmentIonType selectHigherPriorityIonTypeCID(eFragmentIonType experimentalpeakiontype, eFragmentIonType theoreticalpeakiontype); + + // add a peak to the list + void addPeakToList(cPeak& peak, int& theoreticalpeaksrealsize); + + // add an adduct to a peak description + void addAdductToDescription(string& description); + + // count isotopes of metals + int countIsotopesOfMetals(cParameters* parameters); + + // add theoretical peaks containing metals + void addMetalPeaks(cPeak& peak, int& peaklistrealsize, int charge, bool writedescription); + + // remove unmatched biometal isotopes + void removeUnmatchedMetalIsotopes(cPeaksList& theoreticalpeaks, int theoreticalpeaksrealsize, cPeaksList& experimentalpeaks, vector >& experimentalpeakmatches); + public: @@ -180,7 +198,7 @@ class cTheoreticalSpectrum { /** \brief Compare the theoretical spectrum of a branched peptide with an experimental spectrum. - \param sortedpeaklist reference to a peak list of an experimental spectrum + \param sortedpeaklist reference to an experimental peaklist \param bricksdatabasewithcombinations reference to a database of bricks with combinations of bricks \param writedescription if true then string descriptions of peaks are filled \param sequencetag reference to a regex of a sequence tag @@ -192,7 +210,7 @@ class cTheoreticalSpectrum { /** \brief Compare the theoretical spectrum of a linear peptide with an experimental spectrum. - \param sortedpeaklist reference to a peak list of an experimental spectrum + \param sortedpeaklist reference to an experimental peaklist \param bricksdatabasewithcombinations reference to a database of bricks with combinations of bricks \param writedescription if true then string descriptions of peaks are filled \param sequencetag reference to a regex of a sequence tag @@ -204,7 +222,7 @@ class cTheoreticalSpectrum { /** \brief Compare the theoretical spectrum of a cyclic peptide with an experimental spectrum. - \param sortedpeaklist reference to a peak list of an experimental spectrum + \param sortedpeaklist reference to an experimental peaklist \param bricksdatabasewithcombinations reference to a database of bricks with combinations of bricks \param writedescription if true then string descriptions of peaks are filled \param sequencetag reference to a regex of a sequence tag @@ -216,19 +234,49 @@ class cTheoreticalSpectrum { /** \brief Compare the theoretical spectrum of a branch-cyclic peptide with an experimental spectrum. - \param sortedpeaklist reference to a peak list of an experimental spectrum + \param sortedpeaklist reference to an experimental peaklist \param bricksdatabasewithcombinations reference to a database of bricks with combinations of bricks \param writedescription if true then string descriptions of peaks are filled \param sequencetag reference to a regex of a sequence tag \param searchedsequence reference to a regex of a searched sequence \retval int number theoretical peaks generated; -2 when the sequence tag does not match the peptide sequence candidate */ - int compareLasso(cPeaksList& sortedpeaklist, cBricksDatabase& bricksdatabasewithcombinations, bool writedescription, regex& sequencetag, regex& searchedsequence); + int compareBranchCyclic(cPeaksList& sortedpeaklist, cBricksDatabase& bricksdatabasewithcombinations, bool writedescription, regex& sequencetag, regex& searchedsequence); + + +#if POLYKETIDE_SIDEROPHORES == 1 + + + /** + \brief Compare the theoretical spectrum of a linear polyketide siderophore with an experimental spectrum. + \param sortedpeaklist reference to an experimental peaklist + \param bricksdatabasewithcombinations reference to a database of bricks with combinations of bricks + \param writedescription if true then string descriptions of peaks are filled + \param sequencetag reference to a regex of a sequence tag + \param searchedsequence reference to a regex of a searched sequence + \retval int number theoretical peaks generated; -2 when the sequence tag does not match the peptide sequence candidate + */ + int compareLinearPolyketideSiderophore(cPeaksList& sortedpeaklist, cBricksDatabase& bricksdatabasewithcombinations, bool writedescription, regex& sequencetag, regex& searchedsequence); + + + /** + \brief Compare the theoretical spectrum of a cyclic polyketide siderophore with an experimental spectrum. + \param sortedpeaklist reference to an experimental peaklist + \param bricksdatabasewithcombinations reference to a database of bricks with combinations of bricks + \param writedescription if true then string descriptions of peaks are filled + \param sequencetag reference to a regex of a sequence tag + \param searchedsequence reference to a regex of a searched sequence + \retval int number theoretical peaks generated; -2 when the sequence tag does not match the peptide sequence candidate + */ + int compareCyclicPolyketideSiderophore(cPeaksList& sortedpeaklist, cBricksDatabase& bricksdatabasewithcombinations, bool writedescription, regex& sequencetag, regex& searchedsequence); + + +#endif /** \brief Compare the theoretical spectrum of a linear polysaccharide with an experimental spectrum. - \param sortedpeaklist reference to a peak list of an experimental spectrum + \param sortedpeaklist reference to an experimental peaklist \param bricksdatabasewithcombinations reference to a database of bricks with combinations of bricks \param writedescription if true then string descriptions of peaks are filled \param sequencetag reference to a regex of a sequence tag @@ -240,9 +288,15 @@ class cTheoreticalSpectrum { /** \brief Compare theoretical peaks with an experimental spectrum. - \param parameters a pointer to the parameters of the application */ - void compareMSSpectrum(cParameters* parameters); + void generateMSSpectrum(); + + + /** + \brief Compare theoretical peaks with an experimental spectrum. + \param peaklist reference to an experimental peaklist + */ + void compareMSSpectrum(cPeaksList& peaklist); /** @@ -264,7 +318,7 @@ class cTheoreticalSpectrum { \param iontype a fragment ion type \retval int number of matched peaks */ - int getNumberOfMatchedPeaks(fragmentIonType iontype) const; + int getNumberOfMatchedPeaks(eFragmentIonType iontype) const; /** @@ -281,14 +335,6 @@ class cTheoreticalSpectrum { double getRatioOfMatchedPeaks(); - /** - \brief Print a peptide-spectrum match into a stream. - \param os reference to an ouput stream - \param peptidetype the type of peptide corresponding to the experimental spectrum - */ - void printMatch(ofstream& os, peptideType peptidetype); - - /** \brief Generate a N-terminal fragment ion series. \param maxcharge a charge of precursor ion @@ -302,8 +348,10 @@ class cTheoreticalSpectrum { \param searchedmodifications reference to a vector of searched modifications \param peptidetype the type of searched peptide \param trotation a pointer to a T-permutation of a branched peptide + \param leftresiduelosstype a residue type of the leftmost building block + \param hasfirstblockartificial true when the first block is artificial, false otherwise */ - void generateNTerminalFragmentIons(int maxcharge, int& peaklistrealsize, vector& intcomposition, fragmentIonType fragmentiontype, cBricksDatabase& bricksdatabase, bool writedescription, int rotationid, vector& splittingsites, vector& searchedmodifications, peptideType peptidetype, TRotationInfo* trotation = 0); + void generateNTerminalFragmentIons(int maxcharge, int& peaklistrealsize, vector& intcomposition, eFragmentIonType fragmentiontype, cBricksDatabase& bricksdatabase, bool writedescription, int rotationid, vector& splittingsites, vector& searchedmodifications, ePeptideType peptidetype, TRotationInfo* trotation = 0, eResidueLossType leftresiduelosstype = water, bool hasfirstblockartificial = false); /** @@ -319,8 +367,10 @@ class cTheoreticalSpectrum { \param searchedmodifications reference to a vector of searched modifications \param peptidetype the type of searched peptide \param trotation a pointer to a T-permutation of a branched peptide + \param rightresiduelosstype a residue type of the rightmost building block + \param haslastblockartificial true when the last block is artificial, false otherwise */ - void generateCTerminalFragmentIons(int maxcharge, int& peaklistrealsize, vector& intcomposition, fragmentIonType fragmentiontype, cBricksDatabase& bricksdatabase, bool writedescription, int rotationid, vector& splittingsites, vector& searchedmodifications, peptideType peptidetype, TRotationInfo* trotation = 0); + void generateCTerminalFragmentIons(int maxcharge, int& peaklistrealsize, vector& intcomposition, eFragmentIonType fragmentiontype, cBricksDatabase& bricksdatabase, bool writedescription, int rotationid, vector& splittingsites, vector& searchedmodifications, ePeptideType peptidetype, TRotationInfo* trotation = 0, eResidueLossType rightresiduelosstype = water, bool haslastblockartificial = false); /** @@ -375,15 +425,15 @@ class cTheoreticalSpectrum { /** - \brief Get list of unmatched peaks. - \retval string list of unmatched peaks + \brief Get a list of unmatched peaks. + \retval cPeaksList a pointer to a list of unmatched peaks */ - string getUnmatchedPeaks(); + cPeaksList* getUnmatchedPeaks(); /** - \brief Access to the list of theoretical peaks. - \retval cPeaksList pointer to a list of theoretical peaks + \brief Get a list of theoretical peaks. + \retval cPeaksList a pointer to a list of theoretical peaks */ cPeaksList* getTheoreticalPeaks(); diff --git a/CycloBranch/core/cTheoreticalSpectrumList.cpp b/CycloBranch/core/cTheoreticalSpectrumList.cpp index 1addb34..e6dccaf 100644 --- a/CycloBranch/core/cTheoreticalSpectrumList.cpp +++ b/CycloBranch/core/cTheoreticalSpectrumList.cpp @@ -89,7 +89,7 @@ cTheoreticalSpectrum& cTheoreticalSpectrumList::operator[](int position) { int cTheoreticalSpectrumList::parallelCompareAndStore(cCandidateSet& candidates, bool& terminatecomputation) { - cPeaksList peaklist = parameters->peaklist; + cPeaksList peaklist = parameters->peaklistseries[0]; peaklist.sortbyMass(); cCandidateSet permutations; theoreticalspectra.clear(); @@ -253,9 +253,17 @@ int cTheoreticalSpectrumList::parallelCompareAndStore(cCandidateSet& candidates, case branched: theoreticalpeaksrealsize = tsp.compareBranched(peaklist, *bricksdb, true, rxsequencetag, rxsearchedsequence); break; - case lasso: - theoreticalpeaksrealsize = tsp.compareLasso(peaklist, *bricksdb, true, rxsequencetag, rxsearchedsequence); + case branchcyclic: + theoreticalpeaksrealsize = tsp.compareBranchCyclic(peaklist, *bricksdb, true, rxsequencetag, rxsearchedsequence); break; +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + theoreticalpeaksrealsize = tsp.compareLinearPolyketideSiderophore(peaklist, *bricksdb, true, rxsequencetag, rxsearchedsequence); + break; + case cyclicpolyketide: + theoreticalpeaksrealsize = tsp.compareCyclicPolyketideSiderophore(peaklist, *bricksdb, true, rxsequencetag, rxsearchedsequence); + break; +#endif case linearpolysaccharide: theoreticalpeaksrealsize = tsp.compareLinearPolysaccharide(peaklist, *bricksdb, true, rxsequencetag, rxsearchedsequence); break; @@ -282,7 +290,7 @@ int cTheoreticalSpectrumList::parallelCompareAndStore(cCandidateSet& candidates, theoreticalspectra[i].getCandidate().setRealPeptideName(*bricksdb, parameters->peptidetype); theoreticalspectra[i].getCandidate().setAcronymPeptideNameWithHTMLReferences(*bricksdb, parameters->peptidetype); theoreticalspectra[i].getCandidate().setAcronyms(*bricksdb); - if ((parameters->peptidetype == branched) || (parameters->peptidetype == lasso)) { + if ((parameters->peptidetype == branched) || (parameters->peptidetype == branchcyclic)) { theoreticalspectra[i].getCandidate().setBackboneAcronyms(*bricksdb); theoreticalspectra[i].getCandidate().setBranchAcronyms(*bricksdb); } @@ -298,16 +306,6 @@ int cTheoreticalSpectrumList::parallelCompareAndStore(cCandidateSet& candidates, } -void cTheoreticalSpectrumList::printPeptideSpectrumMatches(ofstream& os, int limit, peptideType peptidetype) { - int count = min(limit, (int)theoreticalspectra.size()); - for (int i = 0; i < count; i++) { - os << "--------------------------------------------------------------------------------" << endl; - os << "Peptide-Spectrum Match no. " << i+1 << endl; - theoreticalspectra[i].printMatch(os, peptidetype); - } -} - - void cTheoreticalSpectrumList::addButDoNotFitSize(cTheoreticalSpectrum& theoreticalspectrum, int theoreticalpeaksrealsize) { QMutexLocker ml(&mutex); diff --git a/CycloBranch/core/cTheoreticalSpectrumList.h b/CycloBranch/core/cTheoreticalSpectrumList.h index f0129ab..8d2cba5 100644 --- a/CycloBranch/core/cTheoreticalSpectrumList.h +++ b/CycloBranch/core/cTheoreticalSpectrumList.h @@ -92,14 +92,6 @@ class cTheoreticalSpectrumList { int parallelCompareAndStore(cCandidateSet& candidates, bool& terminatecomputation); - /** - \brief Print peptide spectrum matches into a stream. - \param os reference to the main thread of the application (output stream) - \param limit a number of peptide spectrum matches printed - \param peptidetype the type of peptide corresponding to the experimental spectrum - */ - void printPeptideSpectrumMatches(ofstream& os, int limit, peptideType peptidetype); - /** \brief Add a new spectrum to the list when the number cParameters::hitsreported has not been exceeded or when it has been exceeded but the score of the newly added spectrum is better than the worst score of a peptide in the list (thread-safe). diff --git a/CycloBranch/core/utilities.cpp b/CycloBranch/core/utilities.cpp index 73d9619..2b464ca 100644 --- a/CycloBranch/core/utilities.cpp +++ b/CycloBranch/core/utilities.cpp @@ -4,7 +4,8 @@ QString appname = "CycloBranch"; -QString appversion = "v. 1.0.1216 (64-bit)"; +QString appversion = "v. 1.0.1512 (64-bit)"; + #if OS_TYPE == UNX QString installdir = "/usr/share/cyclobranch/"; @@ -60,7 +61,7 @@ string& removeWhiteSpacesExceptSpaces(string& s) { } -bool checkRegex(peptideType peptidetype, string& sequence, string& errormessage) { +bool checkRegex(ePeptideType peptidetype, string& sequence, string& errormessage) { errormessage = ""; if (sequence.compare("") == 0) { @@ -74,15 +75,21 @@ bool checkRegex(peptideType peptidetype, string& sequence, string& errormessage) { case linear: case linearpolysaccharide: +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: +#endif rx = "^\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])*$"; break; case cyclic: +#if POLYKETIDE_SIDEROPHORES == 1 + case cyclicpolyketide: +#endif rx = "^\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])+$"; break; case branched: rx = "^\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])*\\\\\\(\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])+\\\\\\)\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])*$"; break; - case lasso: + case branchcyclic: rx = "(^(\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])*)?\\\\\\(\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])+\\\\\\)\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])*$|^\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])*\\\\\\(\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])+\\\\\\)(\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])*)?$)"; break; case other: @@ -106,13 +113,13 @@ bool checkRegex(peptideType peptidetype, string& sequence, string& errormessage) } -void parseBranch(peptideType peptidetype, string& composition, vector& vectorcomposition, int& branchstart, int& branchend) { +void parseBranch(ePeptideType peptidetype, string& composition, vector& vectorcomposition, int& branchstart, int& branchend) { string s = composition; cBrick b; branchstart = -1; branchend = -1; - if ((peptidetype == branched) || (peptidetype == lasso)) { + if ((peptidetype == branched) || (peptidetype == branchcyclic)) { int i = 0; while (i < (int)s.size()) { if (s[i] == '\\') { @@ -166,7 +173,7 @@ void parseBranch(peptideType peptidetype, string& composition, vector& v } -peptideType getPeptideTypeFromString(string s) { +ePeptideType getPeptideTypeFromString(string& s) { if (s.compare("linear") == 0) { return linear; } @@ -177,9 +184,17 @@ peptideType getPeptideTypeFromString(string s) { return branched; } if (s.compare("branch-cyclic") == 0) { - return lasso; + return branchcyclic; } - if (s.compare("linearpolysaccharide") == 0) { +#if POLYKETIDE_SIDEROPHORES == 1 + if (s.compare("linear-oligoketide-siderophore") == 0) { + return linearpolyketide; + } + if (s.compare("cyclic-oligoketide-siderophore") == 0) { + return cyclicpolyketide; + } +#endif + if (s.compare("linear-polysaccharide") == 0) { return linearpolysaccharide; } if (s.compare("other") == 0) { @@ -190,7 +205,7 @@ peptideType getPeptideTypeFromString(string s) { } -string getStringFromPeptideType(peptideType peptidetype) { +string getStringFromPeptideType(ePeptideType peptidetype) { switch (peptidetype) { case linear: @@ -202,11 +217,19 @@ string getStringFromPeptideType(peptideType peptidetype) { case branched: return "branched"; break; - case lasso: + case branchcyclic: return "branch-cyclic"; break; +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + return "linear-oligoketide-siderophore"; + break; + case cyclicpolyketide: + return "cyclic-oligoketide-siderophore"; + break; +#endif case linearpolysaccharide: - return "linearpolysaccharide"; + return "linear-polysaccharide"; break; case other: return "other"; @@ -218,3 +241,12 @@ string getStringFromPeptideType(peptideType peptidetype) { return "other"; } + +double cropPrecisionToSixDecimals(double value) { + char buffer[50]; + double val; + sprintf_s(buffer, "%.6f\0", value); + sscanf_s(buffer, "%lf", &val); + return val; +} + diff --git a/CycloBranch/core/utilities.h b/CycloBranch/core/utilities.h index daf0cdc..986e584 100644 --- a/CycloBranch/core/utilities.h +++ b/CycloBranch/core/utilities.h @@ -36,6 +36,9 @@ class cBrick; #endif +#define POLYKETIDE_SIDEROPHORES 1 + + using namespace std; using namespace boost; @@ -43,13 +46,20 @@ using namespace boost; /** \brief The types of peptides supported by the application. */ -enum peptideType { +enum ePeptideType { linear = 0, cyclic = 1, branched = 2, - lasso = 3, + branchcyclic = 3, +#if POLYKETIDE_SIDEROPHORES == 1 + linearpolyketide = 4, + cyclicpolyketide = 5, + linearpolysaccharide = 6, + other = 7 +#else linearpolysaccharide = 4, other = 5 +#endif }; @@ -126,7 +136,7 @@ string& removeWhiteSpacesExceptSpaces(string& s); \param errormessage an error message if false is returned \retval bool true when the syntax is correct, false otherwise */ -bool checkRegex(peptideType peptidetype, string& sequence, string& errormessage); +bool checkRegex(ePeptideType peptidetype, string& sequence, string& errormessage); /** @@ -137,15 +147,15 @@ bool checkRegex(peptideType peptidetype, string& sequence, string& errormessage) \param branchstart start position of a branch (an output value) \param branchend end position of a branch (an output value) */ -void parseBranch(peptideType peptidetype, string& composition, vector& vectorcomposition, int& branchstart, int& branchend); +void parseBranch(ePeptideType peptidetype, string& composition, vector& vectorcomposition, int& branchstart, int& branchend); /** \brief Convert a string to peptide type. \param s string - \retval peptideType type of peptide + \retval ePeptideType type of peptide */ -peptideType getPeptideTypeFromString(string s); +ePeptideType getPeptideTypeFromString(string& s); /** @@ -153,7 +163,15 @@ peptideType getPeptideTypeFromString(string s); \param peptidetype type of peptide \retval string string */ -string getStringFromPeptideType(peptideType peptidetype); +string getStringFromPeptideType(ePeptideType peptidetype); + + +/** + \brief Crop a precision of a double to six decimal places. + \param value an input value + \retval double a value with the limited precision +*/ +double cropPrecisionToSixDecimals(double value); #endif diff --git a/CycloBranch/gui/cAboutWidget.cpp b/CycloBranch/gui/cAboutWidget.cpp index 3f354a6..8a0226f 100644 --- a/CycloBranch/gui/cAboutWidget.cpp +++ b/CycloBranch/gui/cAboutWidget.cpp @@ -16,13 +16,19 @@ cAboutWidget::cAboutWidget(QWidget* parent) { layout = new QVBoxLayout(); - QString title = "A tool for de novo sequencing of nonribosomal peptides from accurate product ion mass spectra.


"; + QString homepage = "Homepage: http://ms.biomed.cas.cz/cyclobranch/

"; + + QString citation = "If you use CycloBranch in your work, please, cite us using the following publication:

"; + citation += "Jiri Novak, Karel Lemr, Kevin A. Schug and Vladimir Havlicek.
"; + citation += "CycloBranch: De Novo Sequencing of Nonribosomal Peptides from Accurate Product Ion Mass Spectra.
"; + citation += "J. Am. Soc. Mass Spectrom., vol. 26, no. 10, pp. 1780-1786, 2015. DOI: 10.1007/s13361-015-1211-1.
"; + citation += "Download citation: [ ris ], [ bib ]



"; QString licence = "Licence:

This program 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.

"; licence += "This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

"; licence += "You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.


"; - QString developers = "Developers:

Jiri Novak
Laboratory of Molecular Structure Characterization
Institute of Microbiology
Academy of Sciences of the Czech Republic
Videnska 1083
142 20 Prague
Czech Republic
jiri.novak@biomed.cas.cz
http://ms.biomed.cas.cz/cyclobranch/
http://ms.biomed.cas.cz/staff-novak_jiri.php
https://cas-cz.academia.edu/JiriNovak

(C) 2013 - 2015


"; + QString developers = "Developers:

Jiri Novak
Laboratory of Molecular Structure Characterization
Institute of Microbiology
Academy of Sciences of the Czech Republic
Videnska 1083
142 20 Prague
Czech Republic
jiri.novak@biomed.cas.cz
http://ms.biomed.cas.cz/staff-novak_jiri.php
https://cas-cz.academia.edu/JiriNovak

(C) 2013 - 2015


"; QString splash = "



"; @@ -32,7 +38,7 @@ cAboutWidget::cAboutWidget(QWidget* parent) { message->setReadOnly(true); message->setAcceptRichText(true); message->setOpenExternalLinks(true); - message->setHtml("
" + splash + "" + appname + " " + appversion + "

" + title + developers + licence + acknowledgement); + message->setHtml("
" + splash + "" + appname + " " + appversion + "

" + homepage + citation + developers + licence + acknowledgement); buttonbox = new QDialogButtonBox(QDialogButtonBox::Ok); //buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); diff --git a/CycloBranch/gui/cBranchCyclicWidget.cpp b/CycloBranch/gui/cBranchCyclicWidget.cpp new file mode 100644 index 0000000..3214dc6 --- /dev/null +++ b/CycloBranch/gui/cBranchCyclicWidget.cpp @@ -0,0 +1,362 @@ +#include "gui/cBranchCyclicWidget.h" + +#include +#include +#include +#include +#include +#include +#include + + +void generateBranchLabelsDown(bool nterminal, int rotationid, unordered_set& labels, cParameters* parameters, cTheoreticalSpectrum* theoreticalspectrum, int centerx, int topmargin, int horizontalstep, int verticalstep, int visiblerotationid, int branchstart) { + string name; + int j; + if ((visiblerotationid == -1) || (visiblerotationid == rotationid/6)) { + for (int i = 0; i < (int)parameters->fragmentionsfortheoreticalspectra.size(); i++) { + if ((nterminal && parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].nterminal) || (!nterminal && parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].cterminal)) { + j = 0; + while (j < branchstart) { + if (theoreticalspectrum->getVisualCoverage()[rotationid*parameters->fragmentionsfortheoreticalspectra.size() + i].series[j] > 0) { + name = theoreticalspectrum->getVisualCoverage()[rotationid*parameters->fragmentionsfortheoreticalspectra.size() + i].name.substr(0, theoreticalspectrum->getVisualCoverage()[rotationid*parameters->fragmentionsfortheoreticalspectra.size() + i].name.rfind('_') + 1); + name += parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name[0] + to_string(j + 1) + parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name.substr(1); + insertLabel(labels, centerx + 25, topmargin + verticalstep*j + verticalstep/2 - verticalstep/8, name, false); + } + j++; + } + } + } + } +} + + +void generateBranchLabelsUp(bool nterminal, int rotationid, unordered_set& labels, cParameters* parameters, cTheoreticalSpectrum* theoreticalspectrum, int centerx, int topmargin, int horizontalstep, int verticalstep, int visiblerotationid, int branchend) { + string name; + int j; + int count; + if ((visiblerotationid == -1) || (visiblerotationid == rotationid/6)) { + for (int i = 0; i < (int)parameters->fragmentionsfortheoreticalspectra.size(); i++) { + if ((nterminal && parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].nterminal) || (!nterminal && parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].cterminal)) { + j = 0; + count = (int)theoreticalspectrum->getVisualCoverage()[rotationid*parameters->fragmentionsfortheoreticalspectra.size() + i].series.size() - branchend; + while (j < count) { + if (theoreticalspectrum->getVisualCoverage()[rotationid*parameters->fragmentionsfortheoreticalspectra.size() + i].series[j + branchend] > 0) { + name = theoreticalspectrum->getVisualCoverage()[rotationid*parameters->fragmentionsfortheoreticalspectra.size() + i].name.substr(0, theoreticalspectrum->getVisualCoverage()[rotationid*parameters->fragmentionsfortheoreticalspectra.size() + i].name.rfind('_') + 1); + name += parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name[0] + to_string(j + branchend + 1) + parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name.substr(1); + insertLabel(labels, centerx - 25, topmargin + verticalstep*(count - j - 1) + verticalstep/2, name, true); + } + j++; + } + } + } + } +} + + +cBranchCyclicWidget::cBranchCyclicWidget() { + parameters = 0; + theoreticalspectrum = 0; + visiblerotationid = -1; + visibletrotationid = -1; +} + + +void cBranchCyclicWidget::initialize(cParameters* parameters, cTheoreticalSpectrum* theoreticalspectrum) { + this->parameters = parameters; + this->theoreticalspectrum = theoreticalspectrum; +} + + +void cBranchCyclicWidget::exportToPDF(QString filename, bool postscript) { + QPrinter printer; + if (postscript) { + printer.setPaperSize(QSizeF(width() + 100, height() + 100), QPrinter::DevicePixel); + printer.setPageMargins (50, 50, 50, 50, QPrinter::DevicePixel); + } + else { + printer.setPaperSize(QSizeF(width(), height()), QPrinter::DevicePixel); + printer.setPageMargins (0, 0, 0, 0, QPrinter::DevicePixel); + } + printer.setOutputFormat(QPrinter::NativeFormat); + printer.setOutputFileName(filename); + + QPainter painter; + if (!painter.begin(&printer)) { + QMessageBox msgBox; + QString errstr = "The file cannot be created."; + #if OS_TYPE == UNX + errstr += "\nDo you have a properly configured print server (sudo apt-get install cups-pdf) ?"; + #endif + msgBox.setText(errstr); + msgBox.exec(); + return; + } + + painter.setRenderHints(QPainter::Antialiasing|QPainter::TextAntialiasing|QPainter::SmoothPixmapTransform); + paint(painter); + painter.end(); +} + + +void cBranchCyclicWidget::exportToPNG(QString filename) { + QImage image(width(), height(), QImage::Format_ARGB32); + image.fill(Qt::white); + + QPainter painter; + if (!painter.begin(&image)) { + QMessageBox msgBox; + QString errstr = "The file cannot be created."; + msgBox.setText(errstr); + msgBox.exec(); + return; + } + + painter.setRenderHints(QPainter::Antialiasing|QPainter::TextAntialiasing|QPainter::SmoothPixmapTransform); + paint(painter); + painter.end(); + image.save(filename); +} + + +void cBranchCyclicWidget::exportToSVG(QString filename) { + QSvgGenerator generator; + generator.setFileName(filename); + generator.setSize(QSize(width(), height())); + generator.setViewBox(QRect(0, 0, width(), height())); + + QPainter painter; + if (!painter.begin(&generator)) { + QMessageBox msgBox; + QString errstr = "The file cannot be created."; + msgBox.setText(errstr); + msgBox.exec(); + return; + } + + painter.fillRect(QRect(0, 0, width(), height()), Qt::white); + paint(painter); + painter.end(); +} + + +void cBranchCyclicWidget::paintEvent(QPaintEvent *event) { + QPainter painter; + painter.begin(this); + paint(painter); + painter.end(); +} + + +void cBranchCyclicWidget::paint(QPainter& painter) { + + if (!theoreticalspectrum) { + return; + } + + vector backboneacronyms; + vector branchacronyms; + backboneacronyms = theoreticalspectrum->getCandidate().getBackboneAcronyms(); + branchacronyms = theoreticalspectrum->getCandidate().getBranchAcronyms(); + + const int topmargin = 20; + const int leftmargin = 20; + const int bottommargin = 80; + const int rightmargin = 20; + + int backbonesize = (int)backboneacronyms.size(); + int branchsize = (int)branchacronyms.size(); + + if (branchsize < 1) { + return; + } + + double angle = 2*pi/(double)backbonesize; + int centerx = width()/2; + int centery = height()*3/4; + int radius = (height()/2 - bottommargin)/2; + + const int horizontalstep = (width() - leftmargin - rightmargin)/std::max(backbonesize, 1); + const int verticalstep = (centery - radius - topmargin - 10)/std::max(branchsize, 1); + + QFont myFont("Courier", 9); + QFontMetrics fm(myFont); + painter.setFont(myFont); + + unordered_set labels; + labels.clear(); + + int linesize = 20; + int cornerlinesize = horizontalstep/8; + + paintCircle(painter, backboneacronyms, centerx, centery, radius, angle, horizontalstep, linesize, cornerlinesize, theoreticalspectrum->getVisualCoverage().size() > 0, visiblerotationid, labels); + + + // paint the branch + for (int i = 0; i < branchsize; i++) { + painter.setPen(QPen(Qt::blue, 2, Qt::SolidLine)); + if (fm.width(branchacronyms[branchsize - i - 1].c_str()) > horizontalstep*2/3) { + painter.drawText(centerx - horizontalstep/3 + 2, topmargin + verticalstep*i, horizontalstep*2/3 - 2, 20, Qt::AlignLeft|Qt::AlignVCenter, branchacronyms[branchsize - i - 1].c_str()); + } + else { + painter.drawText(centerx - horizontalstep/3, topmargin + verticalstep*i, horizontalstep*2/3, 20, Qt::AlignCenter, branchacronyms[branchsize - i - 1].c_str()); + } + + if (theoreticalspectrum->getVisualCoverage().size() > 0) { + painter.drawText(centerx - horizontalstep/3, topmargin + verticalstep*i - 20, horizontalstep*2/3, 20, Qt::AlignLeft, to_string(backbonesize + branchsize - i).c_str()); + } + + painter.setPen(QPen(Qt::black, 2, Qt::SolidLine)); + painter.drawRect(centerx - horizontalstep/3, topmargin + verticalstep*i, horizontalstep*2/3, 20); + + painter.setPen(QPen(Qt::black, 2, Qt::SolidLine)); + painter.drawLine(centerx, topmargin + verticalstep*i + 20, centerx, topmargin + verticalstep*i + verticalstep); + + if (theoreticalspectrum->getVisualCoverage().size() > 0) { + painter.setPen(QPen(Qt::black, 2, Qt::DashLine)); + painter.drawLine(centerx - 20, topmargin + verticalstep*i + (verticalstep + 10)/2, centerx + 20, topmargin + verticalstep*i + (verticalstep + 10)/2); + painter.drawLine(centerx - 20, topmargin + verticalstep*i + (verticalstep + 10)/2, centerx - 20, topmargin + verticalstep*i + (verticalstep + 10)/2 + verticalstep/8); + painter.drawLine(centerx + 20, topmargin + verticalstep*i + (verticalstep + 10)/2 - verticalstep/8, centerx + 20, topmargin + verticalstep*i + (verticalstep + 10)/2); + } + + if (i == branchsize - 1) { + painter.setPen(QPen(Qt::black, 2, Qt::SolidLine)); + painter.drawLine(centerx, topmargin + verticalstep*i + verticalstep, centerx, centery - radius - 10); + } + } + + + if (parameters && (theoreticalspectrum->getVisualCoverage().size() > 0)) { + + // get branch-cyclic rotations + vector branchcyclicrotations; + theoreticalspectrum->getCandidate().getBranchCyclicRotations(branchcyclicrotations, true); + + // get T-permutations of branch-cyclic rotations + vector > trotationsofbranchcyclicrotations; + trotationsofbranchcyclicrotations.resize(branchcyclicrotations.size()); + for (int i = 0; i < (int)branchcyclicrotations.size(); i++) { + branchcyclicrotations[i].getPermutationsOfBranches(trotationsofbranchcyclicrotations[i]); + } + + int rotationid; + int half = (int)theoreticalspectrum->getVisualCoverage().size()/(int)parameters->fragmentionsfortheoreticalspectra.size()/2; + for (int i = 0; i < half; i++) { + + if ((visibletrotationid != -1) && (visibletrotationid != i % 6)) { + continue; + } + + if (i/6 == 0) { + rotationid = half/6; + } + else { + rotationid = 2*half/6 - i/6; + } + + switch (i % 6) + { + case 0: + generateCyclicLabelsToRight(true, i, i/6, 0, backbonesize + branchsize - 1, backbonesize, labels, parameters, theoreticalspectrum, centerx, centery, radius, angle, linesize, cornerlinesize, visiblerotationid, trotationsofbranchcyclicrotations[i/6][0].middlebranchstart, trotationsofbranchcyclicrotations[i/6][0].middlebranchend); + break; + case 1: + generateBranchLabelsDown(true, i, labels, parameters, theoreticalspectrum, centerx, topmargin, horizontalstep, verticalstep, visiblerotationid, trotationsofbranchcyclicrotations[i/6][1].middlebranchstart); + generateCyclicLabelsToRight(true, i, -1, trotationsofbranchcyclicrotations[i/6][1].middlebranchend, backbonesize + branchsize - 1, backbonesize, labels, parameters, theoreticalspectrum, centerx, centery, radius, angle, linesize, cornerlinesize, visiblerotationid, trotationsofbranchcyclicrotations[i/6][1].middlebranchstart, trotationsofbranchcyclicrotations[i/6][1].middlebranchend); + break; + case 2: + generateCyclicLabelsToRight(true, rotationid*6 + 2, i/6, 0, backbonesize + branchsize - 1, backbonesize, labels, parameters, theoreticalspectrum, centerx, centery, radius, angle, linesize, cornerlinesize, visiblerotationid, trotationsofbranchcyclicrotations[rotationid][2].middlebranchstart, trotationsofbranchcyclicrotations[rotationid][2].middlebranchend); + break; + case 3: + generateCyclicLabelsToRight(true, i, i/6, 0, trotationsofbranchcyclicrotations[i/6][3].middlebranchstart, backbonesize, labels, parameters, theoreticalspectrum, centerx, centery, radius, angle, linesize, cornerlinesize, visiblerotationid, trotationsofbranchcyclicrotations[i/6][3].middlebranchstart, trotationsofbranchcyclicrotations[i/6][3].middlebranchend); + generateBranchLabelsUp(true, i, labels, parameters, theoreticalspectrum, centerx, topmargin, horizontalstep, verticalstep, visiblerotationid, trotationsofbranchcyclicrotations[i/6][3].middlebranchend); + generateBranchLabelsDown(false, i, labels, parameters, theoreticalspectrum, centerx, topmargin, horizontalstep, verticalstep, visiblerotationid, trotationsofbranchcyclicrotations[i/6][4].middlebranchstart); + generateCyclicLabelsToLeft(false, i, 0, trotationsofbranchcyclicrotations[i/6][4].middlebranchend, backbonesize + branchsize - 1, backbonesize, labels, parameters, theoreticalspectrum, centerx, centery, radius, angle, linesize, cornerlinesize, visiblerotationid, trotationsofbranchcyclicrotations[i/6][4].middlebranchstart, trotationsofbranchcyclicrotations[i/6][4].middlebranchend); + break; + case 4: + generateBranchLabelsDown(true, i, labels, parameters, theoreticalspectrum, centerx, topmargin, horizontalstep, verticalstep, visiblerotationid, trotationsofbranchcyclicrotations[i/6][4].middlebranchstart); + generateCyclicLabelsToLeft(true, i, 0, trotationsofbranchcyclicrotations[i/6][4].middlebranchend, backbonesize + branchsize - 1, backbonesize, labels, parameters, theoreticalspectrum, centerx, centery, radius, angle, linesize, cornerlinesize, visiblerotationid, trotationsofbranchcyclicrotations[i/6][4].middlebranchstart, trotationsofbranchcyclicrotations[i/6][4].middlebranchend); + break; + case 5: + generateCyclicLabelsToRight(true, rotationid*6 + 5, i/6, 0, trotationsofbranchcyclicrotations[rotationid][5].middlebranchstart, backbonesize, labels, parameters, theoreticalspectrum, centerx, centery, radius, angle, linesize, cornerlinesize, visiblerotationid, trotationsofbranchcyclicrotations[rotationid][5].middlebranchstart, trotationsofbranchcyclicrotations[rotationid][5].middlebranchend); + generateBranchLabelsUp(true, i, labels, parameters, theoreticalspectrum, centerx, topmargin, horizontalstep, verticalstep, visiblerotationid, trotationsofbranchcyclicrotations[i/6][5].middlebranchend); + generateBranchLabelsDown(false, i, labels, parameters, theoreticalspectrum, centerx, topmargin, horizontalstep, verticalstep, visiblerotationid, trotationsofbranchcyclicrotations[i/6][1].middlebranchstart); + generateCyclicLabelsToRight(false, i, -1, trotationsofbranchcyclicrotations[i/6][1].middlebranchend, backbonesize + branchsize - 1, backbonesize, labels, parameters, theoreticalspectrum, centerx, centery, radius, angle, linesize, cornerlinesize, visiblerotationid, trotationsofbranchcyclicrotations[i/6][1].middlebranchstart, trotationsofbranchcyclicrotations[i/6][1].middlebranchend); + break; + default: + break; + } + + } + + for (int i = half; i < 2*half; i++) { + + if ((visibletrotationid != -1) && (visibletrotationid != i % 6)) { + continue; + } + + if (i/6 == half/6) { + rotationid = 0; + } + else { + rotationid = 2*half/6 - i/6; + } + + switch (i % 6) + { + case 0: + generateCyclicLabelsToLeft(true, i, i/6, 0, backbonesize + branchsize - 1, backbonesize, labels, parameters, theoreticalspectrum, centerx, centery, radius, angle, linesize, cornerlinesize, visiblerotationid, trotationsofbranchcyclicrotations[i/6][0].middlebranchstart, trotationsofbranchcyclicrotations[i/6][0].middlebranchend); + break; + case 1: + generateBranchLabelsDown(true, i, labels, parameters, theoreticalspectrum, centerx, topmargin, horizontalstep, verticalstep, visiblerotationid, trotationsofbranchcyclicrotations[i/6][1].middlebranchstart); + generateCyclicLabelsToLeft(true, i, 0, trotationsofbranchcyclicrotations[i/6][1].middlebranchend, backbonesize + branchsize - 1, backbonesize, labels, parameters, theoreticalspectrum, centerx, centery, radius, angle, linesize, cornerlinesize, visiblerotationid, trotationsofbranchcyclicrotations[i/6][1].middlebranchstart, trotationsofbranchcyclicrotations[i/6][1].middlebranchend); + break; + case 2: + generateCyclicLabelsToLeft(true, rotationid*6 + 2, i/6, 0, backbonesize + branchsize - 1, backbonesize, labels, parameters, theoreticalspectrum, centerx, centery, radius, angle, linesize, cornerlinesize, visiblerotationid, trotationsofbranchcyclicrotations[rotationid][2].middlebranchstart, trotationsofbranchcyclicrotations[rotationid][2].middlebranchend); + break; + case 3: + generateCyclicLabelsToLeft(true, i, i/6, 0, trotationsofbranchcyclicrotations[i/6][3].middlebranchstart, backbonesize, labels, parameters, theoreticalspectrum, centerx, centery, radius, angle, linesize, cornerlinesize, visiblerotationid, trotationsofbranchcyclicrotations[i/6][3].middlebranchstart, trotationsofbranchcyclicrotations[i/6][3].middlebranchend); + generateBranchLabelsUp(true, i, labels, parameters, theoreticalspectrum, centerx, topmargin, horizontalstep, verticalstep, visiblerotationid, trotationsofbranchcyclicrotations[i/6][3].middlebranchend); + generateBranchLabelsDown(false, i, labels, parameters, theoreticalspectrum, centerx, topmargin, horizontalstep, verticalstep, visiblerotationid, trotationsofbranchcyclicrotations[i/6][4].middlebranchstart); + generateCyclicLabelsToRight(false, i, -1, trotationsofbranchcyclicrotations[i/6][4].middlebranchend, backbonesize + branchsize - 1, backbonesize, labels, parameters, theoreticalspectrum, centerx, centery, radius, angle, linesize, cornerlinesize, visiblerotationid, trotationsofbranchcyclicrotations[i/6][4].middlebranchstart, trotationsofbranchcyclicrotations[i/6][4].middlebranchend); + break; + case 4: + generateBranchLabelsDown(true, i, labels, parameters, theoreticalspectrum, centerx, topmargin, horizontalstep, verticalstep, visiblerotationid, trotationsofbranchcyclicrotations[i/6][4].middlebranchstart); + generateCyclicLabelsToRight(true, i, -1, trotationsofbranchcyclicrotations[i/6][4].middlebranchend, backbonesize + branchsize - 1, backbonesize, labels, parameters, theoreticalspectrum, centerx, centery, radius, angle, linesize, cornerlinesize, visiblerotationid, trotationsofbranchcyclicrotations[i/6][4].middlebranchstart, trotationsofbranchcyclicrotations[i/6][4].middlebranchend); + break; + case 5: + generateCyclicLabelsToLeft(true, rotationid*6 + 5, i/6, 0, trotationsofbranchcyclicrotations[rotationid][5].middlebranchstart, backbonesize, labels, parameters, theoreticalspectrum, centerx, centery, radius, angle, linesize, cornerlinesize, visiblerotationid, trotationsofbranchcyclicrotations[rotationid][5].middlebranchstart, trotationsofbranchcyclicrotations[rotationid][5].middlebranchend); + generateBranchLabelsUp(true, i, labels, parameters, theoreticalspectrum, centerx, topmargin, horizontalstep, verticalstep, visiblerotationid, trotationsofbranchcyclicrotations[i/6][5].middlebranchend); + generateBranchLabelsDown(false, i, labels, parameters, theoreticalspectrum, centerx, topmargin, horizontalstep, verticalstep, visiblerotationid, trotationsofbranchcyclicrotations[i/6][1].middlebranchstart); + generateCyclicLabelsToLeft(false, i, 0, trotationsofbranchcyclicrotations[i/6][1].middlebranchend, backbonesize + branchsize - 1, backbonesize, labels, parameters, theoreticalspectrum, centerx, centery, radius, angle, linesize, cornerlinesize, visiblerotationid, trotationsofbranchcyclicrotations[i/6][1].middlebranchstart, trotationsofbranchcyclicrotations[i/6][1].middlebranchend); + break; + default: + break; + } + + } + + painter.setPen(QPen(Qt::red, 2, Qt::SolidLine)); + for (auto it = labels.begin(); it != labels.end(); ++it) { + if (it->alignright) { + painter.drawText(it->x - fm.width(it->label.c_str()), it->y, fm.width(it->label.c_str()), 20, Qt::AlignLeft, it->label.c_str()); + } + else { + painter.drawText(it->x, it->y, width(), 20, Qt::AlignLeft, it->label.c_str()); + } + } + + } + +} + + +void cBranchCyclicWidget::rotationChanged(int index) { + visiblerotationid = index - 1; + repaint(); +} + + +void cBranchCyclicWidget::trotationChanged(int index) { + visibletrotationid = index - 1; + repaint(); +} + diff --git a/CycloBranch/gui/cBranchCyclicWidget.h b/CycloBranch/gui/cBranchCyclicWidget.h new file mode 100644 index 0000000..e5d6e16 --- /dev/null +++ b/CycloBranch/gui/cBranchCyclicWidget.h @@ -0,0 +1,131 @@ +/** + \file cBranchCyclicWidget.h + \brief Visualization of a branch-cyclic peptide. +*/ + + +#ifndef _CBRANCHCYCLICWIDGET_H +#define _CBRANCHCYCLICWIDGET_H + +#include +#include +#include + +#include "core/cTheoreticalSpectrum.h" +#include "gui/cLinearWidget.h" +#include "gui/cCyclicWidget.h" + + +// forward declaration +class QPaintEvent; + + +/** + \brief Generate labels of fragment ions on a branch of a branch-cyclic peptide (direction down). + \param nterminal if true only nterminal fragment ions are drawn, if false only cterminal fragment ions are drawn + \param rotationid id of a sequence rotation + \param labels labels of fragment ions + \param parameters a pointer to parameters of the application + \param theoreticalspectrum a theoretical spectrum + \param centerx x coordinate of a center of the ring + \param topmargin top margin in pixels + \param horizontalstep horizontal step (determine width of blocks) + \param verticalstep vertical step (determine height of blocks) + \param visiblerotationid id of a rotation whose fragment ions are visualized + \param branchstart starting position of a branch +*/ +void generateBranchLabelsDown(bool nterminal, int rotationid, unordered_set& labels, cParameters* parameters, cTheoreticalSpectrum* theoreticalspectrum, int centerx, int topmargin, int horizontalstep, int verticalstep, int visiblerotationid, int branchstart); + + +/** + \brief Generate labels of fragment ions on a branch of a branch-cyclic peptide (direction up). + \param nterminal if true only nterminal fragment ions are drawn, if false only cterminal fragment ions are drawn + \param rotationid id of a sequence rotation + \param labels labels of fragment ions + \param parameters a pointer to parameters of the application + \param theoreticalspectrum a theoretical spectrum + \param centerx x coordinate of a center of the ring + \param topmargin top margin in pixels + \param horizontalstep horizontal step (determine width of blocks) + \param verticalstep vertical step (determine height of blocks) + \param visiblerotationid id of a rotation whose fragment ions are visualized + \param branchend end position of a branch +*/ +void generateBranchLabelsUp(bool nterminal, int rotationid, unordered_set& labels, cParameters* parameters, cTheoreticalSpectrum* theoreticalspectrum, int centerx, int topmargin, int horizontalstep, int verticalstep, int visiblerotationid, int branchend); + + +/** + \brief Visualization of a branch-cyclic peptide. +*/ +class cBranchCyclicWidget : public QWidget +{ + Q_OBJECT + +public: + + + /** + \brief The constructor. + */ + cBranchCyclicWidget(); + + + /** + \brief Initialize the widget. + \param parameters a pointer to parameters + \param theoreticalspectrum a pointer to a theoretical spectrum + */ + void initialize(cParameters* parameters, cTheoreticalSpectrum* theoreticalspectrum); + + + /** + \brief Export peptide into a PDF or a PS file. + \param filename filename + \param postscript if true then PS file is generated instead of PDF + */ + void exportToPDF(QString filename, bool postscript); + + + /** + \brief Export peptide scene into a PNG file. + \param filename filename + */ + void exportToPNG(QString filename); + + + /** + \brief Export peptide scene into a SVG file. + \param filename filename + */ + void exportToSVG(QString filename); + + +protected: + + + /** + \brief Handle the paint event. + \param event pointer to QPaintEvent + */ + void paintEvent(QPaintEvent *event); + + +private: + + void paint(QPainter& painter); + + cParameters* parameters; + cTheoreticalSpectrum* theoreticalspectrum; + int visiblerotationid; + int visibletrotationid; + + +private slots: + + void rotationChanged(int index); + + void trotationChanged(int index); + +}; + +#endif \ No newline at end of file diff --git a/CycloBranch/gui/cBricksDatabaseWidget.cpp b/CycloBranch/gui/cBricksDatabaseWidget.cpp index ed58bfa..c2c65d2 100644 --- a/CycloBranch/gui/cBricksDatabaseWidget.cpp +++ b/CycloBranch/gui/cBricksDatabaseWidget.cpp @@ -37,29 +37,41 @@ cBricksDatabaseWidget::cBricksDatabaseWidget(QWidget* parent) { insertrow = new QPushButton(tr("Add Row")); insertrow->setToolTip("Add a new row."); + insertrow->setShortcut(QKeySequence(Qt::Key_Insert)); + removechecked = new QPushButton(tr(" Remove Rows ")); removechecked->setToolTip("Remove selected rows."); + removechecked->setShortcut(QKeySequence(Qt::Key_Delete)); close = new QPushButton(tr("Close")); close->setToolTip("Close the window."); + load = new QPushButton(tr("Load")); load->setToolTip("Load the database of building blocks."); + load->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_L)); + save = new QPushButton(QString(" Save ")); save->setToolTip("Save the database of building blocks in the current file. When a file has not been loaded yet, the \"Save As ...\" file dialog is opened."); + saveas = new QPushButton(tr("Save As...")); saveas->setToolTip("Save the database of building blocks into a file."); + saveas->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_D)); rowsfilterline = new QLineEdit(); rowsfilterline->setMinimumWidth(250); rowsfilterline->setToolTip("Text to Find"); + rowsfiltercasesensitive = new QCheckBox(); rowsfiltercasesensitive->setToolTip("Case Sensitive"); + rowsfilterbutton = new QPushButton("Filter"); rowsfilterbutton->setToolTip("Filter Search Results"); rowsfilterbutton->setMinimumWidth(50); + rowsfilterclearbutton = new QPushButton("Clear"); rowsfilterclearbutton->setToolTip("Clear Form and Reset Search Results"); rowsfilterclearbutton->setMinimumWidth(50); + rowsfilterclearbutton->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R)); rowsfilterhbox = new QHBoxLayout(); rowsfilterhbox->addWidget(rowsfilterline); @@ -97,6 +109,7 @@ cBricksDatabaseWidget::cBricksDatabaseWidget(QWidget* parent) { database->horizontalHeaderItem(3)->setText("Residue Summary"); database->setHorizontalHeaderItem(4, new QTableWidgetItem()); database->horizontalHeaderItem(4)->setText("Monoisotopic Residue Mass"); + database->setItemDelegateForColumn(4, &columndelegate); database->setHorizontalHeaderItem(5, new QTableWidgetItem()); database->horizontalHeaderItem(5)->setText("Reference(s)"); database->setHorizontalHeaderItem(6, new QTableWidgetItem()); @@ -270,7 +283,7 @@ bool cBricksDatabaseWidget::checkFormula(int row, const string& summary) { return false; } if (database->item(row, 4)) { - database->item(row, 4)->setData(Qt::DisplayRole, to_string(formula.getMass()).c_str()); + database->item(row, 4)->setData(Qt::DisplayRole, cropPrecisionToSixDecimals(formula.getMass())); } return true; } @@ -304,6 +317,26 @@ void cBricksDatabaseWidget::keyPressEvent(QKeyEvent *event) { filterRows(); } } + + if (event->key() == Qt::Key_F1) { + #if OS_TYPE == WIN + QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo("docs/html/blockseditor.html").absoluteFilePath())); + #else + QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(installdir + "docs/html/blockseditor.html").absoluteFilePath())); + #endif + } + + if ((event->modifiers() == Qt::ControlModifier) && (event->key() == Qt::Key_F)) { + rowsfilterline->setFocus(); + } + + if ((event->modifiers() == Qt::ControlModifier) && (event->key() == Qt::Key_N)) { + rowsfiltercasesensitive->setChecked(!rowsfiltercasesensitive->isChecked()); + } + + if ((event->modifiers() == Qt::ControlModifier) && (event->key() == Qt::Key_S)) { + saveDatabase(); + } } @@ -368,7 +401,7 @@ void cBricksDatabaseWidget::loadDatabase() { database->item(i, 3)->setText(bricks[i].getSummary().c_str()); database->setItem(i, 4, widgetitemallocator.getNewItem()); - database->item(i, 4)->setData(Qt::DisplayRole, to_string(bricks[i].getMass()).c_str()); + database->item(i, 4)->setData(Qt::DisplayRole, cropPrecisionToSixDecimals(bricks[i].getMass())); database->setItem(i, 5, widgetitemallocator.getNewItem()); database->item(i, 5)->setText(bricks[i].getReferencesAsString().c_str()); diff --git a/CycloBranch/gui/cBricksDatabaseWidget.h b/CycloBranch/gui/cBricksDatabaseWidget.h index 5b5b03b..5f595d3 100644 --- a/CycloBranch/gui/cBricksDatabaseWidget.h +++ b/CycloBranch/gui/cBricksDatabaseWidget.h @@ -8,10 +8,14 @@ #define _CBRICKSDATABASEWIDGET_H #include +#include +#include +#include #include #include "core/utilities.h" #include "core/cBricksDatabase.h" #include "core/cAllocator.h" +#include "gui/cDelegate.h" using namespace std; @@ -93,6 +97,7 @@ class cBricksDatabaseWidget : public QWidget cBricksDatabase bricks; vector headersort; + cDelegate columndelegate; cAllocator widgetitemallocator; diff --git a/CycloBranch/gui/cCyclicWidget.cpp b/CycloBranch/gui/cCyclicWidget.cpp index bffbf38..5f38f81 100644 --- a/CycloBranch/gui/cCyclicWidget.cpp +++ b/CycloBranch/gui/cCyclicWidget.cpp @@ -209,15 +209,33 @@ void generateCyclicLabelsToRight(bool nterminal, int rotationid, int rotationsta double cumulativeangle; string name; int m; - if ((visiblerotationid == -1) || ((parameters->peptidetype == cyclic) && (visiblerotationid == rotationid)) || ((parameters->peptidetype == lasso) && (visiblerotationid == rotationid/6))) { + if ((visiblerotationid == -1) || ((parameters->peptidetype == cyclic) && (visiblerotationid == rotationid)) +#if POLYKETIDE_SIDEROPHORES == 1 + || ((parameters->peptidetype == cyclicpolyketide) && (visiblerotationid == rotationid)) +#endif + || ((parameters->peptidetype == branchcyclic) && (visiblerotationid == rotationid/6))) { for (int i = 0; i < (int)parameters->fragmentionsfortheoreticalspectra.size(); i++) { - if ((nterminal && parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].nterminal) || (!nterminal && parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].cterminal)) { + if ((nterminal && parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].nterminal) || (!nterminal && parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].cterminal) +#if POLYKETIDE_SIDEROPHORES == 1 + || (parameters->peptidetype == cyclicpolyketide) +#endif + ) { m = 0; for (int j = fragmentstart; j < fragmentend; j++) { if ((branchstart == -1) || (branchend == -1) || ((branchstart >= 0) && (j < branchstart)) || ((branchend >= 0) && (j >= branchend))) { if (theoreticalspectrum->getVisualCoverage()[rotationid*parameters->fragmentionsfortheoreticalspectra.size() + i].series[j] > 0) { name = theoreticalspectrum->getVisualCoverage()[rotationid*parameters->fragmentionsfortheoreticalspectra.size() + i].name.substr(0, theoreticalspectrum->getVisualCoverage()[rotationid*parameters->fragmentionsfortheoreticalspectra.size() + i].name.rfind('_') + 1); - name += parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name[0] + to_string(j + 1) + parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name.substr(1); +#if POLYKETIDE_SIDEROPHORES == 1 + if (parameters->peptidetype == cyclicpolyketide) { + name += parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name.substr(0, 2) + to_string(j + 1) + parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name.substr(2); + } + else { +#endif + name += parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name[0] + to_string(j + 1) + parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name.substr(1); +#if POLYKETIDE_SIDEROPHORES == 1 + } +#endif + cumulativeangle = angle*(double)((rotationstart + m + 1) % numberofringblocks) + angle/(double)2; if (cumulativeangle < pi/2) { QPoint p3(centerx + sin(cumulativeangle)*(radius + linesize) - sin(pi/2 - cumulativeangle)*cornerlinesize, centery - sin(pi/2 - cumulativeangle)*(radius + linesize) - sin(cumulativeangle)*cornerlinesize); @@ -249,15 +267,32 @@ void generateCyclicLabelsToLeft(bool nterminal, int rotationid, int rotationstar double cumulativeangle; string name; int m; - if ((visiblerotationid == -1) || ((parameters->peptidetype == cyclic) && (visiblerotationid == rotationid)) || ((parameters->peptidetype == lasso) && (visiblerotationid == rotationid/6))) { + if ((visiblerotationid == -1) || ((parameters->peptidetype == cyclic) && (visiblerotationid == rotationid)) +#if POLYKETIDE_SIDEROPHORES == 1 + || ((parameters->peptidetype == cyclicpolyketide) && (visiblerotationid == rotationid)) +#endif + || ((parameters->peptidetype == branchcyclic) && (visiblerotationid == rotationid/6))) { for (int i = 0; i < (int)parameters->fragmentionsfortheoreticalspectra.size(); i++) { - if ((nterminal && parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].nterminal) || (!nterminal && parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].cterminal)) { + if ((nterminal && parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].nterminal) || (!nterminal && parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].cterminal) +#if POLYKETIDE_SIDEROPHORES == 1 + || (parameters->peptidetype == cyclicpolyketide) +#endif + ) { m = 0; for (int j = fragmentstart; j < fragmentend; j++) { if ((branchstart == -1) || (branchend == -1) || ((branchstart >= 0) && (j < branchstart)) || ((branchend >= 0) && (j >= branchend))) { if (theoreticalspectrum->getVisualCoverage()[rotationid*parameters->fragmentionsfortheoreticalspectra.size() + i].series[j] > 0) { name = theoreticalspectrum->getVisualCoverage()[rotationid*parameters->fragmentionsfortheoreticalspectra.size() + i].name.substr(0, theoreticalspectrum->getVisualCoverage()[rotationid*parameters->fragmentionsfortheoreticalspectra.size() + i].name.rfind('_') + 1); - name += parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name[0] + to_string(j + 1) + parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name.substr(1); +#if POLYKETIDE_SIDEROPHORES == 1 + if (parameters->peptidetype == cyclicpolyketide) { + name += parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name.substr(0, 2) + to_string(j + 1) + parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name.substr(2); + } + else { +#endif + name += parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name[0] + to_string(j + 1) + parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name.substr(1); +#if POLYKETIDE_SIDEROPHORES == 1 + } +#endif cumulativeangle = angle*(double)((2*numberofringblocks - rotationstart + numberofringblocks - m - 1) % numberofringblocks) + angle/(double)2; if (cumulativeangle < pi/2) { QPoint p4(centerx + sin(cumulativeangle)*(radius - linesize) + sin(pi/2 - cumulativeangle)*cornerlinesize, centery - sin(pi/2 - cumulativeangle)*(radius - linesize) + sin(cumulativeangle)*cornerlinesize); diff --git a/CycloBranch/gui/cDelegate.cpp b/CycloBranch/gui/cDelegate.cpp new file mode 100644 index 0000000..052adf2 --- /dev/null +++ b/CycloBranch/gui/cDelegate.cpp @@ -0,0 +1,12 @@ +#include "gui/cDelegate.h" + + +QString cDelegate::displayText(const QVariant &value, const QLocale &locale) const { + if (value.type() == QVariant::Double) { + return locale.toString(value.toDouble(), 'f', 6); + } + else { + return QStyledItemDelegate::displayText(value, locale); + } +} + diff --git a/CycloBranch/gui/cDelegate.h b/CycloBranch/gui/cDelegate.h new file mode 100644 index 0000000..0d5b518 --- /dev/null +++ b/CycloBranch/gui/cDelegate.h @@ -0,0 +1,38 @@ +/** + \file cDelegate.h + \brief A delegate for visualization of doubles in QTableWidget. +*/ + + +#ifndef _CDELEGATE_H +#define _CDELEGATE_H + +#include +#include +#include +#include + + +/** + \brief A delegate for visualization of doubles in QTableWidget. +*/ +class cDelegate: public QStyledItemDelegate { + + Q_OBJECT + +public: + + + /** + \brief Reimplementation of displayText. + \param value a value + \param locale a locale + \retval QString the value as a string + */ + QString displayText(const QVariant &value, const QLocale &locale) const; + +}; + + +#endif + diff --git a/CycloBranch/gui/cDrawPeptideWidget.cpp b/CycloBranch/gui/cDrawPeptideWidget.cpp index 2c659de..a33802d 100644 --- a/CycloBranch/gui/cDrawPeptideWidget.cpp +++ b/CycloBranch/gui/cDrawPeptideWidget.cpp @@ -37,11 +37,15 @@ cDrawPeptideWidget::cDrawPeptideWidget(QWidget* parent) { peptidetypecombobox = new QComboBox(); peptidetypecombobox->setToolTip("Select the type of peptide."); - peptidetypecombobox->setMaximumWidth(150); + peptidetypecombobox->setMaximumWidth(250); peptidetypecombobox->addItem(tr("Linear")); peptidetypecombobox->addItem(tr("Cyclic")); peptidetypecombobox->addItem(tr("Branched")); peptidetypecombobox->addItem(tr("Branch-cyclic")); +#if POLYKETIDE_SIDEROPHORES == 1 + peptidetypecombobox->addItem(tr("Linear oligoketide siderophore")); + peptidetypecombobox->addItem(tr("Cyclic oligoketide siderophore")); +#endif //peptidetypecombobox->addItem(tr("Linear polysaccharide (beta version)")); //peptidetypecombobox->addItem(tr("Other")); drawpeptideformlayout->addRow(tr("Peptide Type: "), peptidetypecombobox); @@ -130,21 +134,21 @@ cDrawPeptideWidget::cDrawPeptideWidget(QWidget* parent) { branchedwidget = new cBranchedWidget(); branchedwidget->initialize(0, 0); - lassowidget = new cLassoWidget(); - lassowidget->initialize(0, 0); + branchcyclicwidget = new cBranchCyclicWidget(); + branchcyclicwidget->initialize(0, 0); vbox = new QVBoxLayout(); vbox->addWidget(drawpeptideformwidget); vbox->addWidget(linearwidget); vbox->addWidget(cyclicwidget); vbox->addWidget(branchedwidget); - vbox->addWidget(lassowidget); + vbox->addWidget(branchcyclicwidget); vbox->setStretchFactor(drawpeptideformwidget, 1); vbox->setStretchFactor(linearwidget, 5); vbox->setStretchFactor(cyclicwidget, 5); vbox->setStretchFactor(branchedwidget, 5); - vbox->setStretchFactor(lassowidget, 5); + vbox->setStretchFactor(branchcyclicwidget, 5); mainbox = new QHBoxLayout(); mainbox->addLayout(vbox); @@ -176,7 +180,7 @@ cDrawPeptideWidget::~cDrawPeptideWidget() { delete linearwidget; delete cyclicwidget; delete branchedwidget; - delete lassowidget; + delete branchcyclicwidget; delete peptidetypecombobox; @@ -242,6 +246,14 @@ void cDrawPeptideWidget::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Escape) { hide(); } + + if (event->key() == Qt::Key_F1) { + #if OS_TYPE == WIN + QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo("docs/html/drawpeptide.html").absoluteFilePath())); + #else + QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(installdir + "docs/html/drawpeptide.html").absoluteFilePath())); + #endif + } } @@ -250,7 +262,7 @@ void cDrawPeptideWidget::separateBlocksChanged(int state) { sequenceline->setDisabled(true); numberofblocksbackbone->setDisabled(false); backboneblockswidget->setDisabled(false); - if (((peptideType)(peptidetypecombobox->currentIndex()) == branched) || ((peptideType)(peptidetypecombobox->currentIndex()) == lasso)) { + if (((ePeptideType)(peptidetypecombobox->currentIndex()) == branched) || ((ePeptideType)(peptidetypecombobox->currentIndex()) == branchcyclic)) { numberofblocksbranch->setDisabled(false); branchblockswidget->setDisabled(false); branchposition->setDisabled(false); @@ -276,12 +288,18 @@ void cDrawPeptideWidget::separateBlocksChanged(int state) { void cDrawPeptideWidget::numberOfBackboneBlocksChanged(int numberofblocks) { - switch ((peptideType)peptidetypecombobox->currentIndex()) { + switch ((ePeptideType)peptidetypecombobox->currentIndex()) { case linear: +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: +#endif numberofblocksbackbone->setRange(1, 100); branchposition->setRange(1, 1); break; case cyclic: +#if POLYKETIDE_SIDEROPHORES == 1 + case cyclicpolyketide: +#endif numberofblocksbackbone->setRange(2, 100); branchposition->setRange(1, 1); break; @@ -289,7 +307,7 @@ void cDrawPeptideWidget::numberOfBackboneBlocksChanged(int numberofblocks) { numberofblocksbackbone->setRange(3, 100); branchposition->setRange(2, numberofblocks - 1); break; - case lasso: + case branchcyclic: numberofblocksbackbone->setRange(2, 100); branchposition->setRange(1, numberofblocks); break; @@ -349,14 +367,20 @@ void cDrawPeptideWidget::peptideTypeChanged(int index) { linearwidget->hide(); cyclicwidget->hide(); branchedwidget->hide(); - lassowidget->hide(); + branchcyclicwidget->hide(); - switch ((peptideType)index) { + switch ((ePeptideType)index) { case linear: +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: +#endif linearwidget->show(); numberOfBackboneBlocksChanged(max(backboneblocks->count(), 1)); break; case cyclic: +#if POLYKETIDE_SIDEROPHORES == 1 + case cyclicpolyketide: +#endif cyclicwidget->show(); numberOfBackboneBlocksChanged(max(backboneblocks->count(), 2)); break; @@ -364,8 +388,8 @@ void cDrawPeptideWidget::peptideTypeChanged(int index) { branchedwidget->show(); numberOfBackboneBlocksChanged(max(backboneblocks->count(), 3)); break; - case lasso: - lassowidget->show(); + case branchcyclic: + branchcyclicwidget->show(); numberOfBackboneBlocksChanged(max(backboneblocks->count(), 2)); break; case linearpolysaccharide: @@ -421,9 +445,13 @@ void cDrawPeptideWidget::blocksChanged() { branchcount--; } - peptideType peptidetype = (peptideType)peptidetypecombobox->currentIndex(); + ePeptideType peptidetype = (ePeptideType)peptidetypecombobox->currentIndex(); - if ((peptidetype == linear) || (peptidetype == linearpolysaccharide) || ((backbonecount > 1) && (peptidetype == cyclic))) { + if ((peptidetype == linear) || (peptidetype == linearpolysaccharide) || ((backbonecount > 1) && (peptidetype == cyclic)) +#if POLYKETIDE_SIDEROPHORES == 1 + || (peptidetype == linearpolyketide) || ((backbonecount > 1) && (peptidetype == cyclicpolyketide)) +#endif + ) { for (int i = 0; i < backbonecount; i++) { if (i > 0) { s += "-"; @@ -439,7 +467,7 @@ void cDrawPeptideWidget::blocksChanged() { } } - if (((backbonecount > branchposition->value()) && (peptidetype == branched)) || ((backbonecount >= branchposition->value()) && (peptidetype == lasso))) { + if (((backbonecount > branchposition->value()) && (peptidetype == branched)) || ((backbonecount >= branchposition->value()) && (peptidetype == branchcyclic))) { for (int i = 0; i < backbonecount; i++) { if (i == branchposition->value() - 1) { s += "\\(["; @@ -509,7 +537,7 @@ void cDrawPeptideWidget::sequenceChanged() { } string errormessage = ""; - peptideType peptidetype = (peptideType)peptidetypecombobox->currentIndex(); + ePeptideType peptidetype = (ePeptideType)peptidetypecombobox->currentIndex(); string sequence = sequenceline->text().toStdString(); int start1, start2; @@ -560,7 +588,11 @@ void cDrawPeptideWidget::sequenceChanged() { b.setComposition(theoreticalspectrum.getCandidate().getComposition(), false); b.explodeToIntComposition(intcomposition); - if ((peptidetype == linear) || (peptidetype == linearpolysaccharide) || (peptidetype == cyclic)) { + if ((peptidetype == linear) || (peptidetype == linearpolysaccharide) || (peptidetype == cyclic) +#if POLYKETIDE_SIDEROPHORES == 1 + || (peptidetype == linearpolyketide) || (peptidetype == cyclicpolyketide) +#endif + ) { numberofblocksbackbone->setValue((int)v.size()); for (int i = 0; i < backboneblocks->count(); i++) { if (i < (int)v.size()) { @@ -573,7 +605,7 @@ void cDrawPeptideWidget::sequenceChanged() { } } - if ((peptidetype == branched) || (peptidetype == lasso)) { + if ((peptidetype == branched) || (peptidetype == branchcyclic)) { branchstart = theoreticalspectrum.getCandidate().getBranchStart(); branchend = theoreticalspectrum.getCandidate().getBranchEnd(); @@ -614,17 +646,23 @@ void cDrawPeptideWidget::sequenceChanged() { void cDrawPeptideWidget::drawPeptide(vector& composition, cBricksDatabase& bricksdb, int branchstart, int branchend) { vector netmp; - vector lassorotations; + vector branchcyclicrotations; int numberofbricks; - switch ((peptideType)peptidetypecombobox->currentIndex()) { + switch ((ePeptideType)peptidetypecombobox->currentIndex()) { case linear: +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: +#endif theoreticalspectrum.getCandidate().setCandidate(composition, netmp, 0, 0, 0, -1, -1); theoreticalspectrum.getCandidate().setAcronyms(bricksdb); linearwidget->initialize(0, &theoreticalspectrum); linearwidget->repaint(); break; case cyclic: +#if POLYKETIDE_SIDEROPHORES == 1 + case cyclicpolyketide: +#endif theoreticalspectrum.getCandidate().setCandidate(composition, netmp, 0, 0, 0, -1, -1); theoreticalspectrum.getCandidate().setAcronyms(bricksdb); cyclicwidget->initialize(0, &theoreticalspectrum); @@ -637,26 +675,26 @@ void cDrawPeptideWidget::drawPeptide(vector& composition, cBricksDatabas branchedwidget->initialize(0, &theoreticalspectrum); branchedwidget->repaint(); break; - case lasso: + case branchcyclic: theoreticalspectrum.getCandidate().setCandidate(composition, netmp, 0, 0, 0, branchstart, branchend); // normalize the candidate - theoreticalspectrum.getCandidate().getLassoRotations(lassorotations, false); + theoreticalspectrum.getCandidate().getBranchCyclicRotations(branchcyclicrotations, false); numberofbricks = getNumberOfBricks(theoreticalspectrum.getCandidate().getComposition()); - for (int i = 0; i < (int)lassorotations.size(); i++) { - if (lassorotations[i].getBranchEnd() == numberofbricks - 1) { + for (int i = 0; i < (int)branchcyclicrotations.size(); i++) { + if (branchcyclicrotations[i].getBranchEnd() == numberofbricks - 1) { vector v; - v.push_back(lassorotations[i].getComposition()); + v.push_back(branchcyclicrotations[i].getComposition()); vector cpath = theoreticalspectrum.getCandidate().getPath(); - theoreticalspectrum.getCandidate().setCandidate(v, cpath, theoreticalspectrum.getCandidate().getStartModifID(), theoreticalspectrum.getCandidate().getEndModifID(), theoreticalspectrum.getCandidate().getMiddleModifID(), lassorotations[i].getBranchStart(), lassorotations[i].getBranchEnd()); + theoreticalspectrum.getCandidate().setCandidate(v, cpath, theoreticalspectrum.getCandidate().getStartModifID(), theoreticalspectrum.getCandidate().getEndModifID(), theoreticalspectrum.getCandidate().getMiddleModifID(), branchcyclicrotations[i].getBranchStart(), branchcyclicrotations[i].getBranchEnd()); break; } } theoreticalspectrum.getCandidate().setBackboneAcronyms(bricksdb); theoreticalspectrum.getCandidate().setBranchAcronyms(bricksdb); - lassowidget->initialize(0, &theoreticalspectrum); - lassowidget->repaint(); + branchcyclicwidget->initialize(0, &theoreticalspectrum); + branchcyclicwidget->repaint(); break; case linearpolysaccharide: break; diff --git a/CycloBranch/gui/cDrawPeptideWidget.h b/CycloBranch/gui/cDrawPeptideWidget.h index e44ea87..7809599 100644 --- a/CycloBranch/gui/cDrawPeptideWidget.h +++ b/CycloBranch/gui/cDrawPeptideWidget.h @@ -8,10 +8,13 @@ #define _CDRAWPEPTIDEWIDGET_H #include +#include +#include +#include #include "gui/cLinearWidget.h" #include "gui/cCyclicWidget.h" #include "gui/cBranchedWidget.h" -#include "gui/cLassoWidget.h" +#include "gui/cBranchCyclicWidget.h" // forward declaration @@ -114,7 +117,7 @@ class cDrawPeptideWidget : public QWidget cLinearWidget* linearwidget; cCyclicWidget* cyclicwidget; cBranchedWidget* branchedwidget; - cLassoWidget* lassowidget; + cBranchCyclicWidget* branchcyclicwidget; QVBoxLayout* vbox; QHBoxLayout* mainbox; diff --git a/CycloBranch/gui/cFragmentIonsListWidget.cpp b/CycloBranch/gui/cFragmentIonsListWidget.cpp index 8e002e8..09ed03f 100644 --- a/CycloBranch/gui/cFragmentIonsListWidget.cpp +++ b/CycloBranch/gui/cFragmentIonsListWidget.cpp @@ -17,11 +17,11 @@ cFragmentIonsListWidget::cFragmentIonsListWidget(QObject* parent) { list->setSelectionMode(QAbstractItemView::MultiSelection); selectall = new QPushButton("Select All"); - selectall->setToolTip("Select all fragment ion types in the list."); + selectall->setToolTip("Select all ion types in the list."); clearall = new QPushButton("Clear All"); - clearall->setToolTip("Unselect all fragment ion types in the list."); + clearall->setToolTip("Unselect all ion types in the list."); reset = new QPushButton("Reset"); - reset->setToolTip("Reset to a default selection of fragment ions considering selected \"Peptide Type\"."); + reset->setToolTip("Reset to the default seletion of ions."); hbox->addWidget(selectall); hbox->addWidget(clearall); diff --git a/CycloBranch/gui/cGraphWidget.cpp b/CycloBranch/gui/cGraphWidget.cpp index 201f2fc..dd409e2 100644 --- a/CycloBranch/gui/cGraphWidget.cpp +++ b/CycloBranch/gui/cGraphWidget.cpp @@ -67,5 +67,13 @@ void cGraphWidget::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Escape) { hide(); } + + if (event->key() == Qt::Key_F1) { + #if OS_TYPE == WIN + QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo("docs/html/menubar.html").absoluteFilePath())); + #else + QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(installdir + "docs/html/menubar.html").absoluteFilePath())); + #endif + } } diff --git a/CycloBranch/gui/cGraphWidget.h b/CycloBranch/gui/cGraphWidget.h index 08a04ba..dbcbddb 100644 --- a/CycloBranch/gui/cGraphWidget.h +++ b/CycloBranch/gui/cGraphWidget.h @@ -10,6 +10,9 @@ #include #include #include +#include +#include +#include #include "core/utilities.h" diff --git a/CycloBranch/gui/cHTMLExportDialog.cpp b/CycloBranch/gui/cHTMLExportDialog.cpp new file mode 100644 index 0000000..b74673f --- /dev/null +++ b/CycloBranch/gui/cHTMLExportDialog.cpp @@ -0,0 +1,77 @@ +#include "gui/cHTMLExportDialog.h" + + +cHTMLExportDialog::cHTMLExportDialog(QWidget* parent) { + this->parent = parent; + + checkboxparameters = new QCheckBox(); + checkboxdenovo = new QCheckBox(); + checkboxlogwindow = new QCheckBox(); + checkboxsummaryresultstable = new QCheckBox(); + checkboxsummarypeakstable = new QCheckBox(); + checkboxpeakstable = new QCheckBox(); + checkboxunmatchedexperimental = new QCheckBox(); + checkboxunmatchedtheoretical = new QCheckBox(); + checkboxdetails = new QCheckBox(); + + form1 = new QFormLayout(); + form1->addRow("Parameters", checkboxparameters); + form1->addRow("De Novo Graph", checkboxdenovo); + form1->addRow("Log Window", checkboxlogwindow); + form1->addRow("Output Report Table", checkboxsummaryresultstable); + form1->addRow("Summary Table of Matched Peaks", checkboxsummarypeakstable); + + group1 = new QGroupBox("Summary"); + group1->setLayout(form1); + + form2 = new QFormLayout(); + form2->addRow("Peaks Table", checkboxpeakstable); + form2->addRow("Unmatched Experimental Peaks", checkboxunmatchedexperimental); + form2->addRow("Unmatched Theoretical Peaks", checkboxunmatchedtheoretical); + form2->addRow("Details of Identification", checkboxdetails); + + group2 = new QGroupBox("Individual Rows in Output Report Table"); + group2->setLayout(form2); + + hbox = new QHBoxLayout(); + hbox->addWidget(group1); + hbox->addWidget(group2); + + buttonbox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this); + + vbox = new QVBoxLayout(); + vbox->addLayout(hbox); + vbox->addWidget(buttonbox); + + setLayout(vbox); + setWindowTitle("Export HTML report"); + setMinimumWidth(500); + + connect(buttonbox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonbox, SIGNAL(rejected()), this, SLOT(reject())); +} + + +cHTMLExportDialog::~cHTMLExportDialog() { + delete checkboxparameters; + delete checkboxdenovo; + delete checkboxlogwindow; + delete checkboxsummaryresultstable; + delete checkboxsummarypeakstable; + delete checkboxpeakstable; + delete checkboxunmatchedexperimental; + delete checkboxunmatchedtheoretical; + delete checkboxdetails; + + delete form1; + delete form2; + + delete group1; + delete group2; + + delete hbox; + delete buttonbox; + + delete vbox; +} + diff --git a/CycloBranch/gui/cHTMLExportDialog.h b/CycloBranch/gui/cHTMLExportDialog.h new file mode 100644 index 0000000..ed1dd3c --- /dev/null +++ b/CycloBranch/gui/cHTMLExportDialog.h @@ -0,0 +1,111 @@ +/** + \file cHTMLExportDialog.h + \brief The implementation of HTML export dialog. +*/ + + +#ifndef _CHTMLEXPORTDIALOG_H +#define _CHTMLEXPORTDIALOG_H + +#include +#include +#include +#include +#include +#include +#include + + +/** + \brief The implementation of HTML export dialog. +*/ +class cHTMLExportDialog : public QDialog +{ + Q_OBJECT + +private: + + QWidget* parent; + + QFormLayout* form1; + QFormLayout* form2; + + QGroupBox* group1; + QGroupBox* group2; + + QHBoxLayout* hbox; + QDialogButtonBox* buttonbox; + QVBoxLayout* vbox; + +public: + + /** + \brief Export parameters. + */ + QCheckBox* checkboxparameters; + + + /** + \brief Export de novo graph. + */ + QCheckBox* checkboxdenovo; + + + /** + \brief Export log window. + */ + QCheckBox* checkboxlogwindow; + + + /** + \brief Export output report table. + */ + QCheckBox* checkboxsummaryresultstable; + + + /** + \brief Export summary table of matched peaks. + */ + QCheckBox* checkboxsummarypeakstable; + + + /** + \brief Export peakss table. + */ + QCheckBox* checkboxpeakstable; + + + /** + \brief Export unmatched experimental peaks. + */ + QCheckBox* checkboxunmatchedexperimental; + + + /** + \brief Export matched experimental peaks. + */ + QCheckBox* checkboxunmatchedtheoretical; + + + /** + \brief Export details of identification. + */ + QCheckBox* checkboxdetails; + + + /** + \brief The constructor. + \param parent pointer to a parent widget + */ + cHTMLExportDialog(QWidget* parent = (QWidget *)0); + + + /** + \brief The destructor. + */ + ~cHTMLExportDialog(); + +}; + +#endif + diff --git a/CycloBranch/gui/cLinearWidget.cpp b/CycloBranch/gui/cLinearWidget.cpp index b1d7c30..4c116af 100644 --- a/CycloBranch/gui/cLinearWidget.cpp +++ b/CycloBranch/gui/cLinearWidget.cpp @@ -176,18 +176,50 @@ void cLinearWidget::paint(QPainter& painter) { int len = (int)theoreticalspectrum->getVisualCoverage()[0].series.size(); for (int i = 0; i < (int)parameters->fragmentionsfortheoreticalspectra.size(); i++) { +#if POLYKETIDE_SIDEROPHORES == 1 + if (((parameters->peptidetype == linear) && (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].nterminal)) || + ((parameters->peptidetype == linearpolyketide) && ((parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].parent == l1h_ion) || (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].parent == l2h_ion) + || (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].parent == l1oh_ion) || (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].parent == l2oh_ion)))) + { +#else if (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].nterminal) { +#endif for (int j = 0; j < len; j++) { if (theoreticalspectrum->getVisualCoverage()[i].series[j] > 0) { - name = parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name[0] + to_string(j + 1) + parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name.substr(1); +#if POLYKETIDE_SIDEROPHORES == 1 + if (parameters->peptidetype == linearpolyketide) { + name = parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name.substr(0, 2) + to_string(j + 1) + parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name.substr(2); + } + else { +#endif + name = parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name[0] + to_string(j + 1) + parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name.substr(1); +#if POLYKETIDE_SIDEROPHORES == 1 + } +#endif insertLabel(labels, leftmargin + horizontalstep/4 + horizontalstep/8 + horizontalstep*j + horizontalstep/2, topmargin - 35, name, false); } } } +#if POLYKETIDE_SIDEROPHORES == 1 + if (((parameters->peptidetype == linear) && (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].cterminal)) || + ((parameters->peptidetype == linearpolyketide) && ((parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].parent == r1h_ion) || (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].parent == r2h_ion) + || (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].parent == r1oh_ion) || (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].parent == r2oh_ion)))) + { +#else if (parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].cterminal) { +#endif for (int j = len - 1; j >= 0; j--) { if (theoreticalspectrum->getVisualCoverage()[i].series[len - j - 1] > 0) { - name = parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name[0] + to_string(len - j) + parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name.substr(1); +#if POLYKETIDE_SIDEROPHORES == 1 + if (parameters->peptidetype == linearpolyketide) { + name = parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name.substr(0, 2) + to_string(len - j) + parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name.substr(2); + } + else { +#endif + name = parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name[0] + to_string(len - j) + parameters->fragmentdefinitions[parameters->fragmentionsfortheoreticalspectra[i]].name.substr(1); +#if POLYKETIDE_SIDEROPHORES == 1 + } +#endif insertLabel(labels, leftmargin + horizontalstep*(j + 1), topmargin + 35, name, false); } } diff --git a/CycloBranch/gui/cMainThread.cpp b/CycloBranch/gui/cMainThread.cpp index 3204a2b..acd93a2 100644 --- a/CycloBranch/gui/cMainThread.cpp +++ b/CycloBranch/gui/cMainThread.cpp @@ -7,20 +7,32 @@ bool cMainThread::checkModifications(cParameters& parameters, cSequence& sequenc middlemodifid = 0; errormessage = ""; - if ((sequence.getPeptideType() == linear) || (sequence.getPeptideType() == branched) || (sequence.getPeptideType() == lasso) || (sequence.getPeptideType() == linearpolysaccharide)) { - - if ((sequence.getPeptideType() == linear) || (sequence.getPeptideType() == branched) || (sequence.getPeptideType() == linearpolysaccharide)) { + if ((sequence.getPeptideType() == linear) || (sequence.getPeptideType() == branched) || (sequence.getPeptideType() == branchcyclic) || (sequence.getPeptideType() == linearpolysaccharide) +#if POLYKETIDE_SIDEROPHORES == 1 + || (sequence.getPeptideType() == linearpolyketide) +#endif + ) { + + if ((sequence.getPeptideType() == linear) || (sequence.getPeptideType() == branched) || (sequence.getPeptideType() == linearpolysaccharide) +#if POLYKETIDE_SIDEROPHORES == 1 + || (sequence.getPeptideType() == linearpolyketide) +#endif + ) { startmodifid = -1; endmodifid = -1; } - if ((sequence.getPeptideType() == branched) || (sequence.getPeptideType() == lasso)) { + if ((sequence.getPeptideType() == branched) || (sequence.getPeptideType() == branchcyclic)) { middlemodifid = -1; } for (int i = 0; i < (int)parameters.searchedmodifications.size(); i++) { - if ((sequence.getPeptideType() == linear) || (sequence.getPeptideType() == branched) || (sequence.getPeptideType() == linearpolysaccharide)) { + if ((sequence.getPeptideType() == linear) || (sequence.getPeptideType() == branched) || (sequence.getPeptideType() == linearpolysaccharide) +#if POLYKETIDE_SIDEROPHORES == 1 + || (sequence.getPeptideType() == linearpolyketide) +#endif + ) { if (parameters.searchedmodifications[i].name.compare(sequence.getNTterminalModification()) == 0) { startmodifid = i; } @@ -30,7 +42,7 @@ bool cMainThread::checkModifications(cParameters& parameters, cSequence& sequenc } } - if ((sequence.getPeptideType() == branched) || (sequence.getPeptideType() == lasso)) { + if ((sequence.getPeptideType() == branched) || (sequence.getPeptideType() == branchcyclic)) { if (parameters.searchedmodifications[i].name.compare(sequence.getBranchModification()) == 0) { middlemodifid = i; } @@ -146,7 +158,7 @@ void cMainThread::run() { *os << appname.toStdString() << " started at " << time.currentTime().toString().toStdString() << "." << endl << endl; parameters.setOutputStream(*os); - if (parameters.checkAndPrepare() == -1) { + if (parameters.checkAndPrepare(terminatecomputation) == -1) { emitEndSignals(); return; } @@ -174,27 +186,41 @@ void cMainThread::run() { } } - parameters.peaklist.sortbyMass(); - parameters.peaklist.cropMinimumMZRatio(parameters.minimummz, parameters.fragmentmasserrortolerance); - - if ((parameters.mode == denovoengine) || (parameters.mode == singlecomparison) || (parameters.mode == databasesearch)) { - parameters.peaklist.cropMaximumMZRatio(charge(uncharge(parameters.precursormass, parameters.precursorcharge), (parameters.precursorcharge > 0)?1:-1), parameters.precursormasserrortolerance); + bool seriesofspectra = false; + if (parameters.mode == dereplication) { + seriesofspectra = true; } - if (parameters.peaklist.normalizeIntenzity() == -1) { - *os << "Error: the spectrum cannot be normalized because the maximum intensity is <= 0. The format of peaklist is likely incorrect." << endl; + if (parameters.peaklistseries.size() == 0) { + *os << "Error: no peaklist found. The format of peaklist is likely incorrect." << endl; emitEndSignals(); return; } - parameters.peaklist.cropIntenzity(parameters.minimumrelativeintensitythreshold); - //parameters.peaklist.maxHighestPeaksInWindow(10, 50); - *os << "Peaklist:" << endl; - *os << parameters.peaklist.print(); - if (parameters.masserrortolerancefordeisotoping > 0) { - parameters.peaklist.removeIsotopes(parameters.precursorcharge, parameters.masserrortolerancefordeisotoping, this); - } + for (int i = 0; i < (seriesofspectra?parameters.peaklistseries.size():1); i++) { + parameters.peaklistseries[i].sortbyMass(); + parameters.peaklistseries[i].cropMinimumMZRatio(parameters.minimummz, parameters.fragmentmasserrortolerance); + if ((parameters.mode == denovoengine) || (parameters.mode == singlecomparison) || (parameters.mode == databasesearch)) { + parameters.peaklistseries[i].cropMaximumMZRatio(charge(uncharge(parameters.precursormass, parameters.precursorcharge), (parameters.precursorcharge > 0)?1:-1), parameters.precursormasserrortolerance); + } + + if (parameters.peaklistseries[i].normalizeIntenzity() == -1) { + *os << "Error: the spectrum no. " << i + 1 << " cannot be normalized because the maximum intensity is <= 0. The spectrum likely does not contain any peak or the format of peaklist is incorrect." << endl; + emitEndSignals(); + return; + } + parameters.peaklistseries[i].cropIntenzity(parameters.minimumrelativeintensitythreshold); + //parameters.peaklistseries[i].maxHighestPeaksInWindow(10, 50); + + if (parameters.mode != dereplication) { + *os << "Peaklist no. " << i + 1 << ":" << endl; + *os << parameters.peaklistseries[i].print(); + if (parameters.masserrortolerancefordeisotoping > 0) { + parameters.peaklistseries[i].removeIsotopes(parameters.precursorcharge, parameters.masserrortolerancefordeisotoping, this); + } + } + } if ((parameters.mode == denovoengine) || (parameters.mode == singlecomparison) || (parameters.mode == databasesearch)) { int startmodifid, endmodifid, middlemodifid; @@ -355,8 +381,39 @@ void cMainThread::run() { // parse branch of a branched or a branch-cyclic peptide parseBranch(parameters.sequencedatabase[i].getPeptideType(), composition, v, branchstart, branchend); - // set candidate and check precursor mass error + // set candidate c.setCandidate(v, netmp, startmodifid, endmodifid, middlemodifid, branchstart, branchend); + +#if POLYKETIDE_SIDEROPHORES == 1 + + if (!calculatesummaries && ((parameters.sequencedatabase[i].getPeptideType() == linearpolyketide) || (parameters.sequencedatabase[i].getPeptideType() == cyclicpolyketide))) { + + if (!c.checkPolyketideSequence(parameters.bricksdatabase, parameters.sequencedatabase[i].getPeptideType())) { + if (parameters.sequencedatabase[i].getPeptideType() == linearpolyketide) { + *os << "Ignored sequence: " << parameters.sequencedatabase[i].getName() << " " << parameters.sequencedatabase[i].getSequence() << "; the order of building blocks is not correct." << endl; + } + else { + *os << "Ignored sequence: " << parameters.sequencedatabase[i].getName() << " " << parameters.sequencedatabase[i].getSequence() << "; the number or order of building blocks is not correct." << endl; + } + continue; + } + + eResidueLossType leftresiduelosstype = c.getLeftResidueType(parameters.bricksdatabase); + eResidueLossType rightresiduelosstype = c.getRightResidueType(parameters.bricksdatabase); + + if (((leftresiduelosstype == h2) && (c.getStartModifID() > 0) && parameters.searchedmodifications[c.getStartModifID()].cterminal) + || ((leftresiduelosstype == h2o2) && (c.getStartModifID() > 0) && parameters.searchedmodifications[c.getStartModifID()].nterminal) + || ((rightresiduelosstype == h2) && (c.getEndModifID() > 0) && parameters.searchedmodifications[c.getEndModifID()].cterminal) + || ((rightresiduelosstype == h2o2) && (c.getEndModifID() > 0) && parameters.searchedmodifications[c.getEndModifID()].nterminal)) { + *os << "Ignored sequence: " << parameters.sequencedatabase[i].getName() << " " << parameters.sequencedatabase[i].getSequence() << "; the N-terminal modification is attached to C-terminus or vice versa." << endl; + continue; + } + + } + +#endif + + // check the precursor mass error if (!calculatesummaries && !parameters.similaritysearch && !isInPpmMassErrorTolerance(charge(uncharge(parameters.precursormass, parameters.precursorcharge), (parameters.precursorcharge > 0)?1:-1), c.getPrecursorMass(parameters.bricksdatabase, ¶meters), parameters.precursormasserrortolerance)) { continue; } @@ -365,7 +422,7 @@ void cMainThread::run() { candidates.getSet().insert(c); if (calculatesummaries) { - *os << c.getSummaryFormula(parameters, parameters.sequencedatabase[i].getPeptideType()).getSummary() << endl; + *os << i + 1 << " " << c.getSummaryFormula(parameters, parameters.sequencedatabase[i].getPeptideType()).getSummary() << endl; } } @@ -384,11 +441,32 @@ void cMainThread::run() { // database search - MS mode if (parameters.mode == dereplication) { - *os << "Comparing theoretical peaks with the peak list... " << endl; - cTheoreticalSpectrum ts; - ts.compareMSSpectrum(¶meters); + *os << "Comparing theoretical peaks with the experimental peaklist(s)... " << endl; + *os << "Number of experimental peaklists: " << parameters.peaklistseries.size() << endl; + *os << "Processing the peaklist no. : " << endl; + theoreticalspectrumlist->initialize(*os, parameters, &graph); - theoreticalspectrumlist->add(ts); + cTheoreticalSpectrum ts; + ts.setParameters(¶meters); + ts.generateMSSpectrum(); + + for (int i = 0; i < parameters.peaklistseries.size(); i++) { + *os << i + 1 << " "; + if ((i + 1) % 25 == 0) { + *os << endl; + } + + if (terminatecomputation) { + emitEndSignals(); + return; + } + + cTheoreticalSpectrum tstmp; + tstmp = ts; + tstmp.compareMSSpectrum(parameters.peaklistseries[i]); + theoreticalspectrumlist->add(tstmp); + } + *os << " ok" << endl; } diff --git a/CycloBranch/gui/cMainWindow.cpp b/CycloBranch/gui/cMainWindow.cpp index 217ddcf..63c15a9 100644 --- a/CycloBranch/gui/cMainWindow.cpp +++ b/CycloBranch/gui/cMainWindow.cpp @@ -76,6 +76,12 @@ cMainWindow::cMainWindow() { actionDrawPeptide = new QAction(QIcon(":/images/icons/96.png"), tr("Draw &Peptide"), this); actionDrawPeptide->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_P)); + actionNorine = new QAction(QIcon(":/images/icons/25.png"), tr("&Norine"), this); + actionNorine->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_N)); + + actionSmilesToMonomers = new QAction(QIcon(":/images/icons/5.png"), tr("Smiles2Monome&rs"), this); + actionSmilesToMonomers->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_T)); + actionShowIsomers = new QAction(QIcon(":/images/icons/95.png"), tr("Show &Isomers"), this); actionShowIsomers->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_I)); @@ -117,6 +123,9 @@ cMainWindow::cMainWindow() { toolbarTools->addAction(actionSequenceDatabase); toolbarTools->addAction(actionModifications); toolbarTools->addAction(actionDrawPeptide); + toolbarTools->addSeparator(); + toolbarTools->addAction(actionNorine); + toolbarTools->addAction(actionSmilesToMonomers); toolbarView = addToolBar(tr("View")); toolbarView->addAction(actionShowIsomers); @@ -131,14 +140,18 @@ cMainWindow::cMainWindow() { rowsfilterline = new QLineEdit(); rowsfilterline->setMinimumWidth(250); rowsfilterline->setToolTip("Text to Find"); + rowsfiltercasesensitive = new QCheckBox(); rowsfiltercasesensitive->setToolTip("Case Sensitive"); + rowsfilterbutton = new QPushButton("Filter"); rowsfilterbutton->setToolTip("Filter Search Results"); rowsfilterbutton->setMinimumWidth(50); + rowsfilterclearbutton = new QPushButton("Clear"); rowsfilterclearbutton->setToolTip("Clear Form and Reset Search Results"); rowsfilterclearbutton->setMinimumWidth(50); + rowsfilterclearbutton->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R)); rowsfilterhbox = new QHBoxLayout(); rowsfilterhbox->addWidget(rowsfilterline); @@ -170,6 +183,7 @@ cMainWindow::cMainWindow() { modificationswidget = new cModificationsWidget(); drawpeptidewidget = new cDrawPeptideWidget(this); parameterswidget = new cParametersWidget(this); + htmlexportdialog = new cHTMLExportDialog(this); // additional key shortcuts // actionOpen->setShortcut(QKeySequence("Ctrl+O")); @@ -188,6 +202,8 @@ cMainWindow::cMainWindow() { connect(actionSequenceDatabase, SIGNAL(triggered()), this, SLOT(showSequenceDatabase())); connect(actionModifications, SIGNAL(triggered()), this, SLOT(showModifications())); connect(actionDrawPeptide, SIGNAL(triggered()), this, SLOT(showDrawPeptideWidget())); + connect(actionNorine, SIGNAL(triggered()), this, SLOT(gotoNorine())); + connect(actionSmilesToMonomers, SIGNAL(triggered()), this, SLOT(gotoSmiles2Monomers())); connect(actionShowIsomers, SIGNAL(triggered()), this, SLOT(updateSpectra())); connect(actionGraph, SIGNAL(triggered()), this, SLOT(showGraph())); connect(actionLog, SIGNAL(triggered()), this, SLOT(showHideLog())); @@ -216,6 +232,9 @@ cMainWindow::cMainWindow() { menuTools->addAction(actionModifications); menuTools->addSeparator(); menuTools->addAction(actionDrawPeptide); + menuTools->addSeparator(); + menuTools->addAction(actionNorine); + menuTools->addAction(actionSmilesToMonomers); menuView->addAction(actionShowIsomers); menuView->addSeparator(); menuView->addAction(actionGraph); @@ -299,6 +318,7 @@ cMainWindow::~cMainWindow() { delete modificationswidget; delete drawpeptidewidget; delete parameterswidget; + delete htmlexportdialog; delete actionOpenResults; delete actionSaveResults; @@ -311,6 +331,8 @@ cMainWindow::~cMainWindow() { delete actionSequenceDatabase; delete actionModifications; delete actionDrawPeptide; + delete actionNorine; + delete actionSmilesToMonomers; delete actionShowIsomers; delete actionGraph; delete actionLog; @@ -339,6 +361,14 @@ void cMainWindow::keyPressEvent(QKeyEvent *event) { } } } + + if ((event->modifiers() == Qt::ControlModifier) && (event->key() == Qt::Key_F)) { + rowsfilterline->setFocus(); + } + + if ((event->modifiers() == Qt::ControlModifier) && (event->key() == Qt::Key_N)) { + rowsfiltercasesensitive->setChecked(!rowsfiltercasesensitive->isChecked()); + } } @@ -405,7 +435,7 @@ void cMainWindow::reportSpectrum(int id, cTheoreticalSpectrum& theoreticalspectr results->item(row, 3 + dbsearchspecificcolumncount)->setData(Qt::DisplayRole, formula.isPartial()?string(formula.getSummary() + " (partial)").c_str():formula.getSummary().c_str()); results->setItem(row, 4 + dbsearchspecificcolumncount, widgetitemallocator.getNewItem()); - results->item(row, 4 + dbsearchspecificcolumncount)->setData(Qt::DisplayRole, to_string(formula.getMass()).c_str()); + results->item(row, 4 + dbsearchspecificcolumncount)->setData(Qt::DisplayRole, cropPrecisionToSixDecimals(formula.getMass())); results->setItem(row, 5 + dbsearchspecificcolumncount, widgetitemallocator.getNewItem()); results->item(row, 5 + dbsearchspecificcolumncount)->setData(Qt::DisplayRole, getNumberOfBricks(theoreticalspectrum.getCandidate().getComposition())); @@ -413,6 +443,10 @@ void cMainWindow::reportSpectrum(int id, cTheoreticalSpectrum& theoreticalspectr switch (parameters.peptidetype) { case linear: +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: +#endif + case linearpolysaccharide: results->setItem(row, 6 + dbsearchspecificcolumncount, widgetitemallocator.getNewItem()); results->item(row, 6 + dbsearchspecificcolumncount)->setText(parameters.searchedmodifications[theoreticalspectrum.getCandidate().getStartModifID()].name.c_str()); results->setItem(row, 7 + dbsearchspecificcolumncount, widgetitemallocator.getNewItem()); @@ -427,19 +461,16 @@ void cMainWindow::reportSpectrum(int id, cTheoreticalSpectrum& theoreticalspectr results->item(row, 8 + dbsearchspecificcolumncount)->setText(parameters.searchedmodifications[theoreticalspectrum.getCandidate().getEndModifID()].name.c_str()); break; case cyclic: +#if POLYKETIDE_SIDEROPHORES == 1 + case cyclicpolyketide: +#endif results->setItem(row, 6 + dbsearchspecificcolumncount, widgetitemallocator.getNewItem()); results->item(row, 6 + dbsearchspecificcolumncount)->setData(Qt::DisplayRole, theoreticalspectrum.getNumberOfMatchedBricks()); break; - case lasso: + case branchcyclic: results->setItem(row, 6 + dbsearchspecificcolumncount, widgetitemallocator.getNewItem()); results->item(row, 6 + dbsearchspecificcolumncount)->setText(parameters.searchedmodifications[theoreticalspectrum.getCandidate().getMiddleModifID()].name.c_str()); break; - case linearpolysaccharide: - results->setItem(row, 6 + dbsearchspecificcolumncount, widgetitemallocator.getNewItem()); - results->item(row, 6 + dbsearchspecificcolumncount)->setText(parameters.searchedmodifications[theoreticalspectrum.getCandidate().getStartModifID()].name.c_str()); - results->setItem(row, 7 + dbsearchspecificcolumncount, widgetitemallocator.getNewItem()); - results->item(row, 7 + dbsearchspecificcolumncount)->setText(parameters.searchedmodifications[theoreticalspectrum.getCandidate().getEndModifID()].name.c_str()); - break; case other: default: break; @@ -449,10 +480,10 @@ void cMainWindow::reportSpectrum(int id, cTheoreticalSpectrum& theoreticalspectr results->item(row, 6 + dbsearchspecificcolumncount + resultsspecificcolumncount)->setData(Qt::DisplayRole, theoreticalspectrum.getNumberOfMatchedPeaks()); results->setItem(row, 7 + dbsearchspecificcolumncount + resultsspecificcolumncount, widgetitemallocator.getNewItem()); - results->item(row, 7 + dbsearchspecificcolumncount + resultsspecificcolumncount)->setData(Qt::DisplayRole, to_string(theoreticalspectrum.getRatioOfMatchedPeaks()*100).c_str()); + results->item(row, 7 + dbsearchspecificcolumncount + resultsspecificcolumncount)->setData(Qt::DisplayRole, cropPrecisionToSixDecimals(theoreticalspectrum.getRatioOfMatchedPeaks()*100)); results->setItem(row, 8 + dbsearchspecificcolumncount + resultsspecificcolumncount, widgetitemallocator.getNewItem()); - results->item(row, 8 + dbsearchspecificcolumncount + resultsspecificcolumncount)->setData(Qt::DisplayRole, to_string(theoreticalspectrum.getWeightedIntensityScore()).c_str()); + results->item(row, 8 + dbsearchspecificcolumncount + resultsspecificcolumncount)->setData(Qt::DisplayRole, cropPrecisionToSixDecimals(theoreticalspectrum.getWeightedIntensityScore())); for (int i = 0; i < (int)parameters.fragmentionsfortheoreticalspectra.size(); i++) { results->setItem(row, resultsbasecolumncount + dbsearchspecificcolumncount + resultsspecificcolumncount + i, widgetitemallocator.getNewItem()); @@ -479,15 +510,28 @@ void cMainWindow::reportSpectrum(int id, cTheoreticalSpectrum& theoreticalspectr results->item(row, 2)->setData(Qt::DisplayRole, theoreticalspectrum.getNumberOfMatchedPeaks()); results->setItem(row, 3, widgetitemallocator.getNewItem()); - results->item(row, 3)->setData(Qt::DisplayRole, to_string(theoreticalspectrum.getRatioOfMatchedPeaks()*100).c_str()); + results->item(row, 3)->setData(Qt::DisplayRole, cropPrecisionToSixDecimals(theoreticalspectrum.getRatioOfMatchedPeaks()*100)); results->setItem(row, 4, widgetitemallocator.getNewItem()); - results->item(row, 4)->setData(Qt::DisplayRole, to_string(theoreticalspectrum.getWeightedIntensityScore()).c_str()); + results->item(row, 4)->setData(Qt::DisplayRole, cropPrecisionToSixDecimals(theoreticalspectrum.getWeightedIntensityScore())); + + if ((parameters.peaklistfileformat == mis) || (parameters.peaklistfileformat == imzML)) { + results->setItem(row, 5, widgetitemallocator.getNewItem()); + results->item(row, 5)->setData(Qt::DisplayRole, theoreticalspectrum.getExperimentalSpectrum().getCoordinateX()); + + results->setItem(row, 6, widgetitemallocator.getNewItem()); + results->item(row, 6)->setData(Qt::DisplayRole, theoreticalspectrum.getExperimentalSpectrum().getCoordinateY()); + } } - spectradetails[id].initialize(¶meters, theoreticalspectrum); - spectradetails[id].setWindowTitle(("Theoretical Spectrum No. " + to_string(row+1)).c_str()); + spectradetails[id].initialize(¶meters, theoreticalspectrum, this); + if (parameters.mode == dereplication) { + spectradetails[id].setWindowTitle(("Experimental Spectrum No. " + to_string(row+1)).c_str()); + } + else { + spectradetails[id].setWindowTitle(("Theoretical Spectrum No. " + to_string(row+1)).c_str()); + } } @@ -615,14 +659,13 @@ void cMainWindow::enableButtonsHandlingResults(bool enable) { actionSaveResults->setEnabled(enable); actionExportToCsv->setEnabled(enable); actionExportToHTML->setEnabled(enable); + rowsfilterwidget->setEnabled(enable); if (parameters.mode == dereplication) { actionShowIsomers->setEnabled(false); - rowsfilterwidget->setEnabled(false); } else { actionShowIsomers->setEnabled(enable); - rowsfilterwidget->setEnabled(enable); } } @@ -641,20 +684,24 @@ void cMainWindow::reportSpectra() { switch (parameters.peptidetype) { case linear: +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: +#endif + case linearpolysaccharide: resultsspecificcolumncount = 2; break; case branched: resultsspecificcolumncount = 3; break; case cyclic: +#if POLYKETIDE_SIDEROPHORES == 1 + case cyclicpolyketide: +#endif resultsspecificcolumncount = 1; break; - case lasso: + case branchcyclic: resultsspecificcolumncount = 1; break; - case linearpolysaccharide: - resultsspecificcolumncount = 2; - break; case other: default: break; @@ -696,6 +743,7 @@ void cMainWindow::reportSpectra() { results->setHorizontalHeaderItem(4 + dbsearchspecificcolumncount, widgetitemallocator.getNewItem()); results->horizontalHeaderItem(4 + dbsearchspecificcolumncount)->setText("Monoisotopic Mass"); + results->setItemDelegateForColumn(4 + dbsearchspecificcolumncount, &columndelegate); results->setHorizontalHeaderItem(5 + dbsearchspecificcolumncount, widgetitemallocator.getNewItem()); results->horizontalHeaderItem(5 + dbsearchspecificcolumncount)->setText("Number of Bricks"); @@ -703,6 +751,7 @@ void cMainWindow::reportSpectra() { switch (parameters.peptidetype) { case linear: + case linearpolysaccharide: results->setHorizontalHeaderItem(6 + dbsearchspecificcolumncount, widgetitemallocator.getNewItem()); results->horizontalHeaderItem(6 + dbsearchspecificcolumncount)->setText("N-terminal Modification"); results->setHorizontalHeaderItem(7 + dbsearchspecificcolumncount, widgetitemallocator.getNewItem()); @@ -717,19 +766,24 @@ void cMainWindow::reportSpectra() { results->horizontalHeaderItem(8 + dbsearchspecificcolumncount)->setText("C-terminal Modification"); break; case cyclic: +#if POLYKETIDE_SIDEROPHORES == 1 + case cyclicpolyketide: +#endif results->setHorizontalHeaderItem(6 + dbsearchspecificcolumncount, widgetitemallocator.getNewItem()); results->horizontalHeaderItem(6 + dbsearchspecificcolumncount)->setText("Matched Bricks"); break; - case lasso: + case branchcyclic: results->setHorizontalHeaderItem(6 + dbsearchspecificcolumncount, widgetitemallocator.getNewItem()); results->horizontalHeaderItem(6 + dbsearchspecificcolumncount)->setText("Branch Modification"); break; - case linearpolysaccharide: +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: results->setHorizontalHeaderItem(6 + dbsearchspecificcolumncount, widgetitemallocator.getNewItem()); - results->horizontalHeaderItem(6 + dbsearchspecificcolumncount)->setText("N-terminal Modification"); + results->horizontalHeaderItem(6 + dbsearchspecificcolumncount)->setText("Left Terminal Modification"); results->setHorizontalHeaderItem(7 + dbsearchspecificcolumncount, widgetitemallocator.getNewItem()); - results->horizontalHeaderItem(7 + dbsearchspecificcolumncount)->setText("C-terminal Modification"); + results->horizontalHeaderItem(7 + dbsearchspecificcolumncount)->setText("Right Terminal Modification"); break; +#endif case other: default: break; @@ -740,13 +794,15 @@ void cMainWindow::reportSpectra() { results->setHorizontalHeaderItem(7 + dbsearchspecificcolumncount + resultsspecificcolumncount, widgetitemallocator.getNewItem()); results->horizontalHeaderItem(7 + dbsearchspecificcolumncount + resultsspecificcolumncount)->setText("Ratio of Matched Peaks [%]"); + results->setItemDelegateForColumn(7 + dbsearchspecificcolumncount + resultsspecificcolumncount, &columndelegate); results->setHorizontalHeaderItem(8 + dbsearchspecificcolumncount + resultsspecificcolumncount, widgetitemallocator.getNewItem()); results->horizontalHeaderItem(8 + dbsearchspecificcolumncount + resultsspecificcolumncount)->setText("Sum of Relative Intensities"); + results->setItemDelegateForColumn(8 + dbsearchspecificcolumncount + resultsspecificcolumncount, &columndelegate); for (int i = 0; i < (int)parameters.fragmentionsfortheoreticalspectra.size(); i++) { results->setHorizontalHeaderItem(resultsbasecolumncount + dbsearchspecificcolumncount + resultsspecificcolumncount + i, widgetitemallocator.getNewItem()); - results->horizontalHeaderItem(resultsbasecolumncount + dbsearchspecificcolumncount + resultsspecificcolumncount + i)->setText(parameters.fragmentdefinitions[(fragmentIonType)parameters.fragmentionsfortheoreticalspectra[i]].name.c_str()); + results->horizontalHeaderItem(resultsbasecolumncount + dbsearchspecificcolumncount + resultsspecificcolumncount + i)->setText(parameters.fragmentdefinitions[(eFragmentIonType)parameters.fragmentionsfortheoreticalspectra[i]].name.c_str()); } if ((parameters.peptidetype == cyclic) && parameters.enablescrambling) { @@ -758,17 +814,37 @@ void cMainWindow::reportSpectra() { if (parameters.mode == dereplication) { - results->setColumnCount(5); + if ((parameters.peaklistfileformat == mis) || (parameters.peaklistfileformat == imzML)) { + results->setColumnCount(7); + } + else { + results->setColumnCount(5); + } + results->setHorizontalHeaderItem(0, widgetitemallocator.getNewItem()); results->horizontalHeaderItem(0)->setText("*"); + results->setHorizontalHeaderItem(1, widgetitemallocator.getNewItem()); - results->horizontalHeaderItem(1)->setText("Result ID"); + results->horizontalHeaderItem(1)->setText("Spectrum ID"); + results->setHorizontalHeaderItem(2, widgetitemallocator.getNewItem()); results->horizontalHeaderItem(2)->setText("Matched Peaks"); + results->setHorizontalHeaderItem(3, widgetitemallocator.getNewItem()); results->horizontalHeaderItem(3)->setText("Ratio of Matched Peaks [%]"); + results->setItemDelegateForColumn(3, &columndelegate); + results->setHorizontalHeaderItem(4, widgetitemallocator.getNewItem()); results->horizontalHeaderItem(4)->setText("Sum of Relative Intensities"); + results->setItemDelegateForColumn(4, &columndelegate); + + if ((parameters.peaklistfileformat == mis) || (parameters.peaklistfileformat == imzML)) { + results->setHorizontalHeaderItem(5, widgetitemallocator.getNewItem()); + results->horizontalHeaderItem(5)->setText("Coordinate X"); + + results->setHorizontalHeaderItem(6, widgetitemallocator.getNewItem()); + results->horizontalHeaderItem(6)->setText("Coordinate Y"); + } } @@ -882,7 +958,7 @@ void cMainWindow::exportToCsv() { if (!filename.isEmpty()) { lastdirexporttocsv = filename; - QProgressDialog progress("Exporting the CSV file...", /*"Cancel"*/0, 0, results->rowCount(), this); + QProgressDialog progress("Exporting CSV file...", /*"Cancel"*/0, 0, results->rowCount(), this); cEventFilter filter; progress.installEventFilter(&filter); progress.setMinimumDuration(0); @@ -933,12 +1009,16 @@ void cMainWindow::exportToCsv() { void cMainWindow::exportToHTML() { + if (htmlexportdialog->exec() != QDialog::Accepted) { + return; + } + QString filename = QFileDialog::getSaveFileName(this, tr("Export to HTML"), lastdirexporttohtml, tr("HTML Files (*.htm *.html)")); - if (!filename.isEmpty()) { + if (!filename.isEmpty()) { lastdirexporttohtml = filename; - - QProgressDialog progress("Exporting the HTML report...", /*"Cancel"*/0, 0, results->rowCount(), this); + + QProgressDialog progress("Exporting HTML report...", /*"Cancel"*/0, 0, results->rowCount(), this); cEventFilter filter; progress.installEventFilter(&filter); progress.setMinimumDuration(0); @@ -959,83 +1039,187 @@ void cMainWindow::exportToHTML() { out << "\n"; out << "\n"; out << "" << QString(title.c_str()) << "\n"; - out << "\n"; - - out << ""; + + out << "\n"; + out << "\n"; + out << "\n"; + out << ""; out << "\n"; out << "\n"; out << "

" << QString(title.c_str()) << "

\n"; - out << "

Results

\n"; + if (htmlexportdialog->checkboxparameters->isChecked()) { + out << "

Parameters

\n"; + out << "

\n" << parameters.printToString().c_str() << "

\n"; + out << "
\n"; + } - out << "

Hint: Click on a row to expand details about a peptide sequence candidate.

"; + if (htmlexportdialog->checkboxdenovo->isChecked() && (parameters.mode == denovoengine)) { + out << "

De Novo Graph

\n"; + out << "

\n" << graph->getHTML().c_str() << "

\n"; + out << "
\n"; + } - out << "

\n\n"; - for (int i = 0; i < results->columnCount(); i++) { - out << "\n"; + if (htmlexportdialog->checkboxlogwindow->isChecked()) { + out << "

Log Window

\n"; + out << "

\n" << logWindow->toPlainText() << "

\n"; + out << "
\n"; } - out << "\n"; - int spectrumindex; - for (int i = 0; i < results->rowCount(); i++) { + if (htmlexportdialog->checkboxsummaryresultstable->isChecked()) { + out << "

Output Report Table

\n"; - if (results->isRowHidden(i)) { - continue; + out << "

" << results->horizontalHeaderItem(i)->text() << "
\n\n"; + for (int i = 0; i < results->columnCount(); i++) { + out << "\n"; } + out << "\n"; - spectrumindex = results->item(i, 1)->data(Qt::DisplayRole).toInt() - 1; + for (int i = 0; i < results->rowCount(); i++) { - out << "\n"; + if (results->isRowHidden(i)) { + continue; + } + + out << "\n"; - matchedrow = false; - if (results->item(i, 0)->data(Qt::DisplayRole).toString().compare("*") == 0) { - matchedrow = true; + matchedrow = false; + if (results->item(i, 0)->data(Qt::DisplayRole).toString().compare("*") == 0) { + matchedrow = true; + } + + for (int j = 0; j < results->columnCount(); j++) { + out << "" << results->item(i, j)->data(Qt::DisplayRole).toString() << "\n"; + } + + out << "\n"; + } + out << "
" << results->horizontalHeaderItem(i)->text() << "

\n"; + out << "

\n"; + } - for (int j = 0; j < results->columnCount(); j++) { - out << "checkboxsummarypeakstable->isChecked()) { + out << "

Summary Table of Matched Peaks

\n"; + + out << "

"; + + int columncount; + string tdwidth; + if (parameters.mode == dereplication) { + if ((parameters.peaklistfileformat == mis) || (parameters.peaklistfileformat == imzML)) { + columncount = 11; + } + else { + columncount = 9; } - out << ">" << results->item(i, j)->data(Qt::DisplayRole).toString() << "\n"; + } + else { + columncount = 7; } - out << "\n"; + tdwidth = to_string(100/columncount); - out << ""; - out << "

" << spectradetails[spectrumindex].getTheoreticalSpectrum().getCoverageBySeries().c_str(); - out << "
" << spectradetails[spectrumindex].getDetailsAsHTMLString().c_str() << "

\n"; + if (parameters.mode == dereplication) { + if ((parameters.peaklistfileformat == mis) || (parameters.peaklistfileformat == imzML)) { + out << ""; + out << ""; + } + out << ""; + } + else { + out << ""; + } - out << "\n"; + out << ""; + out << ""; + out << ""; + out << ""; + + if (parameters.mode == dereplication) { + out << ""; + out << ""; + out << ""; + } + else { + out << ""; + } + + out << ""; + + int spectrumindex; + for (int i = 0; i < results->rowCount(); i++) { + + if (results->isRowHidden(i)) { + continue; + } + + spectrumindex = results->item(i, 1)->data(Qt::DisplayRole).toInt() - 1; + + if (spectradetails[spectrumindex].getTheoreticalSpectrum().getNumberOfMatchedPeaks() == 0) { + continue; + } + + out << spectradetails[spectrumindex].getPartialPeaksTableAsHTMLString(spectrumindex).c_str(); + + } + + out << "
columnCount(); - out << "\">\n"; + out << "IDCoordinate XCoordinate YIon TypeFragment Type
Theoretical m/zExperimental m/zIntensity [%]Error [ppm]Summary FormulaNameReferenceSequence

"; + out << "

\n"; - progress.setValue(i); - //if (progress.wasCanceled()) { - // break; - //} } - out << "

\n"; - out << "

Parameters

\n"; + if (htmlexportdialog->checkboxdetails->isChecked() || htmlexportdialog->checkboxpeakstable->isChecked()) { + out << "

Individual Rows in Output Report Table

\n"; + + int spectrumindex; + for (int i = 0; i < results->rowCount(); i++) { - out << "

\n" << parameters.printToString().c_str() << "

"; + if (results->isRowHidden(i)) { + continue; + } - if (parameters.mode == denovoengine) { + spectrumindex = results->item(i, 1)->data(Qt::DisplayRole).toInt() - 1; - out << "

De Novo Graph

\n"; + if ((spectradetails[spectrumindex].getTheoreticalSpectrum().getNumberOfMatchedPeaks() == 0) && !htmlexportdialog->checkboxunmatchedtheoretical->isChecked() && !htmlexportdialog->checkboxunmatchedexperimental->isChecked()) { + continue; + } - out << "

\n" << graph->getHTML().c_str() << "

"; + out << "

ID: "; + out << spectrumindex + 1; + out << "

\n"; + if (htmlexportdialog->checkboxpeakstable->isChecked()) { + out << "

"; + out << spectradetails[spectrumindex].getPeaksTableAsHTMLString(htmlexportdialog->checkboxunmatchedtheoretical->isChecked(), htmlexportdialog->checkboxunmatchedexperimental->isChecked()).c_str(); + out << "

\n"; + } + + if (htmlexportdialog->checkboxdetails->isChecked()) { + out << ""; + out << "

"; + out << spectradetails[spectrumindex].getDetailsAsHTMLString().c_str(); + out << spectradetails[spectrumindex].getTheoreticalSpectrum().getCoverageBySeries().c_str(); + out << "

"; + out << "
\n"; + } + + out << "

\n"; + + progress.setValue(i); + //if (progress.wasCanceled()) { + // break; + //} + } + } out << "\n"; @@ -1194,11 +1378,9 @@ void cMainWindow::openResultsFile() { if (parameters.mode == dereplication) { actionShowIsomers->setEnabled(false); - rowsfilterwidget->setEnabled(false); } else { actionShowIsomers->setEnabled(true); - rowsfilterwidget->setEnabled(true); } // load theoretical spectra @@ -1315,6 +1497,16 @@ void cMainWindow::resetFilter() { } +void cMainWindow::gotoNorine() { + QDesktopServices::openUrl(QUrl("http://bioinfo.lifl.fr/norine/")); +} + + +void cMainWindow::gotoSmiles2Monomers() { + QDesktopServices::openUrl(QUrl("http://bioinfo.lifl.fr/norine/smiles2monomers.jsp")); +} + + /* void cMainWindow::showContextMenu(const QPoint &pt) { QMenu *menu = logWindow->createStandardContextMenu(); diff --git a/CycloBranch/gui/cMainWindow.h b/CycloBranch/gui/cMainWindow.h index 259d309..503cac3 100644 --- a/CycloBranch/gui/cMainWindow.h +++ b/CycloBranch/gui/cMainWindow.h @@ -12,6 +12,7 @@ #include #include #include "core/utilities.h" +#include "core/cAllocator.h" #include "core/cTheoreticalSpectrum.h" #include "gui/cAboutWidget.h" #include "gui/cGraphWidget.h" @@ -22,7 +23,8 @@ #include "gui/cModificationsWidget.h" #include "gui/cDrawPeptideWidget.h" #include "gui/cMainThread.h" -#include "core/cAllocator.h" +#include "gui/cDelegate.h" +#include "gui/cHTMLExportDialog.h" // forward declaration @@ -95,6 +97,8 @@ class cMainWindow : public QMainWindow QAction* actionSequenceDatabase; QAction* actionModifications; QAction *actionDrawPeptide; + QAction *actionNorine; + QAction *actionSmilesToMonomers; QAction* actionShowIsomers; QAction* actionGraph; QAction* actionLog; @@ -125,12 +129,14 @@ class cMainWindow : public QMainWindow cModificationsWidget* modificationswidget; cDrawPeptideWidget* drawpeptidewidget; cParametersWidget* parameterswidget; + cHTMLExportDialog* htmlexportdialog; int resultsbasecolumncount; int resultsspecificcolumncount; int dbsearchspecificcolumncount; vector resultsheadersort; + cDelegate columndelegate; QString lastdirexporttocsv; QString lastdirexporttohtml; @@ -218,6 +224,10 @@ private slots: void resetFilter(); + void gotoNorine(); + + void gotoSmiles2Monomers(); + //void showContextMenu(const QPoint &pt); signals: diff --git a/CycloBranch/gui/cModificationsWidget.cpp b/CycloBranch/gui/cModificationsWidget.cpp index 2b3b1f2..2581d28 100644 --- a/CycloBranch/gui/cModificationsWidget.cpp +++ b/CycloBranch/gui/cModificationsWidget.cpp @@ -25,29 +25,41 @@ cModificationsWidget::cModificationsWidget(QWidget* parent) { insertrow = new QPushButton(tr("Add Row")); insertrow->setToolTip("Add a new row."); + insertrow->setShortcut(QKeySequence(Qt::Key_Insert)); + removechecked = new QPushButton(tr(" Remove Rows ")); removechecked->setToolTip("Remove selected rows."); + removechecked->setShortcut(QKeySequence(Qt::Key_Delete)); close = new QPushButton(tr("Close")); close->setToolTip("Close the window."); + load = new QPushButton(tr("Load")); load->setToolTip("Load modifications."); + load->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_L)); + save = new QPushButton(QString(" Save ")); save->setToolTip("Save modifications in the current file. When a file has not been loaded yet, the \"Save As ...\" file dialog is opened."); + saveas = new QPushButton(tr("Save As...")); saveas->setToolTip("Save modifications into a file."); + saveas->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_D)); rowsfilterline = new QLineEdit(); rowsfilterline->setMinimumWidth(250); rowsfilterline->setToolTip("Text to Find"); + rowsfiltercasesensitive = new QCheckBox(); rowsfiltercasesensitive->setToolTip("Case Sensitive"); + rowsfilterbutton = new QPushButton("Filter"); rowsfilterbutton->setToolTip("Filter Search Results"); rowsfilterbutton->setMinimumWidth(50); + rowsfilterclearbutton = new QPushButton("Clear"); rowsfilterclearbutton->setToolTip("Clear Form and Reset Search Results"); rowsfilterclearbutton->setMinimumWidth(50); + rowsfilterclearbutton->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R)); rowsfilterhbox = new QHBoxLayout(); rowsfilterhbox->addWidget(rowsfilterline); @@ -80,6 +92,7 @@ cModificationsWidget::cModificationsWidget(QWidget* parent) { database->setHorizontalHeaderItem(1, new QTableWidgetItem("Name")); database->setHorizontalHeaderItem(2, new QTableWidgetItem("Summary Formula")); database->setHorizontalHeaderItem(3, new QTableWidgetItem("Monoisotopic Mass")); + database->setItemDelegateForColumn(3, &columndelegate); database->setHorizontalHeaderItem(4, new QTableWidgetItem("N-terminal")); database->setHorizontalHeaderItem(5, new QTableWidgetItem("C-terminal")); database->horizontalHeader()->setStretchLastSection(true); @@ -235,7 +248,7 @@ bool cModificationsWidget::checkFormula(int row, const string& summary) { return false; } if (database->item(row, 3)) { - database->item(row, 3)->setData(Qt::DisplayRole, to_string(formula.getMass()).c_str()); + database->item(row, 3)->setData(Qt::DisplayRole, cropPrecisionToSixDecimals(formula.getMass())); } return true; } @@ -269,6 +282,26 @@ void cModificationsWidget::keyPressEvent(QKeyEvent *event) { filterRows(); } } + + if (event->key() == Qt::Key_F1) { + #if OS_TYPE == WIN + QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo("docs/html/modificationseditor.html").absoluteFilePath())); + #else + QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(installdir + "docs/html/modificationseditor.html").absoluteFilePath())); + #endif + } + + if ((event->modifiers() == Qt::ControlModifier) && (event->key() == Qt::Key_F)) { + rowsfilterline->setFocus(); + } + + if ((event->modifiers() == Qt::ControlModifier) && (event->key() == Qt::Key_N)) { + rowsfiltercasesensitive->setChecked(!rowsfiltercasesensitive->isChecked()); + } + + if ((event->modifiers() == Qt::ControlModifier) && (event->key() == Qt::Key_S)) { + saveDatabase(); + } } @@ -330,7 +363,7 @@ void cModificationsWidget::loadDatabase() { database->item(i, 2)->setText(modifications[i].summary.c_str()); database->setItem(i, 3, widgetitemallocator.getNewItem()); - database->item(i, 3)->setData(Qt::DisplayRole, to_string(modifications[i].massdifference).c_str()); + database->item(i, 3)->setData(Qt::DisplayRole, cropPrecisionToSixDecimals(modifications[i].massdifference)); checkbox = new QCheckBox(); connect(checkbox, SIGNAL(stateChanged(int)), this, SLOT(checkBoxModified(int))); diff --git a/CycloBranch/gui/cModificationsWidget.h b/CycloBranch/gui/cModificationsWidget.h index ff9fd69..39a4be5 100644 --- a/CycloBranch/gui/cModificationsWidget.h +++ b/CycloBranch/gui/cModificationsWidget.h @@ -8,11 +8,16 @@ #define _CMODIFICATIONSWIDGET_H #include +#include +#include +#include #include #include "core/utilities.h" #include "core/cFragmentIons.h" #include "core/cSummaryFormula.h" #include "core/cAllocator.h" +#include "gui/cDelegate.h" + using namespace std; @@ -85,6 +90,7 @@ class cModificationsWidget : public QWidget vector modifications; vector headersort; + cDelegate columndelegate; cAllocator widgetitemallocator; diff --git a/CycloBranch/gui/cParametersWidget.cpp b/CycloBranch/gui/cParametersWidget.cpp index dff9ca3..9a95e53 100644 --- a/CycloBranch/gui/cParametersWidget.cpp +++ b/CycloBranch/gui/cParametersWidget.cpp @@ -35,12 +35,17 @@ cParametersWidget::cParametersWidget(QWidget* parent) { stdbuttons->button(QDialogButtonBox::Ok)->setToolTip("Accept changes and hide window."); stdbuttons->button(QDialogButtonBox::Apply)->setToolTip("Accept changes and keep window opened."); stdbuttons->button(QDialogButtonBox::Cancel)->setToolTip("Drop changes and hide window."); + load = new QPushButton(tr("Load")); load->setToolTip("Load settings from a file (*.ini)."); + load->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_L)); + save = new QPushButton(QString("Save")); save->setToolTip("Save settings in the current file (*.ini). When a file has not been loaded yet, the \"Save As ...\" file dialog is opened."); + saveas = new QPushButton(tr("Save As...")); saveas->setToolTip("Save settings into a file (*.ini)."); + saveas->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_D)); buttons = new QHBoxLayout(); buttons->addWidget(stdbuttons); @@ -58,6 +63,10 @@ cParametersWidget::cParametersWidget(QWidget* parent) { peptidetype->addItem(tr("Cyclic")); peptidetype->addItem(tr("Branched")); peptidetype->addItem(tr("Branch-cyclic")); +#if POLYKETIDE_SIDEROPHORES == 1 + peptidetype->addItem(tr("Linear oligoketide siderophore")); + peptidetype->addItem(tr("Cyclic oligoketide siderophore")); +#endif peptidetype->addItem(tr("Linear polysaccharide (beta version)")); //peptidetype->addItem(tr("Other")); peaklistformlayout->addRow(tr("Peptide Type: "), peptidetype); @@ -65,12 +74,13 @@ cParametersWidget::cParametersWidget(QWidget* parent) { peaklistline = new QLineEdit(); peaklistbutton = new QPushButton("Select"); #if OS_TYPE != WIN - peaklistline->setToolTip("Select the peaklist. Following formats are supported: txt, mgf, mzML, mzXML."); - peaklistbutton->setToolTip("Select the peaklist. Following formats are supported: txt, mgf, mzML, mzXML."); + peaklistline->setToolTip("Select the peaklist. Following formats are supported: txt, mgf, mzML, mzXML, imzML."); + peaklistbutton->setToolTip("Select the peaklist. Following formats are supported: txt, mgf, mzML, mzXML, imzML."); #else - peaklistline->setToolTip("Select the peaklist. Following formats are supported: txt, mgf, mzML, mzXML, baf."); - peaklistbutton->setToolTip("Select the peaklist. Following formats are supported: txt, mgf, mzML, mzXML, baf."); + peaklistline->setToolTip("Select the peaklist. Following formats are supported: txt, mgf, mzML, mzXML, baf, mis, imzML."); + peaklistbutton->setToolTip("Select the peaklist. Following formats are supported: txt, mgf, mzML, mzXML, baf, mis, imzML."); #endif + peaklistbutton->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_P)); peaklistlayout = new QHBoxLayout(); peaklistlayout->addWidget(peaklistline); peaklistlayout->addWidget(peaklistbutton); @@ -102,20 +112,20 @@ cParametersWidget::cParametersWidget(QWidget* parent) { peaklistformlayout->addRow(tr("Precursor m/z Error Tolerance: "), precursormasserrortolerance); fragmentmasserrortolerance = new QDoubleSpinBox(); - fragmentmasserrortolerance->setToolTip("Enter the fragment m/z error tolerance in ppm."); + fragmentmasserrortolerance->setToolTip("Enter the m/z error tolerance in MS mode or the fragment m/z error tolerance in MS/MS mode [ppm]."); fragmentmasserrortolerance->setDecimals(3); fragmentmasserrortolerance->setRange(0, 10000); fragmentmasserrortolerance->setSingleStep(1); fragmentmasserrortolerance->setSuffix(" ppm"); - peaklistformlayout->addRow(tr("Fragment m/z Error Tolerance: "), fragmentmasserrortolerance); + peaklistformlayout->addRow(tr("m/z Error Tolerance: "), fragmentmasserrortolerance); masserrortolerancefordeisotoping = new QDoubleSpinBox(); - masserrortolerancefordeisotoping->setToolTip("Enter the fragment m/z error tolerance for deisotoping in ppm (the same value like \"Fragment m/z Error Tolerance\" is recommended by default; 0 = the deisotoping is disabled)."); + masserrortolerancefordeisotoping->setToolTip("Enter the m/z error tolerance for deisotoping in MS mode or the fragment m/z error tolerance for deisotoping in MS/MS mode [ppm]\n(the same value like \"m/z Error Tolerance\" is recommended by default; 0 = the deisotoping is disabled)."); masserrortolerancefordeisotoping->setDecimals(3); masserrortolerancefordeisotoping->setRange(0, 10000); masserrortolerancefordeisotoping->setSingleStep(1); masserrortolerancefordeisotoping->setSuffix(" ppm"); - peaklistformlayout->addRow(tr("Fragment m/z Error Tolerance for Deisotoping: "), masserrortolerancefordeisotoping); + peaklistformlayout->addRow(tr("m/z Error Tolerance for Deisotoping: "), masserrortolerancefordeisotoping); minimumrelativeintensitythreshold = new QDoubleSpinBox(); minimumrelativeintensitythreshold->setToolTip("Enter the threshold of relative intensity in %. Peaks having relative intensities below the threshold will be removed from the peaklist."); @@ -141,6 +151,7 @@ cParametersWidget::cParametersWidget(QWidget* parent) { brickdatabaseline->setToolTip("Select the txt file containing a list of building blocks."); brickdatabasebutton = new QPushButton("Select"); brickdatabasebutton->setToolTip("Select the txt file containing a list of building blocks."); + brickdatabasebutton->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_B)); brickdatabaselayout = new QHBoxLayout(); brickdatabaselayout->addWidget(brickdatabaseline); brickdatabaselayout->addWidget(brickdatabasebutton); @@ -179,6 +190,7 @@ cParametersWidget::cParametersWidget(QWidget* parent) { modificationsline->setToolTip("Select the txt file containing a list of modifications."); modificationsbutton = new QPushButton("Select"); modificationsbutton->setToolTip("Select the txt file containing a list of modifications."); + modificationsbutton->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_M)); modificationslayout = new QHBoxLayout(); modificationslayout->addWidget(modificationsline); modificationslayout->addWidget(modificationsbutton); @@ -231,6 +243,7 @@ cParametersWidget::cParametersWidget(QWidget* parent) { sequencedatabaseline->setToolTip("Select the txt file containing a database of sequences."); sequencedatabasebutton = new QPushButton("Select"); sequencedatabasebutton->setToolTip("Select the txt file containing a database of sequences."); + sequencedatabasebutton->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_T)); sequencedatabaselayout = new QHBoxLayout(); sequencedatabaselayout->addWidget(sequencedatabaseline); sequencedatabaselayout->addWidget(sequencedatabasebutton); @@ -266,8 +279,8 @@ cParametersWidget::cParametersWidget(QWidget* parent) { applicationformlayout->addRow(tr("Peptide Sequence Tag: "), sequencetag); fragmentiontypes = new cFragmentIonsListWidget(this); - fragmentiontypes->setToolTip("Select fragment ion types which will be generated in theoretical spectra of peptide sequence candidates."); - applicationformlayout->addRow(tr("Fragment Ion Types in Theoretical Spectra: "), fragmentiontypes); + fragmentiontypes->setToolTip("Select ion types which will be generated in theoretical spectra."); + applicationformlayout->addRow(tr("Ion Types in Theoretical Spectra: "), fragmentiontypes); clearhitswithoutparent = new QCheckBox(); clearhitswithoutparent->setToolTip("When checked, a hit of a peak is not considered when corresponding parent peak is not hit (e.g., a hit of a dehydrated b-ion is not considered as a hit when corresponding b-ion has not been hit)."); @@ -285,6 +298,7 @@ cParametersWidget::cParametersWidget(QWidget* parent) { searchedsequencebutton = new QPushButton("Edit"); searchedsequencebutton->setMinimumWidth(50); searchedsequencebutton->setToolTip("Edit the sequence using the 'Draw Peptide' tool."); + searchedsequencebutton->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E)); searchedsequencelayout = new QHBoxLayout(); searchedsequencelayout->addWidget(searchedsequenceline); searchedsequencelayout->addWidget(searchedsequencebutton); @@ -314,7 +328,6 @@ cParametersWidget::cParametersWidget(QWidget* parent) { vlayout2 = new QVBoxLayout(); vlayout2->addWidget(applicationgroupbox); vlayout2->addWidget(searchedsequencegroupbox); - hlayout = new QHBoxLayout(); hlayout->setContentsMargins(0, 0, 0, 0); @@ -322,10 +335,11 @@ cParametersWidget::cParametersWidget(QWidget* parent) { hlayout->addLayout(vlayout2); hlayoutwidget = new QWidget(); + hlayoutwidget->setMinimumWidth(1250); hlayoutwidget->setLayout(hlayout); hlayoutscroll = new QScrollArea(); - hlayoutscroll->setWidgetResizable(true); + hlayoutscroll->setWidgetResizable(false); hlayoutscroll->setFrameShape(QFrame::NoFrame); hlayoutscroll->setWidget(hlayoutwidget); @@ -466,7 +480,7 @@ void cParametersWidget::setAndRestoreParameters(cParameters& parameters) { this->parameters = parameters; this->parameters.bricksdatabase.clear(); - this->parameters.peaklist.clear(); + this->parameters.peaklistseries.clear(); this->parameters.fragmentionsfordenovograph.clear(); this->parameters.sequencetag = this->parameters.originalsequencetag; this->parameters.searchedsequence = this->parameters.originalsearchedsequence; @@ -496,6 +510,22 @@ void cParametersWidget::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Escape) { restoreParameters(); } + + if ((event->key() == Qt::Key_Enter) || (event->key() == Qt::Key_Return)) { + updateParametersAndHide(); + } + + if ((event->modifiers() == Qt::ControlModifier) && (event->key() == Qt::Key_S)) { + saveSettings(); + } + + if (event->key() == Qt::Key_F1) { + #if OS_TYPE == WIN + QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo("docs/html/basicconfiguration.html").absoluteFilePath())); + #else + QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(installdir + "docs/html/basicconfiguration.html").absoluteFilePath())); + #endif + } } @@ -634,9 +664,9 @@ void cParametersWidget::saveSettingsAs() { void cParametersWidget::peaklistButtonReleased() { #if OS_TYPE != WIN - QString filename = QFileDialog::getOpenFileName(this, tr("Select Peaklist..."), lastdirselectpeaklist, tr("Peak Lists (*.txt *.mgf *.mzML *.mzXML)")); + QString filename = QFileDialog::getOpenFileName(this, tr("Select Peaklist..."), lastdirselectpeaklist, tr("Peak Lists (*.txt *.mgf *.mzML *.mzXML *.imzML)")); #else - QString filename = QFileDialog::getOpenFileName(this, tr("Select Peaklist..."), lastdirselectpeaklist, tr("Peak Lists (*.txt *.mgf *.mzML *.mzXML *.baf)")); + QString filename = QFileDialog::getOpenFileName(this, tr("Select Peaklist..."), lastdirselectpeaklist, tr("Peak Lists (*.txt *.mgf *.mzML *.mzXML *.baf *.mis *.imzML)")); #endif if (!filename.isEmpty()) { @@ -687,14 +717,14 @@ bool cParametersWidget::updateParameters() { return false; } - if ((brickdatabaseline->text().toStdString().compare("") == 0) && (((modeType)mode->currentIndex() == denovoengine) || ((modeType)mode->currentIndex() == singlecomparison) || ((modeType)mode->currentIndex() == databasesearch))) { + if ((brickdatabaseline->text().toStdString().compare("") == 0) && (((eModeType)mode->currentIndex() == denovoengine) || ((eModeType)mode->currentIndex() == singlecomparison) || ((eModeType)mode->currentIndex() == databasesearch))) { errstr = "A database of building blocks must be specified!"; msgBox.setText(errstr); msgBox.exec(); return false; } - if ((sequencedatabaseline->text().toStdString().compare("") == 0) && (((modeType)mode->currentIndex() == databasesearch) || ((modeType)mode->currentIndex() == dereplication))) { + if ((sequencedatabaseline->text().toStdString().compare("") == 0) && (((eModeType)mode->currentIndex() == databasesearch) || ((eModeType)mode->currentIndex() == dereplication))) { errstr = "A sequence database must be specified!"; msgBox.setText(errstr); msgBox.exec(); @@ -709,7 +739,7 @@ bool cParametersWidget::updateParameters() { } /* - if ((maximumbricksincombinationmiddle->value() < 2) && ((modeType)mode->currentIndex() == denovoengine) && (((peptideType)peptidetype->currentIndex() == branched) || ((peptideType)peptidetype->currentIndex() == lasso))) { + if ((maximumbricksincombinationmiddle->value() < 2) && ((eModeType)mode->currentIndex() == denovoengine) && (((ePeptideType)peptidetype->currentIndex() == branched) || ((ePeptideType)peptidetype->currentIndex() == branchcyclic))) { errstr = "'Maximum Number of Combined Blocks (middle)' must be at least 2 when a branched or a branch-cyclic peptide is searched! (One block represents a branched residue, the other block(s) corresponds to a branch.)"; msgBox.setText(errstr); msgBox.exec(); @@ -717,7 +747,7 @@ bool cParametersWidget::updateParameters() { } */ - parameters.peptidetype = (peptideType)peptidetype->currentIndex(); + parameters.peptidetype = (ePeptideType)peptidetype->currentIndex(); parameters.peaklistfilename = peaklistline->text().toStdString(); parameters.precursormass = precursormass->value(); parameters.precursoradduct = precursoradduct->text().toStdString(); @@ -743,10 +773,10 @@ bool cParametersWidget::updateParameters() { parameters.enablescrambling = enablescrambling->isChecked(); parameters.similaritysearch = similaritysearch->isChecked(); - parameters.mode = (modeType)mode->currentIndex(); + parameters.mode = (eModeType)mode->currentIndex(); parameters.sequencedatabasefilename = sequencedatabaseline->text().toStdString(); parameters.maximumnumberofthreads = maximumnumberofthreads->value(); - parameters.scoretype = (scoreType)scoretype->currentIndex(); + parameters.scoretype = (eScoreType)scoretype->currentIndex(); parameters.hitsreported = hitsreported->value(); parameters.sequencetag = sequencetag->text().toStdString(); parameters.originalsequencetag = parameters.sequencetag; @@ -754,22 +784,26 @@ bool cParametersWidget::updateParameters() { parameters.fragmentionsfortheoreticalspectra.clear(); int start; - if ((modeType)mode->currentIndex() == dereplication) { + if ((eModeType)mode->currentIndex() == dereplication) { start = ms_hplus; } else { - switch ((peptideType)peptidetype->currentIndex()) + switch ((ePeptideType)peptidetype->currentIndex()) { case linear: case branched: - start = a_ion; - break; case cyclic: + case branchcyclic: start = a_ion; break; - case lasso: - start = a_ion; +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + start = l1h_ion; + break; + case cyclicpolyketide: + start = l0h_ion; break; +#endif case linearpolysaccharide: start = ms_nterminal_ion_hplus; break; @@ -781,7 +815,7 @@ bool cParametersWidget::updateParameters() { for (int i = 0; i < fragmentiontypes->getList()->count(); i++) { if (fragmentiontypes->getList()->item(i)->isSelected()) { - parameters.fragmentionsfortheoreticalspectra.push_back((fragmentIonType)(i + start)); + parameters.fragmentionsfortheoreticalspectra.push_back((eFragmentIonType)(i + start)); } } @@ -850,14 +884,18 @@ void cParametersWidget::restoreParameters() { { case linear: case branched: - start = a_ion; - break; case cyclic: + case branchcyclic: start = a_ion; break; - case lasso: - start = a_ion; +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + start = l1h_ion; break; + case cyclicpolyketide: + start = l0h_ion; + break; +#endif case linearpolysaccharide: start = ms_nterminal_ion_hplus; break; @@ -894,7 +932,7 @@ void cParametersWidget::updateSettingsWhenPeptideTypeChanged(int index) { resetFragmentIonTypes(); - switch ((peptideType)index) + switch ((ePeptideType)index) { case linear: modificationsline->setDisabled(false); @@ -926,7 +964,7 @@ void cParametersWidget::updateSettingsWhenPeptideTypeChanged(int index) { searchedsequenceCtermmodif->setDisabled(false); searchedsequenceTmodif->setDisabled(false); break; - case lasso: + case branchcyclic: modificationsline->setDisabled(false); modificationsbutton->setDisabled(false); cyclicnterminus->setDisabled(true); @@ -936,6 +974,28 @@ void cParametersWidget::updateSettingsWhenPeptideTypeChanged(int index) { searchedsequenceCtermmodif->setDisabled(true); searchedsequenceTmodif->setDisabled(false); break; +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + modificationsline->setDisabled(false); + modificationsbutton->setDisabled(false); + cyclicnterminus->setDisabled(true); + cycliccterminus->setDisabled(true); + enablescrambling->setDisabled(true); + searchedsequenceNtermmodif->setDisabled(false); + searchedsequenceCtermmodif->setDisabled(false); + searchedsequenceTmodif->setDisabled(true); + break; + case cyclicpolyketide: + modificationsline->setDisabled(true); + modificationsbutton->setDisabled(true); + cyclicnterminus->setDisabled(true); + cycliccterminus->setDisabled(true); + enablescrambling->setDisabled(true); + searchedsequenceNtermmodif->setDisabled(true); + searchedsequenceCtermmodif->setDisabled(true); + searchedsequenceTmodif->setDisabled(true); + break; +#endif case linearpolysaccharide: modificationsline->setDisabled(false); modificationsbutton->setDisabled(false); @@ -955,7 +1015,7 @@ void cParametersWidget::updateSettingsWhenPeptideTypeChanged(int index) { void cParametersWidget::updateSettingsWhenModeChanged(int index) { - switch ((modeType)index) + switch ((eModeType)index) { case denovoengine: peptidetype->setDisabled(false); @@ -1095,15 +1155,14 @@ void cParametersWidget::updateSettingsWhenModeChanged(int index) { void cParametersWidget::resetFragmentIonTypes() { fragmentiontypes->getList()->clear(); - fragmentIonType start, end; - - - if ((modeType)mode->currentIndex() == dereplication) { + eFragmentIonType start, end; + + if ((eModeType)mode->currentIndex() == dereplication) { start = ms_hplus; - end = ms_MFe4H; + end = ms_MGa4H; } else { - switch ((peptideType)peptidetype->currentIndex()) { + switch ((ePeptideType)peptidetype->currentIndex()) { case linear: case branched: start = a_ion; @@ -1113,10 +1172,20 @@ void cParametersWidget::resetFragmentIonTypes() { start = a_ion; end = c_ion_dehydrated_and_deamidated; break; - case lasso: + case branchcyclic: start = a_ion; end = z_ion_dehydrated_and_deamidated; break; +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + start = l1h_ion; + end = r2oh_ion_co_loss_dehydrated_and_deamidated; + break; + case cyclicpolyketide: + start = l0h_ion; + end = l2h_ion_co_loss_dehydrated_and_deamidated; + break; +#endif case linearpolysaccharide: start = ms_nterminal_ion_hplus; end = ms_cterminal_ion_kplus; @@ -1129,33 +1198,47 @@ void cParametersWidget::resetFragmentIonTypes() { for (int i = (int)start; i <= (int)end; i++) { - fragmentiontypes->getList()->addItem(tr(parameters.fragmentdefinitions[(fragmentIonType)i].name.c_str())); + fragmentiontypes->getList()->addItem(tr(parameters.fragmentdefinitions[(eFragmentIonType)i].name.c_str())); - if ((modeType)mode->currentIndex() == dereplication) { - if ((fragmentIonType)i == ms_hplus) { + if ((eModeType)mode->currentIndex() == dereplication) { + if ((eFragmentIonType)i == ms_hplus) { fragmentiontypes->getList()->item(i-start)->setSelected(true); } } else { - switch ((peptideType)peptidetype->currentIndex()) { + switch ((ePeptideType)peptidetype->currentIndex()) { case linear: case branched: - if (((fragmentIonType)i == b_ion) || ((fragmentIonType)i == y_ion)) { + if (((eFragmentIonType)i == b_ion) || ((eFragmentIonType)i == y_ion)) { fragmentiontypes->getList()->item(i-start)->setSelected(true); } break; case cyclic: - if ((fragmentIonType)i == b_ion) { + if ((eFragmentIonType)i == b_ion) { + fragmentiontypes->getList()->item(i-start)->setSelected(true); + } + break; + case branchcyclic: + if (((eFragmentIonType)i == b_ion) || ((eFragmentIonType)i == y_ion)) { + fragmentiontypes->getList()->item(i-start)->setSelected(true); + } + break; +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + if (((eFragmentIonType)i == l1h_ion) || ((eFragmentIonType)i == l2h_ion) || ((eFragmentIonType)i == r1h_ion) || ((eFragmentIonType)i == r2h_ion) || + ((eFragmentIonType)i == l1oh_ion) || ((eFragmentIonType)i == l2oh_ion) || ((eFragmentIonType)i == r1oh_ion) || ((eFragmentIonType)i == r2oh_ion) + ) { fragmentiontypes->getList()->item(i-start)->setSelected(true); } break; - case lasso: - if (((fragmentIonType)i == b_ion) || ((fragmentIonType)i == y_ion)) { + case cyclicpolyketide: + if (((eFragmentIonType)i == l0h_ion) || ((eFragmentIonType)i == l1h_ion) || ((eFragmentIonType)i == l2h_ion)) { fragmentiontypes->getList()->item(i-start)->setSelected(true); } break; +#endif case linearpolysaccharide: - if (((fragmentIonType)i == ms_nterminal_ion_hplus) || ((fragmentIonType)i == ms_cterminal_ion_hplus)) { + if (((eFragmentIonType)i == ms_nterminal_ion_hplus) || ((eFragmentIonType)i == ms_cterminal_ion_hplus)) { fragmentiontypes->getList()->item(i-start)->setSelected(true); } break; diff --git a/CycloBranch/gui/cParametersWidget.h b/CycloBranch/gui/cParametersWidget.h index 46bff4d..c4f15c5 100644 --- a/CycloBranch/gui/cParametersWidget.h +++ b/CycloBranch/gui/cParametersWidget.h @@ -11,6 +11,9 @@ #include #include +#include +#include +#include #include #include diff --git a/CycloBranch/gui/cSequenceDatabaseWidget.cpp b/CycloBranch/gui/cSequenceDatabaseWidget.cpp index 963bf63..5760ddb 100644 --- a/CycloBranch/gui/cSequenceDatabaseWidget.cpp +++ b/CycloBranch/gui/cSequenceDatabaseWidget.cpp @@ -27,29 +27,41 @@ cSequenceDatabaseWidget::cSequenceDatabaseWidget(QWidget* parent) { insertrow = new QPushButton(tr("Add Row")); insertrow->setToolTip("Add a new row."); + insertrow->setShortcut(QKeySequence(Qt::Key_Insert)); + removechecked = new QPushButton(tr(" Remove Rows ")); removechecked->setToolTip("Remove selected rows."); + removechecked->setShortcut(QKeySequence(Qt::Key_Delete)); close = new QPushButton(tr("Close")); close->setToolTip("Close the window."); + load = new QPushButton(tr("Load")); load->setToolTip("Load the database of sequences."); + load->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_L)); + save = new QPushButton(QString(" Save ")); save->setToolTip("Save the database of sequences in the current file. When a file has not been loaded yet, the \"Save As ...\" file dialog is opened."); + saveas = new QPushButton(tr("Save As...")); saveas->setToolTip("Save the database of sequences into a file."); + saveas->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_D)); rowsfilterline = new QLineEdit(); rowsfilterline->setMinimumWidth(250); rowsfilterline->setToolTip("Text to Find"); + rowsfiltercasesensitive = new QCheckBox(); rowsfiltercasesensitive->setToolTip("Case Sensitive"); + rowsfilterbutton = new QPushButton("Filter"); rowsfilterbutton->setToolTip("Filter Search Results"); rowsfilterbutton->setMinimumWidth(50); + rowsfilterclearbutton = new QPushButton("Clear"); rowsfilterclearbutton->setToolTip("Clear Form and Reset Search Results"); rowsfilterclearbutton->setMinimumWidth(50); + rowsfilterclearbutton->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R)); rowsfilterhbox = new QHBoxLayout(); rowsfilterhbox->addWidget(rowsfilterline); @@ -83,6 +95,7 @@ cSequenceDatabaseWidget::cSequenceDatabaseWidget(QWidget* parent) { database->setHorizontalHeaderItem(2, new QTableWidgetItem("Name")); database->setHorizontalHeaderItem(3, new QTableWidgetItem("Summary Formula")); database->setHorizontalHeaderItem(4, new QTableWidgetItem("Monoisotopic Mass")); + database->setItemDelegateForColumn(4, &columndelegate); database->setHorizontalHeaderItem(5, new QTableWidgetItem("Sequence")); database->setHorizontalHeaderItem(6, new QTableWidgetItem("N-terminal Modification")); database->setHorizontalHeaderItem(7, new QTableWidgetItem("C-terminal Modification")); @@ -262,7 +275,7 @@ bool cSequenceDatabaseWidget::checkFormula(int row, const string& summary) { return false; } if (database->item(row, 4)) { - database->item(row, 4)->setData(Qt::DisplayRole, to_string(formula.getMass()).c_str()); + database->item(row, 4)->setData(Qt::DisplayRole, cropPrecisionToSixDecimals(formula.getMass())); } return true; } @@ -276,19 +289,25 @@ bool cSequenceDatabaseWidget::checkSequence(int row) { regex rx; // [^\\[\\]]+ is used instead of .+ to prevent from a too complex regex error - switch ((peptideType)((QComboBox *)database->cellWidget(row, 1))->currentIndex()) + switch ((ePeptideType)((QComboBox *)database->cellWidget(row, 1))->currentIndex()) { case linear: case linearpolysaccharide: +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: +#endif rx = "^\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])*$"; break; case cyclic: +#if POLYKETIDE_SIDEROPHORES == 1 + case cyclicpolyketide: +#endif rx = "^\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])+$"; break; case branched: rx = "^\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])*\\\\\\(\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])+\\\\\\)\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])*$"; break; - case lasso: + case branchcyclic: rx = "(^(\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])*)?\\\\\\(\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])+\\\\\\)\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])*$|^\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])*\\\\\\(\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])+\\\\\\)(\\[[^\\[\\]]+\\](-\\[[^\\[\\]]+\\])*)?$)"; break; case other: @@ -305,7 +324,7 @@ bool cSequenceDatabaseWidget::checkSequence(int row) { errstr += ". The format of sequence '"; errstr += database->item(row, 5)->text().toStdString().c_str(); errstr += "' does not correspond to the sequence type '"; - errstr += getStringFromPeptideType((peptideType)((QComboBox *)database->cellWidget(row, 1))->currentIndex()).c_str(); + errstr += getStringFromPeptideType((ePeptideType)((QComboBox *)database->cellWidget(row, 1))->currentIndex()).c_str(); errstr += "'."; msgBox.setText(errstr); msgBox.exec(); @@ -358,6 +377,26 @@ void cSequenceDatabaseWidget::keyPressEvent(QKeyEvent *event) { filterRows(); } } + + if (event->key() == Qt::Key_F1) { + #if OS_TYPE == WIN + QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo("docs/html/sequenceeditor.html").absoluteFilePath())); + #else + QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(installdir + "docs/html/sequenceeditor.html").absoluteFilePath())); + #endif + } + + if ((event->modifiers() == Qt::ControlModifier) && (event->key() == Qt::Key_F)) { + rowsfilterline->setFocus(); + } + + if ((event->modifiers() == Qt::ControlModifier) && (event->key() == Qt::Key_N)) { + rowsfiltercasesensitive->setChecked(!rowsfiltercasesensitive->isChecked()); + } + + if ((event->modifiers() == Qt::ControlModifier) && (event->key() == Qt::Key_S)) { + saveDatabase(); + } } @@ -414,7 +453,7 @@ void cSequenceDatabaseWidget::loadDatabase() { QComboBox* combo = new QComboBox(); for (int j = 0; j <= (int)other; j++) { - combo->addItem(QString(getStringFromPeptideType((peptideType)j).c_str())); + combo->addItem(QString(getStringFromPeptideType((ePeptideType)j).c_str())); } combo->setCurrentIndex((int)sequences[i].getPeptideType()); connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(comboBoxModified(int))); @@ -428,7 +467,7 @@ void cSequenceDatabaseWidget::loadDatabase() { formula.setFormula(sequences[i].getSummaryFormula()); database->setItem(i, 4, widgetitemallocator.getNewItem()); - database->item(i, 4)->setData(Qt::DisplayRole, to_string(formula.getMass()).c_str()); + database->item(i, 4)->setData(Qt::DisplayRole, cropPrecisionToSixDecimals(formula.getMass())); database->setItem(i, 5, widgetitemallocator.getNewItem()); database->item(i, 5)->setText(sequences[i].getSequence().c_str()); @@ -518,7 +557,7 @@ bool cSequenceDatabaseWidget::saveDatabase() { // nothing to do break; case 1: - seq.setPeptideType((peptideType)(((QComboBox *)database->cellWidget(i,j))->currentIndex())); + seq.setPeptideType((ePeptideType)(((QComboBox *)database->cellWidget(i,j))->currentIndex())); break; case 2: s = database->item(i,j)->text().toStdString(); @@ -606,7 +645,7 @@ void cSequenceDatabaseWidget::addRow() { QComboBox* combo = new QComboBox(); for (int i = 0; i <= (int)other; i++) { - combo->addItem(QString(getStringFromPeptideType((peptideType)i).c_str())); + combo->addItem(QString(getStringFromPeptideType((ePeptideType)i).c_str())); } combo->setCurrentIndex((int)other); connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(comboBoxModified(int))); diff --git a/CycloBranch/gui/cSequenceDatabaseWidget.h b/CycloBranch/gui/cSequenceDatabaseWidget.h index e206bcb..01c2881 100644 --- a/CycloBranch/gui/cSequenceDatabaseWidget.h +++ b/CycloBranch/gui/cSequenceDatabaseWidget.h @@ -8,10 +8,15 @@ #define _CSEQUENCEDATABASEWIDGET_H #include +#include +#include +#include #include #include "core/utilities.h" #include "core/cSequenceDatabase.h" #include "core/cAllocator.h" +#include "gui/cDelegate.h" + using namespace std; using namespace boost; @@ -93,6 +98,7 @@ class cSequenceDatabaseWidget : public QWidget cSequenceDatabase sequences; vector headersort; + cDelegate columndelegate; cAllocator widgetitemallocator; diff --git a/CycloBranch/gui/cSpectrumDetailWidget.cpp b/CycloBranch/gui/cSpectrumDetailWidget.cpp index 875e075..76268a6 100644 --- a/CycloBranch/gui/cSpectrumDetailWidget.cpp +++ b/CycloBranch/gui/cSpectrumDetailWidget.cpp @@ -1,6 +1,5 @@ #include "gui/cSpectrumDetailWidget.h" -#include #include #include #include @@ -21,6 +20,7 @@ cSpectrumDetailWidget::cSpectrumDetailWidget() { + parent = 0; parameters = 0; preparedToShow = false; theoreticalspectrum = new cTheoreticalSpectrum(); @@ -39,7 +39,7 @@ cSpectrumDetailWidget& cSpectrumDetailWidget::operator=(const cSpectrumDetailWid theoreticalspectrum = new cTheoreticalSpectrum(); if (parameters && sd.theoreticalspectrum) { - initialize(parameters, *sd.theoreticalspectrum); + initialize(parameters, *sd.theoreticalspectrum, sd.parent); } if (parameters && sd.preparedToShow) { @@ -52,7 +52,8 @@ cSpectrumDetailWidget& cSpectrumDetailWidget::operator=(const cSpectrumDetailWid } -void cSpectrumDetailWidget::initialize(cParameters* parameters, cTheoreticalSpectrum& theoreticalspectrum) { +void cSpectrumDetailWidget::initialize(cParameters* parameters, cTheoreticalSpectrum& theoreticalspectrum, QWidget* parent) { + this->parent = parent; this->parameters = parameters; *this->theoreticalspectrum = theoreticalspectrum; } @@ -74,8 +75,73 @@ string cSpectrumDetailWidget::getDetailsAsHTMLString() { s += theoreticalspectrum->getCandidate().getAcronymPeptideNameWithHTMLReferences(); s += "

"; s += "Full Peptide Name:
"; - s += theoreticalspectrum->getCandidate().getRealPeptideName() + "
"; + s += theoreticalspectrum->getCandidate().getRealPeptideName(); + string lname = parameters->searchedmodifications[theoreticalspectrum->getCandidate().getStartModifID()].name; + if (lname.empty()) { + lname = "-"; + } + else { + lname += " (" + to_string(parameters->searchedmodifications[theoreticalspectrum->getCandidate().getStartModifID()].massdifference) + " Da)"; + } + + string bname = parameters->searchedmodifications[theoreticalspectrum->getCandidate().getMiddleModifID()].name; + if (bname.empty()) { + bname = "-"; + } + else { + bname += " (" + to_string(parameters->searchedmodifications[theoreticalspectrum->getCandidate().getMiddleModifID()].massdifference) + " Da)"; + } + + string rname = parameters->searchedmodifications[theoreticalspectrum->getCandidate().getEndModifID()].name; + if (rname.empty()) { + rname = "-"; + } + else { + rname += " (" + to_string(parameters->searchedmodifications[theoreticalspectrum->getCandidate().getEndModifID()].massdifference) + " Da)"; + } + + switch (parameters->peptidetype) + { + case linear: + s += "

"; + s += "N-terminal Modification: " + lname + "
"; + s += "C-terminal Modification: " + rname + "
"; + break; + case cyclic: + s += "
"; + break; + case branched: + s += "

"; + s += "N-terminal Modification: " + lname + "
"; + s += "Branch Modification: " + bname + "
"; + s += "C-terminal Modification: " + rname + "
"; + break; + case branchcyclic: + s += "

"; + s += "Branch Modification: " + bname + "
"; + break; +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + s += "

"; + s += "Left Modification: " + lname + "
"; + s += "Right Modification: " + rname + "
"; + break; + case cyclicpolyketide: + s += "
"; + break; +#endif + case linearpolysaccharide: + s += "

"; + s += "N-terminal Modification: " + lname + "
"; + s += "C-terminal Modification: " + rname + "
"; + break; + case other: + break; + default: + break; + } + if ((int)theoreticalspectrum->getCandidate().getPathAsString().size() > 0) { s += "
"; s += "Path in the De Novo Graph:
"; @@ -86,8 +152,271 @@ string cSpectrumDetailWidget::getDetailsAsHTMLString() { } - s += "Unmatched Measured Peaks:
" + theoreticalspectrum->getUnmatchedPeaks() + "

"; - s += "Theoretical Peaks:
" + theoreticalspectrum->getTheoreticalPeaks()->print(true); + } + + return s; +} + + +string cSpectrumDetailWidget::getPeaksTableAsHTMLString(bool unmatchedtheoreticalpeaks, bool unmatchedexperimentalpeaks) { + string s = ""; + int rowcount, columncount, thpeakscount; + int secondspace, langle, rangle, tmp1, tmp2; + cPeaksList* thpeaks; + cPeak* peak; + bool isred; + string tdwidth; + + if (theoreticalspectrum && parameters) { + + s += ""; + + if (parameters->mode == dereplication) { + columncount = 8; + } + else { + columncount = 6; + } + + tdwidth = to_string(100/columncount); + + if (parameters->mode == dereplication) { + s += ""; + } + else { + s += ""; + } + + s += ""; + s += ""; + s += ""; + s += ""; + + if (parameters->mode == dereplication) { + s += ""; + s += ""; + s += ""; + } + else { + s += ""; + } + + s += ""; + + if (parameters->mode == dereplication) { + thpeaks = new cPeaksList(); + for (int i = 0; i < (int)theoreticalspectrum->getTheoreticalPeaks()->size(); i++) { + peak = &((*(theoreticalspectrum->getTheoreticalPeaks()))[i]); + if (peak->matchedmz > 0) { + thpeaks->add(*peak); + } + } + thpeakscount = thpeaks->size(); + } + else { + thpeaks = theoreticalspectrum->getTheoreticalPeaks(); + thpeakscount = theoreticalspectrum->getTheoreticalPeaks()->size(); + } + + rowcount = thpeakscount + theoreticalspectrum->getUnmatchedPeaks()->size(); + + // theoretical peaks + for (int i = 0; i < thpeakscount; i++) { + peak = &((*thpeaks)[i]); + + if (peak->matchedmz > 0) { + isred = true; + } + else { + isred = false; + } + + if (!isred && !unmatchedtheoreticalpeaks) { + continue; + } + + s += ""; + + if (parameters->mode == dereplication) { + secondspace = (int)peak->description.find(' ', peak->description.find(' ') + 1); + s += printHTMLTableCell(peak->description.substr(0, secondspace), isred); + } + else { + s += printHTMLTableCell(peak->description.substr(0, peak->description.find(':')), isred); + } + + s += printHTMLTableCell(to_string(cropPrecisionToSixDecimals(peak->mzratio)), isred); + + if (peak->matchedmz > 0) { + s += printHTMLTableCell(to_string(cropPrecisionToSixDecimals(peak->matchedmz)), isred); + s += printHTMLTableCell(to_string(cropPrecisionToSixDecimals(peak->matchedintensity)), isred); + s += printHTMLTableCell(to_string(cropPrecisionToSixDecimals(peak->matchedppm)), isred); + } + else { + s += ""; + } + + if (parameters->mode == dereplication) { + s += printHTMLTableCell(peak->description.substr(peak->description.rfind('(') + 1, peak->description.rfind(')') - peak->description.rfind('(') - 1), isred); + + langle = (int)peak->description.rfind('<'); + rangle = (int)peak->description.find('>'); + if ((langle != string::npos) && (rangle != string::npos)) { + s += printHTMLTableCell(peak->description.substr(rangle + 1, langle - rangle - 1), isred); + + tmp1 = (int)peak->description.find('<'); + tmp2 = (int)peak->description.rfind('>'); + s += printHTMLTableCell(peak->description.substr(tmp1, rangle - tmp1 + 1) + "view" + peak->description.substr(langle, tmp2 - langle + 1), isred); + } + else { + s += printHTMLTableCell(peak->description.substr(secondspace + 1, peak->description.rfind('(') - secondspace - 2), isred); + s += ""; + } + } + else { + if (peak->description.find(':') + 2 < peak->description.size()) { + s += printHTMLTableCell(peak->description.substr(peak->description.find(':') + 2), isred); + } + else { + s += ""; + } + } + + s += ""; + } + + // unmatched experimental peaks + if (unmatchedexperimentalpeaks) { + for (int i = thpeakscount; i < thpeakscount + theoreticalspectrum->getUnmatchedPeaks()->size(); i++) { + s += ""; + peak = &((*(theoreticalspectrum->getUnmatchedPeaks()))[i - thpeakscount]); + s += ""; + s += printHTMLTableCell(to_string(cropPrecisionToSixDecimals(peak->mzratio)), false); + s += printHTMLTableCell(to_string(cropPrecisionToSixDecimals(peak->intensity)), false); + s += ""; + if (parameters->mode == dereplication) { + s += ""; + } + s += ""; + } + } + + s += "
Ion TypeFragment TypeTheoretical m/zExperimental m/zIntensity [%]Error [ppm]Summary FormulaNameReferenceSequence
"; + + if (parameters->mode == dereplication) { + delete thpeaks; + } + + } + + return s; +} + + +string cSpectrumDetailWidget::getPartialPeaksTableAsHTMLString(int id) { + string s = ""; + int thpeakscount; + int secondspace, langle, rangle, tmp1, tmp2; + cPeaksList* thpeaks; + cPeak* peak; + bool isred; + + if (theoreticalspectrum && parameters) { + + if (parameters->mode == dereplication) { + thpeaks = new cPeaksList(); + for (int i = 0; i < (int)theoreticalspectrum->getTheoreticalPeaks()->size(); i++) { + peak = &((*(theoreticalspectrum->getTheoreticalPeaks()))[i]); + if (peak->matchedmz > 0) { + thpeaks->add(*peak); + } + } + thpeakscount = thpeaks->size(); + } + else { + thpeaks = theoreticalspectrum->getTheoreticalPeaks(); + thpeakscount = theoreticalspectrum->getTheoreticalPeaks()->size(); + } + + // theoretical peaks + for (int i = 0; i < thpeakscount; i++) { + peak = &((*thpeaks)[i]); + + if (peak->matchedmz > 0) { + isred = true; + } + else { + isred = false; + } + + if (!isred) { + continue; + } + + s += ""; + s += ""; + s += to_string(id + 1); + s += ""; + + if (parameters->mode == dereplication) { + if ((parameters->peaklistfileformat == mis) || (parameters->peaklistfileformat == imzML)) { + s += ""; + s += to_string(theoreticalspectrum->getExperimentalSpectrum().getCoordinateX()); + s += ""; + s += ""; + s += to_string(theoreticalspectrum->getExperimentalSpectrum().getCoordinateY()); + s += ""; + } + secondspace = (int)peak->description.find(' ', peak->description.find(' ') + 1); + s += printHTMLTableCell(peak->description.substr(0, secondspace), isred); + } + else { + s += printHTMLTableCell(peak->description.substr(0, peak->description.find(':')), isred); + } + + s += printHTMLTableCell(to_string(cropPrecisionToSixDecimals(peak->mzratio)), isred); + + if (peak->matchedmz > 0) { + s += printHTMLTableCell(to_string(cropPrecisionToSixDecimals(peak->matchedmz)), isred); + s += printHTMLTableCell(to_string(cropPrecisionToSixDecimals(peak->matchedintensity)), isred); + s += printHTMLTableCell(to_string(cropPrecisionToSixDecimals(peak->matchedppm)), isred); + } + else { + s += ""; + } + + if (parameters->mode == dereplication) { + s += printHTMLTableCell(peak->description.substr(peak->description.rfind('(') + 1, peak->description.rfind(')') - peak->description.rfind('(') - 1), isred); + + langle = (int)peak->description.rfind('<'); + rangle = (int)peak->description.find('>'); + if ((langle != string::npos) && (rangle != string::npos)) { + s += printHTMLTableCell(peak->description.substr(rangle + 1, langle - rangle - 1), isred); + + tmp1 = (int)peak->description.find('<'); + tmp2 = (int)peak->description.rfind('>'); + s += printHTMLTableCell(peak->description.substr(tmp1, rangle - tmp1 + 1) + "view" + peak->description.substr(langle, tmp2 - langle + 1), isred); + } + else { + s += printHTMLTableCell(peak->description.substr(secondspace + 1, peak->description.rfind('(') - secondspace - 2), isred); + s += ""; + } + } + else { + if (peak->description.find(':') + 2 < peak->description.size()) { + s += printHTMLTableCell(peak->description.substr(peak->description.find(':') + 2), isred); + } + else { + s += ""; + } + } + + s += ""; + } + + if (parameters->mode == dereplication) { + delete thpeaks; + } } @@ -103,9 +432,33 @@ cSpectrumDetailWidget::~cSpectrumDetailWidget() { delete spectrumscene; - delete textedit; delete textbrowser; + //QProgressDialog progress("Clearing the peaklist...", /*"Cancel"*/0, 0, peakstable->rowCount(), this); + //cEventFilter filter; + //progress.installEventFilter(&filter); + //progress.setMinimumDuration(0); + //progress.setWindowModality(Qt::WindowModal); + + widgetitemallocator.reset(); + + if (parameters->mode == dereplication) { + for (int i = 0; i < peakstable->rowCount(); i++) { + if (peakstable->cellWidget(i, 7)) { + delete peakstable->cellWidget(i, 7); + } + + //progress.setValue(i); + //if (progress.wasCanceled()) { + // break; + //} + } + } + + //progress.setValue(peakstable->rowCount()); + + delete peakstable; + delete labelmz; delete minmz; delete maxmz; @@ -129,16 +482,22 @@ cSpectrumDetailWidget::~cSpectrumDetailWidget() { switch (parameters->peptidetype) { case linear: +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: +#endif delete linearwidget; break; case cyclic: +#if POLYKETIDE_SIDEROPHORES == 1 + case cyclicpolyketide: +#endif delete cyclicwidget; break; case branched: delete branchedwidget; break; - case lasso: - delete lassowidget; + case branchcyclic: + delete branchcyclicwidget; break; case linearpolysaccharide: break; @@ -179,7 +538,7 @@ void cSpectrumDetailWidget::closeEvent(QCloseEvent *event) { } -void cSpectrumDetailWidget::prepareToShow(peptideType peptidetype) { +void cSpectrumDetailWidget::prepareToShow(ePeptideType peptidetype) { if (!preparedToShow) { @@ -197,16 +556,22 @@ void cSpectrumDetailWidget::prepareToShow(peptideType peptidetype) { switch (peptidetype) { case linear: +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: +#endif linearwidget = new cLinearWidget(); break; case cyclic: +#if POLYKETIDE_SIDEROPHORES == 1 + case cyclicpolyketide: +#endif cyclicwidget = new cCyclicWidget(); break; case branched: branchedwidget = new cBranchedWidget(); break; - case lasso: - lassowidget = new cLassoWidget(); + case branchcyclic: + branchcyclicwidget = new cBranchCyclicWidget(); break; case linearpolysaccharide: break; @@ -223,11 +588,17 @@ void cSpectrumDetailWidget::prepareToShow(peptideType peptidetype) { toolbarExport->setMovable(false); toolbarExport->setFloatable(false); + actionExportTable = new QAction(QIcon(":/images/icons/62.png"), tr("Export Table to CSV"), this); + actionExportTable->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E)); + actionExportTable->setToolTip("Export Table to CSV (Ctrl + E)"); + toolbarExport->addAction(actionExportTable); + connect(actionExportTable, SIGNAL(triggered()), this, SLOT(exportTableToCSV())); + actionExportSpectrum = new QAction(QIcon(":/images/icons/66.png"), tr("Export Image"), this); - actionExportSpectrum->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E)); - actionExportSpectrum->setToolTip("Export Image (Ctrl + E)"); + actionExportSpectrum->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_I)); + actionExportSpectrum->setToolTip("Export Image (Ctrl + I)"); toolbarExport->addAction(actionExportSpectrum); - connect(actionExportSpectrum, SIGNAL(triggered()), this, SLOT(openExportDialog())); + connect(actionExportSpectrum, SIGNAL(triggered()), this, SLOT(openExportImageDialog())); toolbarFind = addToolBar(tr("Find")); @@ -287,14 +658,14 @@ void cSpectrumDetailWidget::prepareToShow(peptideType peptidetype) { actionHideMatched->setToolTip("Hide Matched Peaks (Ctrl + M)"); actionHideMatched->setCheckable(true); toolbarHide->addAction(actionHideMatched); - connect(actionHideMatched, SIGNAL(toggled(bool)), spectrumscene, SLOT(hideMatchedPeaks(bool))); + connect(actionHideMatched, SIGNAL(toggled(bool)), this, SLOT(hideMatchedPeaks(bool))); actionHideUnmatched = new QAction(QIcon(":/images/icons/81.png"), tr("Hide Unmatched Peaks"), this); actionHideUnmatched->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_U)); actionHideUnmatched->setToolTip("Hide Unmatched Peaks (Ctrl + U)"); actionHideUnmatched->setCheckable(true); toolbarHide->addAction(actionHideUnmatched); - connect(actionHideUnmatched, SIGNAL(toggled(bool)), spectrumscene, SLOT(hideUnmatchedPeaks(bool))); + connect(actionHideUnmatched, SIGNAL(toggled(bool)), this, SLOT(hideUnmatchedPeaks(bool))); actionHideScrambled = new QAction(QIcon(":/images/icons/80.png"), tr("Hide Scrambled Peaks"), this); actionHideScrambled->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S)); @@ -308,23 +679,19 @@ void cSpectrumDetailWidget::prepareToShow(peptideType peptidetype) { if ((parameters->peptidetype == cyclic) && parameters->enablescrambling) { actionHideScrambled->setEnabled(true); - connect(actionHideScrambled, SIGNAL(toggled(bool)), spectrumscene, SLOT(hideScrambledPeaks(bool))); + connect(actionHideScrambled, SIGNAL(toggled(bool)), this, SLOT(hideScrambledPeaks(bool))); } } - textedit = new QTextEdit(); - textedit->setReadOnly(true); - textedit->setFont(QFont("Courier", 9)); - textedit->setLineWrapMode(QTextEdit::NoWrap); - textbrowser = new QTextBrowser(); textbrowser->setReadOnly(true); textbrowser->setFont(QFont("Courier", 9)); textbrowser->setLineWrapMode(QTextEdit::NoWrap); textbrowser->setAcceptRichText(true); textbrowser->setOpenExternalLinks(true); - + + peakstable = new QTableWidget(0, 0, this); labelmz = new QLabel(tr("View m/z (from - to): ")); @@ -411,7 +778,11 @@ void cSpectrumDetailWidget::prepareToShow(peptideType peptidetype) { if (parameters && ((parameters->mode == denovoengine) || (parameters->mode == singlecomparison) || (parameters->mode == databasesearch))) { // cyclic +#if POLYKETIDE_SIDEROPHORES == 1 + if (theoreticalspectrum && ((parameters->peptidetype == cyclic) || (parameters->peptidetype == cyclicpolyketide))) { +#else if (theoreticalspectrum && (parameters->peptidetype == cyclic)) { +#endif int r = (int)theoreticalspectrum->getCandidate().getAcronyms().size(); int hint = (int)theoreticalspectrum->getVisualCoverage().size()/(2*r); @@ -455,7 +826,7 @@ void cSpectrumDetailWidget::prepareToShow(peptideType peptidetype) { } // branch-cyclic - if (parameters && theoreticalspectrum && (parameters->peptidetype == lasso)) { + if (parameters && theoreticalspectrum && (parameters->peptidetype == branchcyclic)) { int r = (int)theoreticalspectrum->getCandidate().getAcronyms().size() - (int)theoreticalspectrum->getCandidate().getBranchSize(); int hint = (int)theoreticalspectrum->getVisualCoverage().size()/(2*r); @@ -471,7 +842,7 @@ void cSpectrumDetailWidget::prepareToShow(peptideType peptidetype) { connect(rotation, SIGNAL(currentIndexChanged(int)), spectrumscene, SLOT(rotationChanged(int))); connect(rotation, SIGNAL(currentIndexChanged(QString)), spectrumscene, SLOT(rotationChanged(QString))); - connect(rotation, SIGNAL(currentIndexChanged(int)), lassowidget, SLOT(rotationChanged(int))); + connect(rotation, SIGNAL(currentIndexChanged(int)), branchcyclicwidget, SLOT(rotationChanged(int))); toolbarRotation = addToolBar(tr("Ring break up point")); toolbarRotation->addWidget(widgetrotation); @@ -487,7 +858,7 @@ void cSpectrumDetailWidget::prepareToShow(peptideType peptidetype) { trotation->addItem(tr("6 (right-to-top)")); connect(trotation, SIGNAL(currentIndexChanged(int)), spectrumscene, SLOT(trotationChanged(int))); - connect(trotation, SIGNAL(currentIndexChanged(int)), lassowidget, SLOT(trotationChanged(int))); + connect(trotation, SIGNAL(currentIndexChanged(int)), branchcyclicwidget, SLOT(trotationChanged(int))); toolbarTrotation = addToolBar(tr("Linearized sequence")); toolbarTrotation->addWidget(widgettrotation); @@ -498,7 +869,7 @@ void cSpectrumDetailWidget::prepareToShow(peptideType peptidetype) { } hsplitter1->addWidget(spectrumscene); - hsplitter2->addWidget(textbrowser); + hsplitter2->addWidget(peakstable); QList sizes; sizes.push_back(100); @@ -509,16 +880,22 @@ void cSpectrumDetailWidget::prepareToShow(peptideType peptidetype) { switch (peptidetype) { case linear: +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: +#endif hsplitter1->addWidget(linearwidget); break; case cyclic: +#if POLYKETIDE_SIDEROPHORES == 1 + case cyclicpolyketide: +#endif hsplitter1->addWidget(cyclicwidget); break; case branched: hsplitter1->addWidget(branchedwidget); break; - case lasso: - hsplitter1->addWidget(lassowidget); + case branchcyclic: + hsplitter1->addWidget(branchcyclicwidget); break; case linearpolysaccharide: break; @@ -528,7 +905,7 @@ void cSpectrumDetailWidget::prepareToShow(peptideType peptidetype) { break; } - hsplitter2->addWidget(textedit); + hsplitter2->addWidget(textbrowser); hsplitter1->setSizes(sizes); hsplitter2->setSizes(sizes); @@ -555,16 +932,22 @@ void cSpectrumDetailWidget::prepareToShow(peptideType peptidetype) { switch (peptidetype) { case linear: +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: +#endif linearwidget->initialize(parameters, theoreticalspectrum); break; case cyclic: +#if POLYKETIDE_SIDEROPHORES == 1 + case cyclicpolyketide: +#endif cyclicwidget->initialize(parameters, theoreticalspectrum); break; case branched: branchedwidget->initialize(parameters, theoreticalspectrum); break; - case lasso: - lassowidget->initialize(parameters, theoreticalspectrum); + case branchcyclic: + branchcyclicwidget->initialize(parameters, theoreticalspectrum); break; case linearpolysaccharide: break; @@ -574,30 +957,36 @@ void cSpectrumDetailWidget::prepareToShow(peptideType peptidetype) { break; } - textedit->setHtml(theoreticalspectrum->getCoverageBySeries().c_str()); + textbrowser->setHtml((getDetailsAsHTMLString() + theoreticalspectrum->getCoverageBySeries()).c_str()); } spectrumscene->initialize(parameters, theoreticalspectrum); - textbrowser->setHtml(getDetailsAsHTMLString().c_str()); + + preparePeaksTable(); } preparedToShow = true; + } } -void cSpectrumDetailWidget::findAll(const QString& str, QTextDocument::FindFlags opt) { +void cSpectrumDetailWidget::findAll(const QString& str, QTextDocument::FindFlags opt, bool errormessage) { currentfinditem = 0; + peakstable->clearSelection(); + tablematches.clear(); + // textbrowser QList extraSelections; - textbrowser->moveCursor(QTextCursor::Start); QColor color = QColor(Qt::yellow); + textbrowser->moveCursor(QTextCursor::Start); + while (textbrowser->find(str, opt)) { QTextEdit::ExtraSelection extra; extra.format.setBackground(color); @@ -608,31 +997,57 @@ void cSpectrumDetailWidget::findAll(const QString& str, QTextDocument::FindFlags textbrowser->setExtraSelections(extraSelections); - - // textedit - extraSelections.clear(); - textedit->moveCursor(QTextCursor::Start); - while (textedit->find(str, opt)) { - QTextEdit::ExtraSelection extra; - extra.format.setBackground(color); - - extra.cursor = textedit->textCursor(); - extraSelections.append(extra); + // table + cTablePosition tpos; + for (int i = 0; i < peakstable->rowCount(); i++) { + + if (peakstable->isRowHidden(i)) { + continue; + } + + for (int j = 0; j < peakstable->columnCount(); j++) { + if (!peakstable->item(i, j)) { + continue; + } + + peakstable->item(i, j)->setBackgroundColor(Qt::white); + + if (str.isEmpty()) { + continue; + } + + if (opt & QTextDocument::FindWholeWords) { + if (peakstable->item(i, j)->text().compare(str, (opt & QTextDocument::FindCaseSensitively)?Qt::CaseSensitive:Qt::CaseInsensitive) == 0) { + tpos.row = i; + tpos.column = j; + tablematches.push_back(tpos); + peakstable->item(i, j)->setBackgroundColor(color); + } + } + else { + if (peakstable->item(i, j)->text().contains(str, (opt & QTextDocument::FindCaseSensitively)?Qt::CaseSensitive:Qt::CaseInsensitive)) { + tpos.row = i; + tpos.column = j; + tablematches.push_back(tpos); + peakstable->item(i, j)->setBackgroundColor(color); + } + } + } } - - textedit->setExtraSelections(extraSelections); - if (textbrowser->extraSelections().size() + textedit->extraSelections().size() == 0) { + if (textbrowser->extraSelections().size() + (int)tablematches.size() == 0) { actionPrevious->setDisabled(true); actionNext->setDisabled(true); - QMessageBox msgBox; - QString errstr = "No results were found."; - msgBox.setWindowTitle("Find Text"); - msgBox.setText(errstr); - msgBox.exec(); + if (errormessage) { + QMessageBox msgBox; + QString errstr = "No results were found."; + msgBox.setWindowTitle("Find Text"); + msgBox.setText(errstr); + msgBox.exec(); + } } else { actionPrevious->setDisabled(false); @@ -641,15 +1056,16 @@ void cSpectrumDetailWidget::findAll(const QString& str, QTextDocument::FindFlags // order changed because of setFocus() - if (textedit->extraSelections().size() > 0) { - textedit->setTextCursor(textedit->extraSelections().at(0).cursor); - textedit->setFocus(); + if (textbrowser->extraSelections().size() > 0) { + textbrowser->setFocus(); + textbrowser->setTextCursor(textbrowser->extraSelections().at(0).cursor); } - if (textbrowser->extraSelections().size() > 0) { - textbrowser->setTextCursor(textbrowser->extraSelections().at(0).cursor); - textbrowser->setFocus(); + if (tablematches.size() > 0) { + peakstable->setFocus(); + peakstable->scrollToItem(peakstable->item(tablematches[0].row, tablematches[0].column)); + peakstable->item(tablematches[0].row, tablematches[0].column)->setSelected(true); } } @@ -676,6 +1092,216 @@ void cSpectrumDetailWidget::keyPressEvent(QKeyEvent *event) { } +void cSpectrumDetailWidget::preparePeaksTable() { + + peakstable->setEditTriggers(QAbstractItemView::NoEditTriggers); + peakstable->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); + peakstable->horizontalHeader()->setSectionsMovable(true); + //peakstable->setSelectionMode(QAbstractItemView::SingleSelection); + + if (parameters->mode == dereplication) { + peakstable->setColumnCount(8); + } + else { + peakstable->setColumnCount(6); + } + + for (int i = 0; i < peakstable->columnCount(); i++) { + peakstable->setHorizontalHeaderItem(i, widgetitemallocator.getNewItem()); + } + + peakstable->horizontalHeaderItem(1)->setText("Theoretical m/z"); + peakstable->setItemDelegateForColumn(1, &columndelegate); + + peakstable->horizontalHeaderItem(2)->setText("Experimental m/z"); + peakstable->setItemDelegateForColumn(2, &columndelegate); + + peakstable->horizontalHeaderItem(3)->setText("Intensity [%]"); + peakstable->setItemDelegateForColumn(3, &columndelegate); + + peakstable->horizontalHeaderItem(4)->setText("Error [ppm]"); + peakstable->setItemDelegateForColumn(4, &columndelegate); + + if (parameters->mode == dereplication) { + peakstable->horizontalHeaderItem(0)->setText("Ion Type"); + peakstable->horizontalHeaderItem(5)->setText("Summary Formula"); + peakstable->horizontalHeaderItem(6)->setText("Name"); + peakstable->horizontalHeaderItem(7)->setText("Reference"); + } + else { + peakstable->horizontalHeaderItem(0)->setText("Fragment Type"); + peakstable->horizontalHeaderItem(5)->setText("Sequence"); + } + + connect(peakstable->horizontalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(headerItemDoubleClicked(int))); + + cPeaksList* thpeaks; + int thpeakscount; + cPeak* peak; + int secondspace, langle, rangle, tmp1, tmp2; + QBrush brush; + + if (parameters->mode == dereplication) { + thpeaks = new cPeaksList(); + for (int i = 0; i < (int)theoreticalspectrum->getTheoreticalPeaks()->size(); i++) { + peak = &((*(theoreticalspectrum->getTheoreticalPeaks()))[i]); + if (peak->matchedmz > 0) { + thpeaks->add(*peak); + } + } + thpeakscount = thpeaks->size(); + } + else { + thpeaks = theoreticalspectrum->getTheoreticalPeaks(); + thpeakscount = theoreticalspectrum->getTheoreticalPeaks()->size(); + } + + peakstable->setRowCount(thpeakscount + theoreticalspectrum->getUnmatchedPeaks()->size()); + + QProgressDialog progress("Preparing the peaklist...", /*"Cancel"*/0, 0, thpeakscount + theoreticalspectrum->getUnmatchedPeaks()->size(), parent); + cEventFilter filter; + progress.installEventFilter(&filter); + progress.setMinimumDuration(0); + progress.setWindowModality(Qt::WindowModal); + + // theoretical peaks + for (int i = 0; i < thpeakscount; i++) { + peakstable->setRowHeight(i, 20); + peak = &((*thpeaks)[i]); + + if (peak->matchedmz > 0) { + brush.setColor(QColor(255, 0, 0)); + } + else { + brush.setColor(QColor(0, 0, 0)); + } + + if (parameters->mode == dereplication) { + secondspace = (int)peak->description.find(' ', peak->description.find(' ') + 1); + + peakstable->setItem(i, 0, widgetitemallocator.getNewItem()); + peakstable->item(i, 0)->setForeground(brush); + peakstable->item(i, 0)->setText(peak->description.substr(0, secondspace).c_str()); + } + else { + peakstable->setItem(i, 0, widgetitemallocator.getNewItem()); + peakstable->item(i, 0)->setForeground(brush); + peakstable->item(i, 0)->setText(peak->description.substr(0, peak->description.find(':')).c_str()); + } + + peakstable->setItem(i, 1, widgetitemallocator.getNewItem()); + peakstable->item(i, 1)->setForeground(brush); + peakstable->item(i, 1)->setData(Qt::DisplayRole, cropPrecisionToSixDecimals(peak->mzratio)); + + if (peak->matchedmz > 0) { + peakstable->setItem(i, 2, widgetitemallocator.getNewItem()); + peakstable->item(i, 2)->setForeground(brush); + peakstable->item(i, 2)->setData(Qt::DisplayRole, cropPrecisionToSixDecimals(peak->matchedmz)); + + peakstable->setItem(i, 3, widgetitemallocator.getNewItem()); + peakstable->item(i, 3)->setForeground(brush); + peakstable->item(i, 3)->setData(Qt::DisplayRole, cropPrecisionToSixDecimals(peak->matchedintensity)); + + peakstable->setItem(i, 4, widgetitemallocator.getNewItem()); + peakstable->item(i, 4)->setForeground(brush); + peakstable->item(i, 4)->setData(Qt::DisplayRole, cropPrecisionToSixDecimals(peak->matchedppm)); + } + + if (parameters->mode == dereplication) { + peakstable->setItem(i, 5, widgetitemallocator.getNewItem()); + peakstable->item(i, 5)->setForeground(brush); + peakstable->item(i, 5)->setText(peak->description.substr(peak->description.rfind('(') + 1, peak->description.rfind(')') - peak->description.rfind('(') - 1).c_str()); + + peakstable->setItem(i, 6, widgetitemallocator.getNewItem()); + peakstable->item(i, 6)->setForeground(brush); + langle = (int)peak->description.rfind('<'); + rangle = (int)peak->description.find('>'); + if ((langle != string::npos) && (rangle != string::npos)) { + peakstable->item(i, 6)->setText(peak->description.substr(rangle + 1, langle - rangle - 1).c_str()); + + tmp1 = (int)peak->description.find('<'); + tmp2 = (int)peak->description.rfind('>'); + peakstable->setCellWidget(i, 7, new QLabel((peak->description.substr(tmp1, rangle - tmp1 + 1) + "view" + peak->description.substr(langle, tmp2 - langle + 1)).c_str())); + ((QLabel *)peakstable->cellWidget(i, 7))->setTextFormat(Qt::RichText); + ((QLabel *)peakstable->cellWidget(i, 7))->setTextInteractionFlags(Qt::TextBrowserInteraction); + ((QLabel *)peakstable->cellWidget(i, 7))->setOpenExternalLinks(true); + } + else { + peakstable->item(i, 6)->setText(peak->description.substr(secondspace + 1, peak->description.rfind('(') - secondspace - 2).c_str()); + } + } + else { + if (peak->description.find(':') + 2 < peak->description.size()) { + peakstable->setItem(i, 5, widgetitemallocator.getNewItem()); + peakstable->item(i, 5)->setForeground(brush); + peakstable->item(i, 5)->setText(peak->description.substr(peak->description.find(':') + 2).c_str()); + } + } + + progress.setValue(i); + //if (progress.wasCanceled()) { + // break; + //} + } + + // unmatched experimental peaks + for (int i = thpeakscount; i < thpeakscount + theoreticalspectrum->getUnmatchedPeaks()->size(); i++) { + peakstable->setRowHeight(i, 20); + peak = &((*(theoreticalspectrum->getUnmatchedPeaks()))[i - thpeakscount]); + + peakstable->setItem(i, 2, widgetitemallocator.getNewItem()); + peakstable->item(i, 2)->setData(Qt::DisplayRole, cropPrecisionToSixDecimals(peak->mzratio)); + + peakstable->setItem(i, 3, widgetitemallocator.getNewItem()); + peakstable->item(i, 3)->setData(Qt::DisplayRole, cropPrecisionToSixDecimals(peak->intensity)); + + progress.setValue(i); + //if (progress.wasCanceled()) { + // break; + //} + } + + peakstableheadersort.resize(peakstable->columnCount()); + for (int i = 0; i < peakstable->columnCount(); i++) { + peakstableheadersort[i] = -1; + } + + for (int i = 0; i < peakstable->columnCount(); i++) { + peakstable->resizeColumnToContents(i); + } + + progress.setValue(thpeakscount + theoreticalspectrum->getUnmatchedPeaks()->size()); + + tablematches.clear(); + + if (parameters->mode == dereplication) { + delete thpeaks; + } + +} + + +string cSpectrumDetailWidget::printHTMLTableCell(string text, bool red) { + string s; + + s += ""; + + if (red) { + s += ""; + } + + s += text; + + if (red) { + s += ""; + } + + s += ""; + + return s; +} + + void cSpectrumDetailWidget::updateMZInterval(double minmz, double maxmz) { this->minmz->setValue(minmz); this->maxmz->setValue(maxmz); @@ -743,8 +1369,11 @@ void cSpectrumDetailWidget::exportPeptide() { regex rx; bool selected = false; - switch ((peptideType)parameters->peptidetype) { + switch ((ePeptideType)parameters->peptidetype) { case linear: +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: +#endif rx = ".+\\.pdf$"; if (!selected && (regex_search(filename.toStdString(), rx))) { linearwidget->exportToPDF(filename, false); @@ -772,6 +1401,9 @@ void cSpectrumDetailWidget::exportPeptide() { } break; case cyclic: +#if POLYKETIDE_SIDEROPHORES == 1 + case cyclicpolyketide: +#endif rx = ".+\\.pdf$"; if (!selected && (regex_search(filename.toStdString(), rx))) { cyclicwidget->exportToPDF(filename, false); @@ -825,30 +1457,30 @@ void cSpectrumDetailWidget::exportPeptide() { selected = true; } break; - case lasso: + case branchcyclic: rx = ".+\\.pdf$"; if (!selected && (regex_search(filename.toStdString(), rx))) { - lassowidget->exportToPDF(filename, false); + branchcyclicwidget->exportToPDF(filename, false); selected = true; } #if OS_TYPE == WIN rx = ".+\\.ps$"; if (!selected && (regex_search(filename.toStdString(), rx))) { - lassowidget->exportToPDF(filename, true); + branchcyclicwidget->exportToPDF(filename, true); selected = true; } #endif rx = ".+\\.png$"; if (!selected && (regex_search(filename.toStdString(), rx))) { - lassowidget->exportToPNG(filename); + branchcyclicwidget->exportToPNG(filename); selected = true; } rx = ".+\\.svg$"; if (!selected && (regex_search(filename.toStdString(), rx))) { - lassowidget->exportToSVG(filename); + branchcyclicwidget->exportToSVG(filename); selected = true; } break; @@ -875,7 +1507,7 @@ void cSpectrumDetailWidget::openFindDialog() { } -void cSpectrumDetailWidget::openExportDialog() { +void cSpectrumDetailWidget::openExportImageDialog() { if (parameters && ((parameters->mode == denovoengine) || (parameters->mode == singlecomparison) || (parameters->mode == databasesearch))) { exportdialog->exec(); } @@ -885,32 +1517,188 @@ void cSpectrumDetailWidget::openExportDialog() { } +void cSpectrumDetailWidget::exportTableToCSV() { + QString filename = QFileDialog::getSaveFileName(this, tr("Export Table to CSV..."), "./", "Files (*.csv)"); + + if (!filename.isEmpty()) { + + QProgressDialog progress("Exporting the CSV file...", /*"Cancel"*/0, 0, peakstable->rowCount(), this); + cEventFilter filter; + progress.installEventFilter(&filter); + progress.setMinimumDuration(0); + progress.setWindowModality(Qt::WindowModal); + + QFile file(filename); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { + return; + } + + QTextStream out(&file); + + for (int i = 0; i < peakstable->columnCount(); i++) { + out << "\"" << peakstable->horizontalHeaderItem(i)->text() << "\""; + if (i < peakstable->columnCount() - 1) { + out << ","; + } + } + out << endl; + + for (int i = 0; i < peakstable->rowCount(); i++) { + + if (peakstable->isRowHidden(i)) { + continue; + } + + for (int j = 0; j < peakstable->columnCount(); j++) { + if (peakstable->item(i, j)) { + out << "\"" << peakstable->item(i, j)->data(Qt::DisplayRole).toString() << "\""; + if (j < peakstable->columnCount() - 1) { + out << ","; + } + } + else if (peakstable->cellWidget(i, j)) { + out << "\"" << ((QLabel *)(peakstable->cellWidget(i, j)))->text() << "\""; + if (j < peakstable->columnCount() - 1) { + out << ","; + } + } + else { + if (j < peakstable->columnCount() - 1) { + out << ","; + } + } + } + out << endl; + + progress.setValue(i); + //if (progress.wasCanceled()) { + // break; + //} + } + + file.close(); + + progress.setValue(peakstable->rowCount()); + + } +} + + void cSpectrumDetailWidget::movePrevious() { - int count = textbrowser->extraSelections().size() + textedit->extraSelections().size(); + if (currentfinditem < (int)tablematches.size()) { + peakstable->clearSelection(); + } + + int count = (int)tablematches.size() + textbrowser->extraSelections().size(); currentfinditem = (currentfinditem + count - 1)%count; - if (currentfinditem < textbrowser->extraSelections().size()) { - textbrowser->setFocus(); - textbrowser->setTextCursor(textbrowser->extraSelections().at(currentfinditem).cursor); + if (currentfinditem < (int)tablematches.size()) { + peakstable->setFocus(); + peakstable->scrollToItem(peakstable->item(tablematches[currentfinditem].row, tablematches[currentfinditem].column)); + peakstable->item(tablematches[currentfinditem].row, tablematches[currentfinditem].column)->setSelected(true); } else { - textedit->setFocus(); - textedit->setTextCursor(textedit->extraSelections().at(currentfinditem - textbrowser->extraSelections().size()).cursor); + textbrowser->setFocus(); + textbrowser->setTextCursor(textbrowser->extraSelections().at(currentfinditem - (int)tablematches.size()).cursor); } } void cSpectrumDetailWidget::moveNext() { - int count = textbrowser->extraSelections().size() + textedit->extraSelections().size(); + if (currentfinditem < (int)tablematches.size()) { + peakstable->clearSelection(); + } + + int count = (int)tablematches.size() + textbrowser->extraSelections().size(); currentfinditem = (currentfinditem + 1)%count; - if (currentfinditem < textbrowser->extraSelections().size()) { + if (currentfinditem < (int)tablematches.size()) { + peakstable->setFocus(); + peakstable->scrollToItem(peakstable->item(tablematches[currentfinditem].row, tablematches[currentfinditem].column)); + peakstable->item(tablematches[currentfinditem].row, tablematches[currentfinditem].column)->setSelected(true); + } + else { textbrowser->setFocus(); - textbrowser->setTextCursor(textbrowser->extraSelections().at(currentfinditem).cursor); + textbrowser->setTextCursor(textbrowser->extraSelections().at(currentfinditem - (int)tablematches.size()).cursor); + } +} + + +void cSpectrumDetailWidget::headerItemDoubleClicked(int index) { + findAll("", 0, false); + + if (peakstableheadersort[index] == -1) { + peakstable->sortByColumn(index, Qt::DescendingOrder); + peakstableheadersort[index] = 0; + return; + } + + if (peakstableheadersort[index] == 0) { + peakstable->sortByColumn(index, Qt::AscendingOrder); + peakstableheadersort[index] = 1; } else { - textedit->setFocus(); - textedit->setTextCursor(textedit->extraSelections().at(currentfinditem - textbrowser->extraSelections().size()).cursor); + peakstable->sortByColumn(index, Qt::DescendingOrder); + peakstableheadersort[index] = 0; } } + +void cSpectrumDetailWidget::filterPeaksTable() { + findAll("", 0, false); + + int rowcount = peakstable->rowCount(); + int i; + bool hide; + cPeaksList* thpeaks = theoreticalspectrum->getTheoreticalPeaks(); + + QProgressDialog progress("Updating...", /*"Cancel"*/0, 0, rowcount, this); + cEventFilter filter; + progress.installEventFilter(&filter); + progress.setMinimumDuration(0); + progress.setWindowModality(Qt::WindowModal); + + bool hm = actionHideMatched->isChecked(); + bool hu = actionHideUnmatched->isChecked(); + bool hs = actionHideScrambled->isChecked(); + + for (i = 0; i < rowcount; i++) { + hide = false; + + if (hm && peakstable->item(i, 1) && peakstable->item(i, 2)) { + hide = true; + } + + if (hu && ((peakstable->item(i, 1) == 0) || (peakstable->item(i, 2) == 0))) { + hide = true; + } + + if (hs && peakstable->item(i, 0) && peakstable->item(i, 0)->text().contains("scrambled")) { + hide = true; + } + + peakstable->setRowHidden(i, hide); + progress.setValue(i); + } + + progress.setValue(rowcount); +} + + +void cSpectrumDetailWidget::hideMatchedPeaks(bool hide) { + spectrumscene->hideMatchedPeaks(hide); + filterPeaksTable(); +} + + +void cSpectrumDetailWidget::hideUnmatchedPeaks(bool hide) { + spectrumscene->hideUnmatchedPeaks(hide); + filterPeaksTable(); +} + + +void cSpectrumDetailWidget::hideScrambledPeaks(bool hide) { + spectrumscene->hideScrambledPeaks(hide); + filterPeaksTable(); +} + diff --git a/CycloBranch/gui/cSpectrumDetailWidget.h b/CycloBranch/gui/cSpectrumDetailWidget.h index 2595315..c093264 100644 --- a/CycloBranch/gui/cSpectrumDetailWidget.h +++ b/CycloBranch/gui/cSpectrumDetailWidget.h @@ -10,18 +10,24 @@ #include #include #include +#include +#include +#include +#include +#include "core/cAllocator.h" #include "core/cTheoreticalSpectrum.h" #include "gui/cLinearWidget.h" #include "gui/cCyclicWidget.h" #include "gui/cBranchedWidget.h" -#include "gui/cLassoWidget.h" +#include "gui/cBranchCyclicWidget.h" #include "gui/cSpectrumSceneWidget.h" #include "gui/cFindDialog.h" #include "gui/cExportDialog.h" +#include "gui/cEventFilter.h" +#include "gui/cDelegate.h" // forward declaration -class QTextEdit; class QTextBrowser; class QHBoxLayout; class QVBoxLayout; @@ -36,6 +42,29 @@ class QAction; class QLabel; +/** + \brief Position in QTableWidget. +*/ +struct cTablePosition { + /** + \brief A number of a row. + */ + int row; + + + /** + \brief A number of a column. + */ + int column; + + + cTablePosition() { + row = 0; + column = 0; + } +}; + + /** \brief The class representing a widget for visualisation of a detail of a spectrum (opened after doubleclick on a row when results are reported). */ @@ -83,8 +112,9 @@ class cSpectrumDetailWidget : public QMainWindow \brief Initialize the widget. \param parameters a pointer to parameters \param theoreticalspectrum a reference to a theoretical spectrum + \param parent pointer to a parent widget */ - void initialize(cParameters* parameters, cTheoreticalSpectrum& theoreticalspectrum); + void initialize(cParameters* parameters, cTheoreticalSpectrum& theoreticalspectrum, QWidget* parent); /** @@ -101,19 +131,37 @@ class cSpectrumDetailWidget : public QMainWindow string getDetailsAsHTMLString(); + /** + \brief Get a table of peaks as a HTML string. + \param unmatchedtheoreticalpeaks if true then unmatched theoretical peaks are included + \param unmatchedexperimentalpeaks if true then unmatched experimental peaks are included + \retval string a table of peaks as a HTML string + */ + string getPeaksTableAsHTMLString(bool unmatchedtheoreticalpeaks, bool unmatchedexperimentalpeaks); + + + /** + \brief Get a partial table of peaks as a HTML string. + \param id identifier of a spectrum + \retval string a partial table of peaks as a HTML string + */ + string getPartialPeaksTableAsHTMLString(int id); + + /** \brief Prepare the widget to show. \param peptidetype a type of peptide */ - void prepareToShow(peptideType peptidetype); + void prepareToShow(ePeptideType peptidetype); /** \brief Find all occurrences of \a str and highlight them. \param str search string \param opt search options + \param errormessage if true, an error message in a popup window is shown when no results are matched */ - void findAll(const QString& str, QTextDocument::FindFlags opt = 0); + void findAll(const QString& str, QTextDocument::FindFlags opt = 0, bool errormessage = true); /** @@ -134,6 +182,8 @@ class cSpectrumDetailWidget : public QMainWindow private: + QWidget* parent; + QToolBar* toolbarExport; QToolBar* toolbarFind; QToolBar* toolbarZoom; @@ -142,6 +192,7 @@ class cSpectrumDetailWidget : public QMainWindow QToolBar* toolbarRotation; QToolBar* toolbarTrotation; + QAction* actionExportTable; QAction* actionExportSpectrum; QAction* actionFind; QAction* actionPrevious; @@ -176,12 +227,18 @@ class cSpectrumDetailWidget : public QMainWindow QLabel* labeltrotation; QComboBox* trotation; - QTextEdit* textedit; QTextBrowser* textbrowser; + + QTableWidget* peakstable; + vector peakstableheadersort; + cAllocator widgetitemallocator; + vector tablematches; + cDelegate columndelegate; + cLinearWidget* linearwidget; cCyclicWidget* cyclicwidget; cBranchedWidget* branchedwidget; - cLassoWidget* lassowidget; + cBranchCyclicWidget* branchcyclicwidget; cSpectrumSceneWidget* spectrumscene; @@ -195,6 +252,11 @@ class cSpectrumDetailWidget : public QMainWindow int currentfinditem; + void preparePeaksTable(); + + string printHTMLTableCell(string text, bool red); + + signals: /** @@ -217,12 +279,25 @@ private slots: void openFindDialog(); - void openExportDialog(); + void openExportImageDialog(); + + void exportTableToCSV(); void movePrevious(); void moveNext(); + void headerItemDoubleClicked(int); + + void filterPeaksTable(); + + void hideMatchedPeaks(bool hide); + + void hideUnmatchedPeaks(bool hide); + + void hideScrambledPeaks(bool hide); + }; -#endif \ No newline at end of file +#endif + diff --git a/CycloBranch/gui/cSpectrumSceneWidget.cpp b/CycloBranch/gui/cSpectrumSceneWidget.cpp index fe11e10..66c261d 100644 --- a/CycloBranch/gui/cSpectrumSceneWidget.cpp +++ b/CycloBranch/gui/cSpectrumSceneWidget.cpp @@ -152,6 +152,42 @@ void cSpectrumSceneWidget::exportToPNG(QString filename) { } +void cSpectrumSceneWidget::hideUnmatchedPeaks(bool state) { + if (state == false) { + hideunmatched = false; + } + else { + hideunmatched = true; + } + + redrawScene(); +} + + +void cSpectrumSceneWidget::hideMatchedPeaks(bool state) { + if (state == false) { + hidematched = false; + } + else { + hidematched = true; + } + + redrawScene(); +} + + +void cSpectrumSceneWidget::hideScrambledPeaks(bool state) { + if (state == false) { + hidescrambled = false; + } + else { + hidescrambled = true; + } + + redrawScene(); +} + + void cSpectrumSceneWidget::wheelEvent(QWheelEvent *event) { if (event->delta() > 0) { zoomIn(); @@ -372,7 +408,7 @@ void cSpectrumSceneWidget::redrawScene() { if (parameters->peptidetype == branched) { coloredtrotationstring = " " + to_string(coloredtrotationid + 1) + "_"; } - if (parameters->peptidetype == lasso) { + if (parameters->peptidetype == branchcyclic) { coloredtrotationstring = "_" + to_string(coloredtrotationid + 1) + "_"; } } @@ -402,7 +438,7 @@ void cSpectrumSceneWidget::redrawScene() { hits.pop_back(); } - if ((parameters->peptidetype == lasso) && (((coloredrotationid != -1) && (hits.back().find(coloredrotationstring) == string::npos)) || ((coloredtrotationid != -1) && (hits.back().find(coloredtrotationstring) == string::npos)))) { + if ((parameters->peptidetype == branchcyclic) && (((coloredrotationid != -1) && (hits.back().find(coloredrotationstring) == string::npos)) || ((coloredtrotationid != -1) && (hits.back().find(coloredtrotationstring) == string::npos)))) { hits.pop_back(); } @@ -429,7 +465,7 @@ void cSpectrumSceneWidget::redrawScene() { hits.pop_back(); } - if ((parameters->peptidetype == lasso) && (((coloredrotationid != -1) && (hits.back().find(coloredrotationstring) == string::npos)) || ((coloredtrotationid != -1) && (hits.back().find(coloredtrotationstring) == string::npos)))) { + if ((parameters->peptidetype == branchcyclic) && (((coloredrotationid != -1) && (hits.back().find(coloredrotationstring) == string::npos)) || ((coloredtrotationid != -1) && (hits.back().find(coloredtrotationstring) == string::npos)))) { hits.pop_back(); } } @@ -524,6 +560,8 @@ void cSpectrumSceneWidget::updateZoomGroup() { qstr += QString::number(getMZRatioFromXPosition((pressedx < currentx)?pressedx:currentx, origwidth)); qstr += "-"; qstr += QString::number(getMZRatioFromXPosition((pressedx < currentx)?currentx:pressedx, origwidth)); + qstr += "\ndiff: "; + qstr += QString::number(getMZRatioFromXPosition((pressedx < currentx)?currentx:pressedx, origwidth) - getMZRatioFromXPosition((pressedx < currentx)?pressedx:currentx, origwidth)); zoomsimpletextitem->setFont(myFont); zoomsimpletextitem->setText(qstr); @@ -533,42 +571,6 @@ void cSpectrumSceneWidget::updateZoomGroup() { } -void cSpectrumSceneWidget::hideUnmatchedPeaks(bool state) { - if (state == false) { - hideunmatched = false; - } - else { - hideunmatched = true; - } - - redrawScene(); -} - - -void cSpectrumSceneWidget::hideMatchedPeaks(bool state) { - if (state == false) { - hidematched = false; - } - else { - hidematched = true; - } - - redrawScene(); -} - - -void cSpectrumSceneWidget::hideScrambledPeaks(bool state) { - if (state == false) { - hidescrambled = false; - } - else { - hidescrambled = true; - } - - redrawScene(); -} - - void cSpectrumSceneWidget::zoomIn() { if (currentscale < 32) { currentscale += factor; diff --git a/CycloBranch/gui/cSpectrumSceneWidget.h b/CycloBranch/gui/cSpectrumSceneWidget.h index 83fc912..611a64b 100644 --- a/CycloBranch/gui/cSpectrumSceneWidget.h +++ b/CycloBranch/gui/cSpectrumSceneWidget.h @@ -79,6 +79,27 @@ class cSpectrumSceneWidget : public QGraphicsView void exportToPNG(QString filename); + /** + \brief Hide/Show unmatched peaks. + \param state the peaks are hidden when \a state is true + */ + void hideUnmatchedPeaks(bool state); + + + /** + \brief Hide/Show matched peaks. + \param state the peaks are hidden when \a state is true + */ + void hideMatchedPeaks(bool state); + + + /** + \brief Hide/Show scrambled peaks. + \param state the peaks are hidden when \a state is true + */ + void hideScrambledPeaks(bool state); + + protected: @@ -181,12 +202,6 @@ class cSpectrumSceneWidget : public QGraphicsView private slots: - void hideUnmatchedPeaks(bool state); - - void hideMatchedPeaks(bool state); - - void hideScrambledPeaks(bool state); - void zoomIn(); void zoomOut(); diff --git a/CycloBranch/images.qrc b/CycloBranch/images.qrc index 1361d37..a67a20b 100644 --- a/CycloBranch/images.qrc +++ b/CycloBranch/images.qrc @@ -31,5 +31,7 @@ images/icons/65.png images/icons/56.png images/icons/66.png + images/icons/5.png + images/icons/25.png diff --git a/CycloBranch/images/icons/25.png b/CycloBranch/images/icons/25.png new file mode 100644 index 0000000000000000000000000000000000000000..dfe63cbd5e50f75ebf3ef904ad7cea1439c04083 GIT binary patch literal 883 zcmV-(1C0EMP)vdh^;U1uJbIZO?gvy4p1BD+E@5`yjzMMOjp^-vEz)l<|%_+Zpi(1i+o z2(1iDBTBlc6q%J;YMN_~smrBnJ3HGs?%&Tg#0;m^AAbDL|M&l%&-vXB1+!S8j&0=> zlT@R%U4J%{mYPW*bssXwLv=oy%XH}rU*!BZQ2bC<$(rWviFg_blU%fe%e_|p&p@ej zUz#%V+^sLX!jNzqggS2x=$E~o`_I5Y%bI$fZRbWxZAT@6Q15vFCF!uve*5xY1I3P= z3uukhrO7-=fm{VZ>(HA8{x=mP$t&AUvjY0xIyOb+z&0WWmIEEiODVpf29NJMz8I>$XnTGoYhzz!Y}#rgeIf-3yKg1 z1Cu~Z98BXg9p7;g0{-p}qI|ck;!9pMPSnuz#D0_2YWA9oPvGehnhVP}isr-4;;sZ@ zC1(v-OUy)r>8s2z-1;s`g8YHh(v};4G^M{Tt67`EFXxvQ;Q#g%4WXc56mcb)+|Fe1 zeBuaAOy?}?Kc1R)YKD11uA9Z#!uUWm$SaQ6W8 zb<2n#R93DL(sL-F5gZP((D{b-F4->$34xExfvnzw9dw4k^;wRlTDS&I$Cc4E%>UsJ z-SG~Iw#;lD2v!1ZuVktCpu$ZH?5on1^nMkaG#o|Dsa(BnySRDk2c7qgd4Yg@cW6+` zWmzg(dXzBJ0(+}8j)+1XU$|S^1T!pWtv)*#P;S|GhgVMa{{*8oGtIOls@wno002ov JPDHLkV1i)Nqox1= literal 0 HcmV?d00001 diff --git a/CycloBranch/images/icons/5.png b/CycloBranch/images/icons/5.png new file mode 100644 index 0000000000000000000000000000000000000000..0b0d70b1b2b91497104eccc50fb00f42996ddeb8 GIT binary patch literal 1042 zcmV+t1nv8YP)hE@c2bnslYV#97YELC^0IGCO6MxI51I4%`zn)= zQi1=pB`3rgM`Zry*hnNmPco$;t6HQAaJrq}$ER}p(~H(`ROQqpLH+o@P~>oWw;bw4 zbgCl~xb7Jh7p3OxTJZ=OZ{tlIhYr0(TbTMlg%PPc6B%^H#;hza+oBVTw}1Bf2ZbR@ zNevloI50VT!*`b4`iko}m**E~ zGSnJqwgFr^)?`wE?d7is`hvlc?$Kc~@Hr<~sVXJLh8ZQG04_ozxHBHdo`79eCq5%8ty$j^ZA+F!@`V$3dnL3R; z5nI(c*q!u$!E606klBe%iw)pDNUxo#b z?=b0ZASS}|9hMhM3v(=Hqdfhdiz5u`LVZIAN}jJyfcBj4L`!20>fd<=C8ha_HZ|z> z4xPR{BuY|^GTfQ>$-Xp$et+?von~!Tdh+s#wtsgC-9Oz(0KTy6Vbs=@LSBs=Hb3JJ zU+Z#?EzSj+cQw-^3h>8wSq@M%8qB83M=E!0)Me?D6H_FXKa}1{mzX9@`@;jLx<`Fu zo|8;#c=-!T<2Tds7BR5eL0zWN`r!8G40%PHjqzNI!(%|!d;Iz>@9*a>_=H)ngNaSA zeogN_U(0s}S#Z7tGeZ8B$QGc(jOWPa8knsJZJO$-bO{=YoaQZ&-e z$``=Nw+zJ1d>_+X%scmdi&bC1-#n*{ioPNwOaK4? M07*qoM6N<$g5G2EzW@LL literal 0 HcmV?d00001 diff --git a/CycloBranch/images/splash.cdr b/CycloBranch/images/splash.cdr index dabcc46e2698c68a245e23bbbb581263b449be44..55793117143a4ff90d2175990587b286bebbb43c 100644 GIT binary patch delta 100733 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%0A{teO#?o+yv|sWYy0x`-WPB4 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 diff --git a/CycloBranch/images/splash.png b/CycloBranch/images/splash.png index d7789d24e12e318fa0e82b09e9715238642f7968..d68602c4e1290d9141959f4b602e0e90acaf6997 100644 GIT binary patch literal 73016 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
    b{_l$KI9h%w$zeZ0vL!|FAs5X-ZDl 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 diff --git a/CycloBranch/main.cpp b/CycloBranch/main.cpp index 563dfe6..67a5703 100644 --- a/CycloBranch/main.cpp +++ b/CycloBranch/main.cpp @@ -25,8 +25,8 @@ int main(int argc, char** argv) { chdir(installdir.toStdString().c_str()); #endif qRegisterMetaType("cParameters"); - qRegisterMetaType >("vector"); - qRegisterMetaType("peptideType"); + qRegisterMetaType >("vector"); + qRegisterMetaType("ePeptideType"); qRegisterMetaType >("vector"); qRegisterMetaType("string"); qRegisterMetaType("cFragmentIons"); @@ -35,7 +35,7 @@ int main(int argc, char** argv) { QSplashScreen splash(pixmap); if(!pixmap.isNull()) { splash.show(); - splash.showMessage(QObject::tr(QString(appname + " " + appversion + " is starting ...").toStdString().c_str()), Qt::AlignLeft | Qt::AlignBottom, Qt::black); + splash.showMessage(QObject::tr(QString(appname + " " + appversion + " is starting ...").toStdString().c_str()), Qt::AlignCenter | Qt::AlignBottom, Qt::black); for (int i = 0; i < 50; i++) { if (!splash.isVisible()) { break; diff --git a/CycloBranch/parallel/cGraphReaderThread.cpp b/CycloBranch/parallel/cGraphReaderThread.cpp index fb488f2..75b5dcb 100644 --- a/CycloBranch/parallel/cGraphReaderThread.cpp +++ b/CycloBranch/parallel/cGraphReaderThread.cpp @@ -9,6 +9,7 @@ int cGraphReaderThread::getCandidatesIter(bool cterminalstartingnode, cCandidate int finish; int size; bool cycle; + int bid; if (*terminatecomputation) { return -1; @@ -18,7 +19,7 @@ int cGraphReaderThread::getCandidatesIter(bool cterminalstartingnode, cCandidate return 0; } - if ((parameters->peptidetype == lasso) && (startmodifID > 0) && (middlemodifID > 0)) { + if ((parameters->peptidetype == branchcyclic) && (startmodifID > 0) && (middlemodifID > 0)) { return 0; } @@ -32,7 +33,7 @@ int cGraphReaderThread::getCandidatesIter(bool cterminalstartingnode, cCandidate } // accumulation of middle modifications is not allowed - if (((parameters->peptidetype == branched) || (parameters->peptidetype == lasso)) && (middlemodifID != 0) && ((*graph)[nodeid][i].middlemodifID != 0)) { + if (((parameters->peptidetype == branched) || (parameters->peptidetype == branchcyclic)) && (middlemodifID != 0) && ((*graph)[nodeid][i].middlemodifID != 0)) { continue; } @@ -53,7 +54,7 @@ int cGraphReaderThread::getCandidatesIter(bool cterminalstartingnode, cCandidate ne.edgeid = i; perspectivepath.push_back(ne); - if ((parameters->peptidetype == branched) || (parameters->peptidetype == lasso)) { + if ((parameters->peptidetype == branched) || (parameters->peptidetype == branchcyclic)) { tmpmiddlemodifID = max(middlemodifID, (*graph)[nodeid][i].middlemodifID); if (tmpmiddlemodifID != middlemodifID) { middlepos = (int)composition.size(); @@ -74,19 +75,125 @@ int cGraphReaderThread::getCandidatesIter(bool cterminalstartingnode, cCandidate } else { cCandidate candidate(composition, perspectivepath, startmodifID, endmodifID, middlemodifID, middlepos); + + if (!cterminalstartingnode && (((parameters->peptidetype == linear) && !parameters->cyclicnterminus && !parameters->cycliccterminus) || (parameters->peptidetype == branched) || (parameters->peptidetype == linearpolysaccharide))) { + if (candidate.hasLastBrickArtificial(*bricksdatabasewithcombinations) && (composition.size() > 0)) { + bid = atoi(composition.back().c_str()) + 1; // offset of -H2O brick + composition.pop_back(); + composition.push_back(to_string(bid)); + candidate.setCandidate(composition, perspectivepath, startmodifID, endmodifID, middlemodifID, middlepos); + } + } + + if ((parameters->peptidetype == linear) && parameters->cyclicnterminus && candidate.hasLastBrickArtificial(*bricksdatabasewithcombinations) && (composition.size() > 0)) { + if (!cterminalstartingnode) { + bid = atoi(composition.back().c_str()) + 1; // offset of -H2O brick + } + else { + bid = atoi(composition.back().c_str()) + 2; // offset of +H2O brick + } + composition.pop_back(); + composition.push_back(to_string(bid)); + candidate.setCandidate(composition, perspectivepath, startmodifID, endmodifID, middlemodifID, middlepos); + } + +#if POLYKETIDE_SIDEROPHORES == 1 + if ((parameters->peptidetype == linearpolyketide) && candidate.hasLastBrickArtificial(*bricksdatabasewithcombinations) && (composition.size() > 0) && (perspectivepath.size() > 0)) { - if (!cterminalstartingnode && (((parameters->peptidetype == linear) && !parameters->cyclicnterminus && !parameters->cycliccterminus) || (parameters->peptidetype == branched) || (parameters->peptidetype == linearpolysaccharide))) { - if ((candidate.hasLastBrickArtificial(*bricksdatabasewithcombinations)) && (composition.size() > 0)) { - int bid = atoi(composition.back().c_str()) + 1; + if ((*graph)[perspectivepath[0].nodeid].checkIonAnnotation(l1h_ion)) { + switch (candidate.getResidueLossType(*bricksdatabasewithcombinations)) + { + case water: + bid = atoi(composition.back().c_str()) + 1; // offset of -H2O brick + break; + case h2: + bid = atoi(composition.back().c_str()) + 3; // offset of -H2 brick + break; + case h2o2: + bid = atoi(composition.back().c_str()) + 8; // offset of -H2O2 brick + break; + default: + bid = atoi(composition.back().c_str()); // nop + break; + } composition.pop_back(); composition.push_back(to_string(bid)); candidate.setCandidate(composition, perspectivepath, startmodifID, endmodifID, middlemodifID, middlepos); } + + if ((*graph)[perspectivepath[0].nodeid].checkIonAnnotation(l2h_ion)) { + switch (candidate.getResidueLossType(*bricksdatabasewithcombinations)) + { + case water: + bid = atoi(composition.back().c_str()) + 4; // offset of -O brick + break; + case h2: + bid = atoi(composition.back().c_str()); // nop + break; + case h2o2: + bid = atoi(composition.back().c_str()) + 6; // offset of -O2 brick + break; + default: + bid = atoi(composition.back().c_str()); // nop + break; + } + composition.pop_back(); + composition.push_back(to_string(bid)); + candidate.setCandidate(composition, perspectivepath, startmodifID, endmodifID, middlemodifID, middlepos); + } + + if ((*graph)[perspectivepath[0].nodeid].checkIonAnnotation(l1oh_ion)) { + switch (candidate.getResidueLossType(*bricksdatabasewithcombinations)) + { + case water: + bid = atoi(composition.back().c_str()) + 3; // offset of -H2 brick + break; + case h2: + bid = atoi(composition.back().c_str()) + 7; // offset of -H2+O brick + break; + case h2o2: + bid = atoi(composition.back().c_str()) + 1; // offset of -H2O brick + break; + default: + bid = atoi(composition.back().c_str()); // nop + break; + } + composition.pop_back(); + composition.push_back(to_string(bid)); + candidate.setCandidate(composition, perspectivepath, startmodifID, endmodifID, middlemodifID, middlepos); + } + + if ((*graph)[perspectivepath[0].nodeid].checkIonAnnotation(l2oh_ion)) { + switch (candidate.getResidueLossType(*bricksdatabasewithcombinations)) + { + case water: + bid = atoi(composition.back().c_str()); // nop + break; + case h2: + bid = atoi(composition.back().c_str()) + 5; // offset of +O brick + break; + case h2o2: + bid = atoi(composition.back().c_str()) + 4; // offset of -O brick + break; + default: + bid = atoi(composition.back().c_str()); // nop + break; + } + composition.pop_back(); + composition.push_back(to_string(bid)); + candidate.setCandidate(composition, perspectivepath, startmodifID, endmodifID, middlemodifID, middlepos); + } + } +#endif - if ((candidate.getComposition().compare("") != 0) && (!candidate.hasOnlyArtificialBricks(*bricksdatabasewithcombinations))) { + if ((candidate.getComposition().compare("") != 0) && !candidate.hasOnlyArtificialBricks(*bricksdatabasewithcombinations) && !candidate.hasLastBrickInvalid(*bricksdatabasewithcombinations)) { - if (isInPpmMassErrorTolerance(precursormass, candidate.getPrecursorMass(*bricksdatabasewithcombinations, parameters), parameters->precursormasserrortolerance)) { + if (isInPpmMassErrorTolerance(precursormass, candidate.getPrecursorMass(*bricksdatabasewithcombinations, parameters), parameters->precursormasserrortolerance) +#if POLYKETIDE_SIDEROPHORES == 1 + && (((parameters->peptidetype != linearpolyketide) && (parameters->peptidetype != cyclicpolyketide)) || candidate.checkPolyketideBlocks(*bricksdatabasewithcombinations, parameters->peptidetype)) +#endif + ) { cCandidateSet result; @@ -94,7 +201,7 @@ int cGraphReaderThread::getCandidatesIter(bool cterminalstartingnode, cCandidate candidate.revertComposition(); } - if ((parameters->peptidetype == branched) || (parameters->peptidetype == lasso)) { + if ((parameters->peptidetype == branched) || (parameters->peptidetype == branchcyclic)) { result.getSet().clear(); candidate.prepareBranchedCandidates(result, parameters->peptidetype, terminatecomputation); if (scanmode == 0) { @@ -206,7 +313,7 @@ void cGraphReaderThread::run() { } } break; - case lasso: + case branchcyclic: for (int i = 1; i <= lastsystemnode; i++) { if (i - 1/*2*/ > 0) { startmodifID = i - 1/*2*/; @@ -219,6 +326,27 @@ void cGraphReaderThread::run() { } } break; +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + for (int i = 1; i <= lastsystemnode; i++) { + if (i - 4 > 0) { + startmodifID = i - 4; + } + if (getCandidatesIter(false, candidates, i, composition, unchargedprecursormass, startmodifID, 0, 0, -1, perspectivepath, (*graph)[i].getMZRatio(), terminatecomputation) == -1) { + // terminated + return; + } + } + break; + case cyclicpolyketide: + for (int i = 1; i <= lastsystemnode; i++) { + if (getCandidatesIter(false, candidates, i, composition, unchargedprecursormass, 0, 0, 0, -1, perspectivepath, (*graph)[i].getMZRatio(), terminatecomputation) == -1) { + // terminated + return; + } + } + break; +#endif case linearpolysaccharide: for (int i = 1; i <= lastsystemnode; i++) { if (i - 1 > 0) { diff --git a/CycloBranch/parallel/cSpectrumComparatorThread.cpp b/CycloBranch/parallel/cSpectrumComparatorThread.cpp index 0a9b202..2a8586a 100644 --- a/CycloBranch/parallel/cSpectrumComparatorThread.cpp +++ b/CycloBranch/parallel/cSpectrumComparatorThread.cpp @@ -139,9 +139,17 @@ void cSpectrumComparatorThread::run() { case branched: theoreticalpeaksrealsize = tsp.compareBranched(peaklist, *bricksdatabasewithcombinations, false, *rxsequencetag, *rxsearchedsequence); break; - case lasso: - theoreticalpeaksrealsize = tsp.compareLasso(peaklist, *bricksdatabasewithcombinations, false, *rxsequencetag, *rxsearchedsequence); + case branchcyclic: + theoreticalpeaksrealsize = tsp.compareBranchCyclic(peaklist, *bricksdatabasewithcombinations, false, *rxsequencetag, *rxsearchedsequence); break; +#if POLYKETIDE_SIDEROPHORES == 1 + case linearpolyketide: + theoreticalpeaksrealsize = tsp.compareLinearPolyketideSiderophore(peaklist, *bricksdatabasewithcombinations, false, *rxsequencetag, *rxsearchedsequence); + break; + case cyclicpolyketide: + theoreticalpeaksrealsize = tsp.compareCyclicPolyketideSiderophore(peaklist, *bricksdatabasewithcombinations, false, *rxsequencetag, *rxsearchedsequence); + break; +#endif case linearpolysaccharide: theoreticalpeaksrealsize = tsp.compareLinearPolysaccharide(peaklist, *bricksdatabasewithcombinations, false, *rxsequencetag, *rxsearchedsequence); break; diff --git a/CycloBranch/readme-linux-compile.txt b/CycloBranch/readme-linux-compile.txt index d47dbc1..7b36dcb 100644 --- a/CycloBranch/readme-linux-compile.txt +++ b/CycloBranch/readme-linux-compile.txt @@ -1,6 +1,7 @@ 1) Required packages: g++ libboost-all-dev +libxerces-c-dev qt5-default libqt5svg5* diff --git a/CycloBranch/readme-macosx-compile.txt b/CycloBranch/readme-macosx-compile.txt index 5a71b33..f4b5269 100644 --- a/CycloBranch/readme-macosx-compile.txt +++ b/CycloBranch/readme-macosx-compile.txt @@ -6,6 +6,7 @@ Tested on OS X Mountain Lion: - download and install Qt 5.2.1 - download and install brew and type "brew doctor" - install boost 1.57.0 using "brew install boost" +- install xerces-c 3.1.1 using "brew install xerces-c" - install gcc using "brew install gcc" 2) Execute the following commands in the directory where CycloBranch is unpacked: diff --git a/readme.txt b/readme.txt index 43fa1a7..2969cd9 100644 --- a/readme.txt +++ b/readme.txt @@ -1,3 +1,3 @@ CycloBranch.sln - project for MS Visual Studio 2012 -- requires Qt 5.2.1 and Boost 1.57 installed +- requires Qt 5.2.1, boost 1.57 and xerces-c 3.1.1 installed