1 /* massXpert - the true massist's program.
2    --------------------------------------
3    Copyright(C) 2006,2007 Filippo Rusconi
4 
5    http://www.massxpert.org/massXpert
6 
7    This file is part of the massXpert project.
8 
9    The massxpert project is the successor to the "GNU polyxmass"
10    project that is an official GNU project package(see
11    www.gnu.org). The massXpert project is not endorsed by the GNU
12    project, although it is released ---in its entirety--- under the
13    GNU General Public License. A huge part of the code in massXpert
14    is actually a C++ rewrite of code in GNU polyxmass. As such
15    massXpert was started at the Centre National de la Recherche
16    Scientifique(FRANCE), that granted me the formal authorization to
17    publish it under this Free Software License.
18 
19    This software is free software; you can redistribute it and/or
20    modify it under the terms of the GNU  General Public
21    License version 3, as published by the Free Software Foundation.
22 
23 
24    This software is distributed in the hope that it will be useful,
25    but WITHOUT ANY WARRANTY; without even the implied warranty of
26    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
27    General Public License for more details.
28 
29    You should have received a copy of the GNU General Public License
30    along with this software; if not, write to the
31 
32    Free Software Foundation, Inc.,
33 
34    51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
35 */
36 
37 
38 /////////////////////// Qt includes
39 #include<QtGui>
40 #include<QtXml>
41 #include<QtGlobal>
42 #include<QMessageBox>
43 #include<QFileDialog>
44 
45 /////////////////////// Local includes
46 #include "globals.hpp"
47 #include "application.hpp"
48 #include "sequenceEditorWnd.hpp"
49 #include "sequenceEditorGraphicsView.hpp"
50 #include "monomerModificationDlg.hpp"
51 #include "polymerModificationDlg.hpp"
52 #include "monomerCrossLinkDlg.hpp"
53 #include "cleavageDlg.hpp"
54 #include "fragmentationDlg.hpp"
55 #include "massSearchDlg.hpp"
56 #include "sequencePurificationDlg.hpp"
57 #include "sequenceImportDlg.hpp"
58 #include "mzCalculationDlg.hpp"
59 #include "compositionsDlg.hpp"
60 #include "pkaPhPi.hpp"
61 #include "pkaPhPiDataParser.hpp"
62 #include "pkaPhPiDlg.hpp"
63 #include "crossLink.hpp"
64 #include "crossLinkerSpec.hpp"
65 #include "decimalPlacesOptionsDlg.hpp"
66 #include "monomerDictionary.hpp"
67 
68 
69 namespace massXpert
70 {
71 
SequenceEditorWnd()72   SequenceEditorWnd::SequenceEditorWnd()
73   {
74     mpa_polymer = 0;
75 
76     m_forciblyClose = false;
77     m_noDelistWnd = false;
78     m_postInitialized = false;
79 
80     if (!preInitialize())
81       {
82         QMessageBox::warning(this,
83                              tr("massXpert - Polymer Sequence Editor"),
84                              tr("Failed to pre-initialize the polymer "
85                                 "sequence editor window."),
86                              QMessageBox::Ok);
87       }
88 
89     readSettings();
90 
91     show();
92   }
93 
94 
~SequenceEditorWnd()95   SequenceEditorWnd::~SequenceEditorWnd()
96   {
97     delete mpa_resultsString;
98 
99     while(!m_propList.isEmpty())
100       delete m_propList.takeFirst();
101 
102     delete mpa_editorGraphicsScene;
103     mpa_editorGraphicsScene = 0;
104 
105     delete mpa_editorGraphicsView;
106     mpa_editorGraphicsView = 0;
107 
108     delete mpa_polymer;
109   }
110 
111 
112   void
writeSettings()113   SequenceEditorWnd::writeSettings()
114   {
115     QSettings settings
116       (static_cast<Application *>(qApp)->configSettingsFilePath(),
117        QSettings::IniFormat);
118 
119     settings.beginGroup("sequence_editor_wnd");
120     settings.setValue("geometry", saveGeometry());
121     settings.setValue("vignetteSize",
122                       mpa_editorGraphicsView->requestedVignetteSize());
123 
124     settings.setValue("hSplitterSize", m_ui.hSplitter->saveState());
125     settings.setValue("vSplitterSize", m_ui.vSplitter->saveState());
126 
127     settings.setValue("calcEngineMonomersToolBox",
128                       m_ui.calcEngineMonomersToolBox->currentIndex());
129 
130     settings.endGroup();
131   }
132 
133 
134   void
readSettings()135   SequenceEditorWnd::readSettings()
136   {
137     QSettings settings
138       (static_cast<Application *>(qApp)->configSettingsFilePath(),
139        QSettings::IniFormat);
140 
141     settings.beginGroup("sequence_editor_wnd");
142 
143     restoreGeometry(settings.value("geometry").toByteArray());
144     int vignetteSize = settings.value("vignetteSize", 32).toInt();
145     mpa_editorGraphicsView->requestVignetteSize(vignetteSize);
146     m_ui.vignetteSizeSpinBox->setValue(vignetteSize);
147 
148     m_ui.vSplitter->
149       restoreState(settings.value("vSplitterSize").toByteArray());
150     m_ui.hSplitter->
151       restoreState(settings.value("hSplitterSize").toByteArray());
152 
153     m_ui.calcEngineMonomersToolBox->
154       setCurrentIndex(settings.value("calcEngineMonomersToolBox").toInt());
155 
156     settings.endGroup();
157   }
158 
159 
160   void
closeEvent(QCloseEvent * event)161   SequenceEditorWnd::closeEvent(QCloseEvent *event)
162   {
163     Application *application = static_cast<Application *>(qApp);
164 
165     // We are asked to close the window even if it has unsaved data.
166     if (m_forciblyClose)
167       {
168         m_forciblyClose = false;
169 
170         // We have to delist the window.
171         if(!m_noDelistWnd)
172           {
173             m_noDelistWnd = false;
174             int index = application->sequenceEditorWndList()->indexOf(this);
175 
176             application->sequenceEditorWndList()->takeAt(index);
177           }
178 
179         writeSettings();
180 
181         event->accept();
182 
183         return;
184       }
185 
186     if (maybeSave())
187       {
188         // We are asked not to remove this window from the list of all
189         // the polymer chemistry definition windows. This occurs when
190         // all the windows are closed in a raw in the application
191         // object.
192 
193         if(m_noDelistWnd)
194           {
195             m_noDelistWnd = false;
196 
197             writeSettings();
198 
199             event->accept();
200 
201             return;
202           }
203 
204         // We must remove this window from the application's list of
205         // such windows:
206 
207         int index = application->sequenceEditorWndList()->indexOf(this);
208 
209         application->sequenceEditorWndList()->takeAt(index);
210 
211         writeSettings();
212 
213         event->accept();
214       }
215     else
216       {
217         event->ignore();
218       }
219   }
220 
221 
222   void
focusInEvent(QFocusEvent * event)223   SequenceEditorWnd::focusInEvent(QFocusEvent *event)
224   {
225     if (!event)
226       printf("%s", "");
227 
228     Application *application = static_cast<Application *>(qApp);
229     application->setLastFocusedWnd(this);
230   }
231 
232 
233   void
focusOutEvent(QFocusEvent * event)234   SequenceEditorWnd::focusOutEvent(QFocusEvent *event)
235   {
236     if (!event)
237       printf("%s", "");
238 
239     Application *application = static_cast<Application *>(qApp);
240     application->setLastFocusedWnd(0);
241   }
242 
243 
244 
245   // The results-exporting functions. ////////////////////////////////
246   // The results-exporting functions. ////////////////////////////////
247   // The results-exporting functions. ////////////////////////////////
248 
249   void
prepareResultsTxtString()250   SequenceEditorWnd::prepareResultsTxtString()
251   {
252     mpa_resultsString->clear();
253 
254     // First whole sequence
255     bool entities =(m_calcOptions.monomerEntities() &
256                     MXT_MONOMER_CHEMENT_MODIF);
257 
258     QString *sequence =
259       mpa_polymer->monomerText(-1, -1, entities);
260 
261     *mpa_resultsString += QObject::tr("\n---------------------------\n"
262                                       "Sequence Data: %1\n"
263                                       "---------------------------\n"
264                                       "Name: %1\n"
265                                       "Code : %2\n"
266                                       "File path: %3\n"
267                                       "Sequence: %4\n")
268       .arg(mpa_polymer->name())
269       .arg(mpa_polymer->code())
270       .arg(mpa_polymer->filePath())
271       .arg(*sequence);
272 
273     delete sequence;
274 
275     *mpa_resultsString += QObject::tr("\nIonization rule:\n");
276 
277     *mpa_resultsString += QObject::tr("Formula: %1 - ")
278       .arg(m_ionizeRule.formula());
279 
280     *mpa_resultsString += QObject::tr("Charge: %1 - ")
281       .arg(m_ionizeRule.charge());
282 
283     *mpa_resultsString += QObject::tr("Level: %1\n")
284       .arg(m_ionizeRule.level());
285 
286     *mpa_resultsString += QObject::tr("\nCalculation options:\n");
287 
288     if (entities)
289       *mpa_resultsString += QObject::tr("Account monomer modifs: yes\n");
290     else
291       *mpa_resultsString += QObject::tr("Account monomer modifs: no\n");
292 
293     entities =(m_calcOptions.monomerEntities() &
294                MXT_MONOMER_CHEMENT_CROSS_LINK);
295 
296     if (entities)
297       *mpa_resultsString += QObject::tr("Account cross-links: yes\n");
298     else
299       *mpa_resultsString += QObject::tr("Account cross-links: no\n");
300 
301 
302     // Left end and right end modifs
303     entities =(m_calcOptions.polymerEntities() &
304                MXT_POLYMER_CHEMENT_LEFT_END_MODIF ||
305                m_calcOptions.polymerEntities() &
306                MXT_POLYMER_CHEMENT_RIGHT_END_MODIF);
307 
308     if (!entities)
309       {
310         *mpa_resultsString += QObject::tr("Account ends' modifs: no\n");
311       }
312     else
313       {
314         *mpa_resultsString += QObject::tr("Account ends' modifs: yes - ");
315 
316         // Left end modif
317         entities =(m_calcOptions.polymerEntities() &
318                    MXT_POLYMER_CHEMENT_LEFT_END_MODIF);
319         if(entities)
320           {
321             *mpa_resultsString += QObject::tr("Left end modif: %1 - ")
322               .arg(mpa_polymer->leftEndModif().name());
323           }
324 
325         // Right end modif
326         entities =(m_calcOptions.polymerEntities() &
327                    MXT_POLYMER_CHEMENT_RIGHT_END_MODIF);
328         if(entities)
329           {
330             *mpa_resultsString += QObject::tr("Right end modif: %1")
331               .arg(mpa_polymer->leftEndModif().name());
332           }
333       }
334 
335     // The options about multi-region selections and multi-selection
336     // regions.
337     if (m_ui.multiRegionSelectionCheckBox->checkState())
338       {
339         *mpa_resultsString +=
340           QObject::tr("\nMulti-region selection enabled: yes\n");
341 
342         if(m_ui.regionSelectionOligomerRadioButton->isChecked())
343           {
344             *mpa_resultsString +=
345               QObject::tr("Multi-region selections are "
346                           "treated as oligomers\n");
347           }
348         else if (m_ui.regionSelectionResChainRadioButton->isChecked())
349           {
350             *mpa_resultsString +=
351               QObject::tr("Multi-region selections are treated as "
352                           "residual chains\n");
353           }
354 
355         if(m_ui.multiSelectionRegionCheckBox->checkState())
356           {
357             *mpa_resultsString +=
358               QObject::tr("Multi-selection region enabled: yes\n");
359           }
360         else
361           {
362             *mpa_resultsString +=
363               QObject::tr("Multi-selection region enabled: no\n");
364           }
365       }
366     else
367       {
368         *mpa_resultsString +=
369           QObject::tr("\nMulti-region selection enabled: no\n");
370       }
371 
372     // If there are cross-links, list all of these.
373 
374     if (mpa_polymer->crossLinkList().size())
375       {
376         *mpa_resultsString += QObject::tr("\n\nCross-links:\n");
377 
378         for(int iter = 0; iter < mpa_polymer->crossLinkList().size(); ++iter)
379           {
380             CrossLink *crossLink = mpa_polymer->crossLinkList().at(iter);
381 
382             QString *text = crossLink->prepareResultsTxtString();
383 
384             Q_ASSERT(text);
385 
386             *mpa_resultsString += *text;
387 
388             delete text;
389           }
390       }
391 
392     // Finally give the masses for the whole sequence:
393 
394     QString value;
395 
396     *mpa_resultsString += QObject::tr("\n\nWhole sequence mono mass: %1\n")
397       .arg(m_ui.monoWholeMassLineEdit->text());
398 
399     *mpa_resultsString += QObject::tr("Whole sequence avg mass: %1\n\n")
400       .arg(m_ui.avgWholeMassLineEdit->text());
401 
402 
403     // And now the selected sequence region(s).
404 
405     entities =(m_calcOptions.monomerEntities() &
406                MXT_MONOMER_CHEMENT_MODIF);
407 
408     sequence = mpa_polymer->monomerText(m_calcOptions.coordinateList(),
409                                         entities, true);
410 
411     *mpa_resultsString += *sequence;
412 
413     delete sequence;
414 
415     entities =(m_calcOptions.monomerEntities() &
416                MXT_MONOMER_CHEMENT_CROSS_LINK);
417 
418     if (entities)
419       {
420         // We should inform that no, one, more cross-links might be left out:
421         *mpa_resultsString += QObject::tr("%1\n\n")
422           .arg(m_ui.incompleteCrossLinkWarningLabel->text());
423       }
424 
425     *mpa_resultsString += QObject::tr("Selected sequence mono mass: %1\n")
426       .arg(m_ui.monoSelectionMassLineEdit->text());
427 
428     *mpa_resultsString += QObject::tr("Selected sequence avg mass: %1\n")
429       .arg(m_ui.avgSelectionMassLineEdit->text());
430   }
431 
432 
433   void
exportClipboard()434   SequenceEditorWnd::exportClipboard()
435   {
436     prepareResultsTxtString();
437 
438     QClipboard *clipboard = QApplication::clipboard();
439 
440     clipboard->setText(*mpa_resultsString, QClipboard::Clipboard);
441   }
442 
443 
444   void
exportFile()445   SequenceEditorWnd::exportFile()
446   {
447     if (m_resultsFilePath.isEmpty())
448       {
449         if(!exportSelectFile())
450           return;
451       }
452 
453     QFile file(m_resultsFilePath);
454 
455     if (!file.open(QIODevice::WriteOnly | QIODevice::Append))
456       {
457         QMessageBox::information(this,
458                                  tr("massXpert - Export Data"),
459                                  tr("Failed to open file in append mode."),
460                                  QMessageBox::Ok);
461         return;
462       }
463 
464     QTextStream stream(&file);
465     stream.setCodec("UTF-8");
466 
467     prepareResultsTxtString();
468 
469     stream << *mpa_resultsString;
470 
471     file.close();
472   }
473 
474 
475   bool
exportSelectFile()476   SequenceEditorWnd::exportSelectFile()
477   {
478     m_resultsFilePath =
479       QFileDialog::getSaveFileName(this, tr("Select File To Export Data To"),
480                                    QDir::homePath(),
481                                    tr("Data files(*.dat *.DAT)"));
482 
483     if (m_resultsFilePath.isEmpty())
484       return false;
485 
486     return true;
487 
488   }
489   // The results-exporting functions. ////////////////////////////////
490   // The results-exporting functions. ////////////////////////////////
491   // The results-exporting functions. ////////////////////////////////
492 
493 
494 
495 
496   void
createActions()497   SequenceEditorWnd::createActions()
498   {
499     // File/Close
500     closeAct = new QAction(tr("&Close"), this);
501     closeAct->setShortcut(tr("Ctrl+W"));
502     closeAct->setStatusTip(tr("Closes the sequence"));
503     connect(closeAct, SIGNAL(triggered()), this, SLOT(close()));
504 
505     // File/Save
506     saveAct = new QAction(tr("&Save"), this);
507     saveAct->setShortcut(tr("Ctrl+S"));
508     saveAct->setStatusTip(tr("Saves the sequence"));
509     connect(saveAct, SIGNAL(triggered()), this, SLOT(save()));
510 
511     // File/SaveAs
512     saveAsAct = new QAction(tr("Save&as"), this);
513     saveAsAct->setShortcut(tr("Ctrl+Alt+S"));
514     saveAsAct->setStatusTip(tr("Saves the sequence in another file"));
515     connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs()));
516 
517 
518 
519     // File/ImportRaw
520     importRawAct = new QAction(tr("&Import raw"), this);
521     importRawAct->setShortcut(tr("Ctrl+I"));
522     importRawAct->setStatusTip(tr("Imports a raw text file"));
523     connect(importRawAct, SIGNAL(triggered()), this, SLOT(importRaw()));
524 
525     // File/ImportPdb
526     importPdbProtAct = new QAction(tr("&Import PDB"), this);
527     importPdbProtAct->setShortcut(tr("Ctrl+I"));
528     importPdbProtAct->setStatusTip(tr("Imports a PDB file"));
529     connect(importPdbProtAct, SIGNAL(triggered()), this, SLOT(importPdbProt()));
530 
531 
532 
533     // File/ExportClipboard
534     exportClipboardAct = new QAction(tr("Export to &clipboard"), this);
535     exportClipboardAct->setShortcut
536       (QKeySequence(Qt::CTRL+Qt::Key_E,Qt::Key_C));
537     exportClipboardAct->setStatusTip(tr("Export as text to the clipboard"));
538     connect(exportClipboardAct, SIGNAL(triggered()),
539             this, SLOT(exportClipboard()));
540 
541     // File/ExportFile
542     exportFileAct = new QAction(tr("Export to &file"), this);
543     exportFileAct->setShortcut
544       (QKeySequence(Qt::CTRL+Qt::Key_E,Qt::Key_F));
545     exportFileAct->setStatusTip(tr("Export as text to file"));
546     connect(exportFileAct, SIGNAL(triggered()),
547             this, SLOT(exportFile()));
548 
549     // File/ExportSelectFile
550     exportSelectFileAct = new QAction(tr("&Select export file"), this);
551     exportSelectFileAct->setShortcut
552       (QKeySequence(Qt::CTRL+Qt::Key_E,Qt::Key_S));
553     exportSelectFileAct->setStatusTip(tr("Select file to export as text to"));
554     connect(exportSelectFileAct, SIGNAL(triggered()),
555             this, SLOT(exportSelectFile()));
556 
557 
558     // Edit/Copy to Clipboard
559     clipboardCopyAct = new QAction(tr("&Copy"), this);
560     clipboardCopyAct->setShortcut(tr("Ctrl+C"));
561     clipboardCopyAct->setStatusTip(tr("Copy the selected "
562                                       "region of the sequence"));
563     connect(clipboardCopyAct, SIGNAL(triggered()),
564             this, SLOT(clipboardCopy()));
565 
566     // Edit/Cut
567     clipboardCutAct = new QAction(tr("C&ut"), this);
568     clipboardCutAct->setShortcut(tr("Ctrl+X"));
569     clipboardCutAct->setStatusTip(tr("Cut the selected "
570                                      "region of the sequence"));
571     connect(clipboardCutAct, SIGNAL(triggered()),
572             this, SLOT(clipboardCut()));
573 
574     // Edit/Paste
575     clipboardPasteAct = new QAction(tr("&Paste"), this);
576     clipboardPasteAct->setShortcut(tr("Ctrl+V"));
577     clipboardPasteAct->setStatusTip(tr("Copy the selected "
578                                        "region of the sequence"));
579     connect(clipboardPasteAct, SIGNAL(triggered()),
580             this, SLOT(clipboardPaste()));
581 
582     // Edit/Find Sequence
583     findSequenceAct = new QAction(tr("&Find sequence"), this);
584     findSequenceAct->setShortcut(tr("Ctrl+F"));
585     findSequenceAct->setStatusTip(tr("Find a sequence "
586                                      "in the polymer sequence"));
587     connect(findSequenceAct, SIGNAL(triggered()),
588             this, SLOT(findSequence()));
589 
590 
591     // Chemistry/ModifyMonomer
592     modifMonomerAct = new QAction(tr("Modify &monomer(s)"), this);
593     modifMonomerAct->setShortcut
594       (QKeySequence(Qt::CTRL+Qt::Key_M,Qt::Key_M));
595     modifMonomerAct->setStatusTip(tr("Modifies monomer(s)"));
596     connect(modifMonomerAct, SIGNAL(triggered()), this,
597             SLOT(modifMonomer()));
598 
599     // Chemistry/ModifyPolymer
600     modifPolymerAct = new QAction(tr("Modify &polymer"), this);
601     modifPolymerAct->setShortcut
602       (QKeySequence(Qt::CTRL+Qt::Key_M,Qt::Key_P));
603     modifPolymerAct->setStatusTip(tr("Modifies the polymer"));
604     connect(modifPolymerAct, SIGNAL(triggered()), this,
605             SLOT(modifPolymer()));
606 
607     // Also connect that SLOT to the two buttons for left and right end
608     // modif.
609 
610     connect(m_ui.leftEndModifPushButton,
611             SIGNAL(clicked()),
612             this,
613             SLOT(modifLeftEnd()));
614 
615     connect(m_ui.rightEndModifPushButton,
616             SIGNAL(clicked()),
617             this,
618             SLOT(modifRightEnd()));
619 
620     // Chemistry/CrossLinkMonomer
621     crossLinkMonomersAct = new QAction(tr("Cross-&link monomers"), this);
622     crossLinkMonomersAct->setShortcut
623       (QKeySequence(Qt::CTRL+Qt::Key_L,Qt::Key_M));
624     crossLinkMonomersAct->setStatusTip(tr("Cross-link monomers"));
625     connect(crossLinkMonomersAct, SIGNAL(triggered()), this,
626             SLOT(crossLinkMonomers()));
627 
628     // Chemistry/Cleave
629     cleaveAct = new QAction(tr("&Cleave"), this);
630     cleaveAct->setShortcut
631       (QKeySequence(Qt::CTRL+Qt::Key_K));
632     cleaveAct->setStatusTip(tr("Cleaves the polymer"));
633     connect(cleaveAct, SIGNAL(triggered()), this,
634             SLOT(cleave()));
635 
636     // Chemistry/Fragment
637     fragmentAct = new QAction(tr("Fra&gment"), this);
638     fragmentAct->setShortcut
639       (QKeySequence(Qt::CTRL+Qt::Key_G));
640     fragmentAct->setStatusTip(tr("Fragments the polymer"));
641     connect(fragmentAct, SIGNAL(triggered()), this,
642             SLOT(fragment()));
643 
644     // Chemistry/MassSearch
645     massSearchAct = new QAction(tr("Search &masses"), this);
646     massSearchAct->setShortcut
647       (QKeySequence(Qt::CTRL+Qt::Key_M,Qt::Key_S));
648     massSearchAct->setStatusTip(tr("Search oligomers based on mass"));
649     connect(massSearchAct, SIGNAL(triggered()), this,
650             SLOT(massSearch()));
651 
652     // Chemistry/mzCalculation
653     mzCalculationAct = new QAction(tr("Compute m/z &ratios"), this);
654     mzCalculationAct->setShortcut
655       (QKeySequence(Qt::CTRL+Qt::Key_M,Qt::Key_Z));
656     mzCalculationAct->setStatusTip(tr("Compute ion charge families"));
657     connect(mzCalculationAct, SIGNAL(triggered()), this,
658             SLOT(mzCalculation()));
659 
660     // Chemistry/compositions
661     compositionsAct = new QAction(tr("Determine compositions"), this);
662     compositionsAct->setShortcut
663       (QKeySequence(Qt::CTRL+Qt::Key_D,Qt::Key_C));
664     compositionsAct->setStatusTip(tr("Determine compositions"));
665     connect(compositionsAct, SIGNAL(triggered()), this,
666             SLOT(compositions()));
667 
668     // Chemistry/isoelectricPoint
669     pkaPhPiAct = new QAction(tr("pKa pH pI"), this);
670     pkaPhPiAct->setShortcut
671       (QKeySequence(Qt::CTRL+Qt::Key_P));
672     pkaPhPiAct->setStatusTip(tr("pKa pH pI"));
673     connect(pkaPhPiAct, SIGNAL(triggered()), this,
674             SLOT(pkaPhPi()));
675 
676     // Options/decimalPlaces
677     decimalPlacesOptionsAct = new QAction(tr("Decimal places"), this);
678     decimalPlacesOptionsAct->setShortcut
679       (QKeySequence(Qt::CTRL+Qt::Key_D));
680     decimalPlacesOptionsAct->setStatusTip(tr("Decimal places"));
681     connect(decimalPlacesOptionsAct, SIGNAL(triggered()), this,
682             SLOT(decimalPlacesOptions()));
683 
684     // Calculator (preseed with whole sequence masses)
685     newCalculatorWholeSequenceMassesAct =
686       new QAction(tr("New calculator (whole seq. masses)"), this);
687 
688     newCalculatorWholeSequenceMassesAct->setStatusTip
689       (tr("Start new calculator preseeded with whole sequence masses"));
690 
691     connect(newCalculatorWholeSequenceMassesAct,
692             SIGNAL(triggered()),
693             this,
694             SLOT(newCalculatorWholeSequenceMasses()));
695 
696     // Calculator (preseed with selected sequence masses)
697     newCalculatorSelectedSequenceMassesAct =
698       new QAction(tr("New calculator (selected seq. masses)"), this);
699 
700     newCalculatorSelectedSequenceMassesAct->setStatusTip
701       (tr("Start new calculator preseeded with selected sequence masses"));
702 
703     connect(newCalculatorSelectedSequenceMassesAct,
704             SIGNAL(triggered()),
705             this,
706             SLOT(newCalculatorSelectedSequenceMasses()));
707   }
708 
709 
710   void
createMenus()711   SequenceEditorWnd::createMenus()
712   {
713     fileMenu = menuBar()->addMenu(tr("&File"));
714     fileMenu->addAction(closeAct);
715     fileMenu->addSeparator();
716     fileMenu->addAction(saveAct);
717     fileMenu->addAction(saveAsAct);
718     fileMenu->addSeparator();
719 
720     // Sequence importers menu
721     fileImportMenu = fileMenu->addMenu(tr("&Import"));
722     fileImportMenu->addAction(importRawAct);
723     fileImportMenu->addAction(importPdbProtAct);
724 
725     fileMenu->addSeparator();
726     fileMenu->addAction(exportClipboardAct);
727     fileMenu->addAction(exportFileAct);
728     fileMenu->addAction(exportSelectFileAct);
729 
730     editMenu = menuBar()->addMenu(tr("&Edit"));
731     editMenu->addAction(clipboardCopyAct);
732 
733     editMenu->addAction(clipboardCutAct);
734     editMenu->addAction(clipboardPasteAct);
735     editMenu->addSeparator();
736     editMenu->addAction(findSequenceAct);
737 
738     chemistryMenu = menuBar()->addMenu(tr("&Chemistry"));
739     chemistryMenu->addAction(modifMonomerAct);
740     chemistryMenu->addAction(modifPolymerAct);
741     chemistryMenu->addAction(crossLinkMonomersAct);
742     chemistryMenu->addAction(cleaveAct);
743     chemistryMenu->addAction(fragmentAct);
744     chemistryMenu->addAction(massSearchAct);
745     chemistryMenu->addAction(mzCalculationAct);
746     chemistryMenu->addAction(compositionsAct);
747     chemistryMenu->addAction(pkaPhPiAct);
748 
749     optionsMenu = menuBar()->addMenu(tr("&Options"));
750     optionsMenu->addAction(decimalPlacesOptionsAct);
751 
752     calculatorMenu = menuBar()->addMenu(tr("&Calculator"));
753     calculatorMenu->addAction(newCalculatorWholeSequenceMassesAct);
754     calculatorMenu->addAction(newCalculatorSelectedSequenceMassesAct);
755   }
756 
757 
758   // Before the creation of the polymer chemistry definition/polymer
759   // relationship.
760   bool
preInitialize()761   SequenceEditorWnd::preInitialize()
762   {
763     m_ui.setupUi(this);
764 
765     // The results-exporting menus. ////////////////////////////////
766     mpa_resultsString = new QString();
767     //////////////////////////////////// The results-exporting menus.
768 
769     QPixmap pixmap(":/images/massxpert-icon-32.png");
770     QIcon icon(pixmap);
771     setWindowIcon(icon);
772 
773     createActions();
774     createMenus();
775 
776     setAttribute(Qt::WA_DeleteOnClose);
777     statusBar()->setSizeGripEnabled(true);
778 
779     setFocusPolicy(Qt::StrongFocus);
780 
781     m_ui.ionizationChargeSpinBox->setRange(1, 1000000000);
782     m_ui.ionizationLevelSpinBox->setRange(0, 1000000000);
783 
784     // By default, selected regions behave as oligomers, the way one
785     // expects the system to behave when selecting oligomers that are
786     // cross-linked, for example.
787     m_ui.regionSelectionOligomerRadioButton->click();
788 
789     m_ui.incompleteCrossLinkWarningLabel->setText
790       (tr("Not accounting for cross-links"));
791 
792     mpa_editorGraphicsView = new SequenceEditorGraphicsView(this);
793     mpa_editorGraphicsView->setParent(this);
794     //    mpa_editorGraphicsView->setAlignment(Qt::AlignLeft);
795 
796     QVBoxLayout *layout = new QVBoxLayout(m_ui.graphicsViewFrame);
797 
798     layout->addWidget(static_cast<QWidget *>(mpa_editorGraphicsView));
799 
800     mp_progressBar = new QProgressBar;
801     statusBar()->addPermanentWidget(mp_progressBar);
802 
803     // We want to be able to start a drag with the mass values...
804     m_ui.monoWholeMassLineEdit->setDragEnabled(true);
805     m_ui.avgWholeMassLineEdit->setDragEnabled(true);
806 
807     m_ui.regionSelectionOligomerRadioButton->setChecked(true);
808     m_calcOptions.setSelectionType(SELECTION_TYPE_OLIGOMERS);
809 
810     m_ui.multiRegionSelectionCheckBox->setChecked(true);
811     m_ui.multiSelectionRegionCheckBox->setChecked(false);
812 
813     // Set the pointer to this window as text in the corresponding
814     // line edit widget.
815 
816     QString text = QString("%1").arg((quintptr) this);
817     m_ui.thisWndLineEdit->setText(text);
818 
819     setWindowModified(false);
820 
821     return true;
822   }
823 
824 
825   bool
postInitialize()826   SequenceEditorWnd::postInitialize()
827   {
828     Q_ASSERT(mpa_polymer);
829 
830     connect(mpa_polymer,
831             SIGNAL(crossLinksPartiallyEncompassedSignal(int)),
832             this,
833             SLOT(crossLinksPartiallyEncompassedSlot(int)));
834 
835     mpa_editorGraphicsScene = new QGraphicsScene(this);
836     mpa_editorGraphicsView->setPolymer(mpa_polymer);
837     mpa_editorGraphicsView->setScene(mpa_editorGraphicsScene);
838 
839     MonomerCodeEvaluator *evaluator =
840       new MonomerCodeEvaluator(mpa_polymer, this,
841                                m_ui.codeLineEdit,
842                                m_ui.codeErrorLineEdit);
843     mpa_editorGraphicsView->setMonomerCodeEvaluator(evaluator);
844 
845     m_ui.sequenceNameLineEdit->setText(mpa_polymer->name());
846     updateWindowTitle();
847 
848     updatePolymerEndsModifs();
849 
850     statusBar()->showMessage(tr("Ready."));
851 
852     if (!populateMonomerCodeList())
853       {
854         QMessageBox::warning(this,
855                              tr("massXpert - Polymer Sequence Editor"),
856                              tr("%1@%2\n"
857                                 "Failed to populate the monomer code list.")
858                              .arg(__FILE__)
859                              .arg(__LINE__),
860                              QMessageBox::Ok);
861 
862         return false;
863       }
864 
865     populateCalculationOptions();
866 
867     m_ui.vignetteListWidget->setSelectionMode
868       (QAbstractItemView::MultiSelection);
869 
870 
871     ////// Connection of the SIGNALS and SLOTS //////
872     connect(m_ui.vignetteSizeSpinBox,
873             SIGNAL(editingFinished()),
874             this,
875             SLOT(vignetteSizeChanged()));
876 
877     connect(m_ui.sequenceNameLineEdit,
878             SIGNAL(textChanged(const QString &)),
879             this,
880             SLOT(nameLineEditChanged(const QString &)));
881 
882     connect(m_ui.leftCapCheckBox,
883             SIGNAL(stateChanged(int)),
884             this,
885             SLOT(calculationOptionsChanged()));
886 
887     connect(m_ui.rightCapCheckBox,
888             SIGNAL(stateChanged(int)),
889             this,
890             SLOT(calculationOptionsChanged()));
891 
892     connect(m_ui.leftModifCheckBox,
893             SIGNAL(stateChanged(int)),
894             this,
895             SLOT(leftModifOptionsChanged()));
896 
897     connect(m_ui.forceLeftModifCheckBox,
898             SIGNAL(stateChanged(int)),
899             this,
900             SLOT(forceLeftModifOptionsChanged()));
901 
902     connect(m_ui.rightModifCheckBox,
903             SIGNAL(stateChanged(int)),
904             this,
905             SLOT(rightModifOptionsChanged()));
906 
907     connect(m_ui.forceRightModifCheckBox,
908             SIGNAL(stateChanged(int)),
909             this,
910             SLOT(forceRightModifOptionsChanged()));
911 
912     connect(m_ui.multiRegionSelectionCheckBox,
913             SIGNAL(stateChanged(int)),
914             this,
915             SLOT(multiRegionSelectionOptionChanged(int)));
916 
917     connect(m_ui.multiSelectionRegionCheckBox,
918             SIGNAL(stateChanged(int)),
919             this,
920             SLOT(multiSelectionRegionOptionChanged(int)));
921 
922     connect(m_ui.regionSelectionOligomerRadioButton,
923             SIGNAL(toggled(bool)),
924             this,
925             SLOT(regionSelectionOligomerOptionChanged(bool)));
926 
927     connect(m_ui.regionSelectionResChainRadioButton,
928             SIGNAL(toggled(bool)),
929             this,
930             SLOT(regionSelectionResChainOptionChanged(bool)));
931 
932     connect(m_ui.monomerModifCheckBox,
933             SIGNAL(stateChanged(int)),
934             this,
935             SLOT(monomerModifOptionChanged(int)));
936 
937     connect(m_ui.monomerCrossLinkCheckBox,
938             SIGNAL(stateChanged(int)),
939             this,
940             SLOT(monomerCrossLinkOptionChanged(int)));
941 
942     connect(m_ui.ionizationFormulaLineEdit,
943             SIGNAL(editingFinished()),
944             this,
945             SLOT(calculationOptionsChanged()));
946 
947     connect(m_ui.ionizationChargeSpinBox,
948             SIGNAL(valueChanged(int)),
949             this,
950             SLOT(calculationOptionsChanged()));
951 
952     connect(m_ui.ionizationLevelSpinBox,
953             SIGNAL(valueChanged(int)),
954             this,
955             SLOT(calculationOptionsChanged()));
956 
957     connect(m_ui.selectionLineEdit,
958             SIGNAL(textEdited(const QString &)),
959             this,
960             SLOT(coordinatesManuallyEdited(const QString &)));
961 
962 
963 
964     ////// Connection of the SIGNALS and SLOTS //////
965 
966     m_postInitialized = true;
967 
968     return true;
969   }
970 
971 
972   QProgressBar *
progressBar()973   SequenceEditorWnd::progressBar()
974   {
975     return mp_progressBar;
976   }
977 
978   bool
openSequence(QString & filePath)979   SequenceEditorWnd::openSequence(QString &filePath)
980   {
981     // We get the filePath of the sequence file.
982 
983     if (filePath.isEmpty() || !QFile::exists(filePath))
984       {
985         QMessageBox::warning(this,
986                              tr("massXpert - Polymer Sequence Editor"),
987                              tr("%1@%2\n"
988                                 "Filepath is empty, or file does not exist.")
989                              .arg(__FILE__)
990                              .arg(__LINE__),
991                              QMessageBox::Ok);
992 
993         return false;
994       }
995 
996     QString name =
997       Polymer::xmlPolymerFileGetPolChemDefName(filePath);
998 
999     if (name.isEmpty())
1000       {
1001         QMessageBox::warning
1002           (this,
1003            tr("massXpert - Polymer Sequence Editor"),
1004            tr("%1@%2\n"
1005               "Failed to get the polymer chemistry definition name.")
1006            .arg(__FILE__)
1007            .arg(__LINE__),
1008            QMessageBox::Ok);
1009 
1010         return false;
1011       }
1012 
1013     mp_polChemDef = preparePolChemDef(name);
1014 
1015     if (!mp_polChemDef)
1016       {
1017         QMessageBox::warning
1018           (this,
1019            tr("massXpert - Polymer Sequence Editor"),
1020            tr("%1@%2\n"
1021               "Failed to prepare the polymer chemistry definition.")
1022            .arg(__FILE__)
1023            .arg(__LINE__),
1024            QMessageBox::Ok);
1025 
1026         return false;
1027       }
1028 
1029     Application *application = static_cast<Application *>(qApp);
1030 
1031     mpa_polymer = new Polymer(mp_polChemDef, "NOT_SET", "NOT_SET",
1032                               application->userSpec()->userName());
1033 
1034     if (!readFile(filePath))
1035       {
1036         QMessageBox::warning
1037           (this,
1038            tr("massXpert - Polymer Sequence Editor"),
1039            tr("%1@%2\n"
1040               "Failed to load the polymer file.")
1041            .arg(__FILE__)
1042            .arg(__LINE__),
1043            QMessageBox::Ok);
1044 
1045         return false;
1046       }
1047 
1048     if (!postInitialize())
1049       return false;
1050 
1051     if (mpa_editorGraphicsView->drawSequence() == -1)
1052       {
1053         QMessageBox::warning
1054           (this,
1055            tr("massXpert - Polymer Sequence Editor"),
1056            tr("%1@%2\n"
1057               "Failed to draw the polymer sequence.")
1058            .arg(__FILE__)
1059            .arg(__LINE__),
1060            QMessageBox::Ok);
1061 
1062         return false;
1063       }
1064 
1065     focusInEvent(0);
1066 
1067     updateWholeSequenceMasses();
1068     updateSelectedSequenceMasses();
1069 
1070     return true;
1071   }
1072 
1073 
1074   bool
newSequence(QString & filePath)1075   SequenceEditorWnd::newSequence(QString &filePath)
1076   {
1077     Application *application = static_cast<Application *>(qApp);
1078 
1079 
1080     // We get the filePath of the polymer chemistry definition file.
1081 
1082     PolChemDefSpec *polChemDefSpec =
1083       application->polChemDefSpecFilePath(filePath);
1084 
1085     if (!polChemDefSpec)
1086       {
1087         QMessageBox::warning
1088           (this,
1089            tr("massXpert - Polymer Sequence Editor"),
1090            tr("%1@%2\n"
1091               "Failed to find the corresponding polymer chemistry "
1092               "filename.")
1093            .arg(__FILE__)
1094            .arg(__LINE__),
1095            QMessageBox::Ok);
1096 
1097         return false;
1098       }
1099 
1100     mp_polChemDef = preparePolChemDef(polChemDefSpec->name());
1101 
1102     if (!mp_polChemDef)
1103       {
1104         QMessageBox::warning
1105           (this,
1106            tr("massXpert - Polymer Sequence Editor"),
1107            tr("%1@%2\n"
1108               "Failed to prepare the polymer chemistry definition.")
1109            .arg(__FILE__)
1110            .arg(__LINE__),
1111            QMessageBox::Ok);
1112 
1113         return false;
1114       }
1115 
1116     mpa_polymer = new Polymer(mp_polChemDef, "NOT_SET", "NOT_SET",
1117                               application->userSpec()->userName());
1118 
1119     if (!postInitialize())
1120       return false;
1121 
1122     if (mpa_editorGraphicsView->drawSequence() == -1)
1123       {
1124         QMessageBox::warning
1125           (this,
1126            tr("massXpert - Polymer Sequence Editor"),
1127            tr("%1@%2\n"
1128               "Failed to draw the polymer sequence.")
1129            .arg(__FILE__)
1130            .arg(__LINE__),
1131            QMessageBox::Ok);
1132 
1133         return false;
1134       }
1135 
1136     focusInEvent(0);
1137 
1138     return true;
1139   }
1140 
1141 
1142   bool
readFile(const QString & filePath)1143   SequenceEditorWnd::readFile(const QString &filePath)
1144   {
1145     return mpa_polymer->renderXmlPolymerFile(filePath);
1146   }
1147 
1148 
1149   PolChemDef *
preparePolChemDef(const QString & name)1150   SequenceEditorWnd::preparePolChemDef(const QString &name)
1151   {
1152     Application *application = static_cast<Application *>(qApp);
1153 
1154     // Is a polymer definition already available, or shall we have to
1155     // load one first ?
1156     PolChemDef *polChemDef = application->polChemDefName(name);
1157     //    qDebug() << __FILE__ << __LINE__ << "polChemDef:" << polChemDef;
1158 
1159     if (!polChemDef)
1160       {
1161         // No polymer chemistry definition by that name is currently
1162         // loaded in memory. We'll have to load one.
1163 
1164         PolChemDefSpec *polChemDefSpec =
1165           application->polChemDefSpecName(name);
1166 
1167         if(!polChemDefSpec)
1168           {
1169             // No polymer chemistry definition by that name is
1170             // registered to the system. We cannot go further.
1171 
1172             QMessageBox::warning
1173               (this,
1174                tr("massXpert - Polymer Sequence Editor"),
1175                tr("%1@%2\n"
1176                   "Failed to get the polymer chemistry "
1177                   "definition specification: %3.")
1178                .arg(__FILE__)
1179                .arg(__LINE__)
1180                .arg(name),
1181                QMessageBox::Ok);
1182 
1183             return 0;
1184           }
1185 
1186         // polChemDefSpec should provide data to create a new polymer
1187         // chemistry definition object.
1188 
1189         polChemDef = new PolChemDef(*polChemDefSpec);
1190 
1191         if(!polChemDef->renderXmlPolChemDefFile())
1192           {
1193             delete polChemDef;
1194 
1195             QMessageBox::warning
1196               (this,
1197                tr("massXpert - Polymer Sequence Editor"),
1198                tr("%1@%2\n"
1199                   "Failed to render the polymer chemistry"
1200                   " definition xml file.")
1201                .arg(__FILE__)
1202                .arg(__LINE__),
1203                QMessageBox::Ok);
1204 
1205             return 0;
1206           }
1207 
1208         // We still have to initialize the m_monomerSpecList and
1209         // m_modifSpecList lists...
1210         QString dictionary = polChemDef->dirPath() + QDir::separator() +
1211           "monomer_dictionary";
1212 
1213         if(!MonomerSpec::parseFile(dictionary,
1214                                    polChemDef->monomerSpecList()))
1215           {
1216             delete polChemDef;
1217 
1218             QMessageBox::warning
1219               (this,
1220                tr("massXpert - Polymer Sequence Editor"),
1221                tr("%1@%2\n"
1222                   "Failed to parse the monomer dictionary file.")
1223                .arg(__FILE__)
1224                .arg(__LINE__),
1225                QMessageBox::Ok);
1226 
1227             return 0;
1228           }
1229 
1230         dictionary = polChemDef->dirPath() + QDir::separator() +
1231           "modification_dictionary";
1232 
1233         if(!ModifSpec::parseFile(dictionary,
1234                                  polChemDef->modifSpecList()))
1235           {
1236             delete polChemDef;
1237 
1238             QMessageBox::warning
1239               (this,
1240                tr("massXpert - Polymer Sequence Editor"),
1241                tr("%1@%2\n"
1242                   "Failed to parse the modification dictionary file.")
1243                .arg(__FILE__)
1244                .arg(__LINE__),
1245                QMessageBox::Ok);
1246 
1247             return 0;
1248           }
1249 
1250         dictionary = polChemDef->dirPath() + QDir::separator() +
1251           "cross_linker_dictionary";
1252 
1253         if(!CrossLinkerSpec::parseFile(dictionary,
1254                                        polChemDef->crossLinkerSpecList()))
1255           {
1256             delete polChemDef;
1257 
1258             QMessageBox::warning
1259               (this,
1260                tr("massXpert - Polymer Sequence Editor"),
1261                tr("%1@%2\n"
1262                   "Failed to parse the cross-linker dictionary file.")
1263                .arg(__FILE__)
1264                .arg(__LINE__),
1265                QMessageBox::Ok);
1266 
1267             return 0;
1268           }
1269 
1270         // Finally we can add this PolChemDef to the application list
1271         // of such objects.
1272         application->polChemDefList()->append(polChemDef);
1273 
1274         // Note also that we have to tell the definition where to
1275         // auto-remove itself as soon as the reference count comes down
1276         // to 0.
1277         polChemDef->setRepositoryList(application->polChemDefList());
1278 
1279         // Finally increment the reference count of the polymer
1280         // chemistry definition.
1281         polChemDef->incrementRefCount();
1282       }
1283     else
1284       polChemDef->incrementRefCount();
1285 
1286     return polChemDef;
1287   }
1288 
1289 
1290 
1291   void
populateCalculationOptions()1292   SequenceEditorWnd::populateCalculationOptions()
1293   {
1294     const PolChemDef *polChemDef = mpa_polymer->polChemDef();
1295 
1296     if (!polChemDef)
1297       return;
1298 
1299     m_ionizeRule = polChemDef->ionizeRule();
1300 
1301     m_ui.ionizationFormulaLineEdit->setText(m_ionizeRule.formula());
1302     m_ui.ionizationChargeSpinBox->setValue(m_ionizeRule.charge());
1303     m_ui.ionizationLevelSpinBox->setValue(m_ionizeRule.level());
1304 
1305     // Update the masses|m/z ratios label.
1306     if (!m_ionizeRule.level())
1307       {
1308         m_ui.massesOrMzGroupBox->setTitle(tr("Masses"));
1309       }
1310     else
1311       {
1312         m_ui.massesOrMzGroupBox->setTitle(tr("m/z ratios"));
1313       }
1314 
1315 
1316     // qDebug() << MXT_CAP_NONE << MXT_CAP_LEFT
1317     // << MXT_CAP_RIGHT << MXT_CAP_BOTH;
1318 
1319     m_ui.leftCapCheckBox->setChecked(m_calcOptions.capping() & MXT_CAP_LEFT);
1320     m_ui.rightCapCheckBox->setChecked(m_calcOptions.capping() & MXT_CAP_RIGHT);
1321 
1322     m_ui.monomerModifCheckBox->setChecked(m_calcOptions.monomerEntities() &
1323                                           MXT_MONOMER_CHEMENT_MODIF);
1324 
1325     m_ui.leftModifCheckBox->setChecked(m_calcOptions.polymerEntities() &
1326                                        MXT_POLYMER_CHEMENT_LEFT_END_MODIF);
1327     m_ui.rightModifCheckBox->setChecked(m_calcOptions.polymerEntities() &
1328                                         MXT_POLYMER_CHEMENT_RIGHT_END_MODIF);
1329   }
1330 
1331 
1332 
1333   void
calculationOptionsChanged()1334   SequenceEditorWnd::calculationOptionsChanged()
1335   {
1336     //   qDebug() << " calculationOptionsChanged";
1337 
1338     // CAPPING
1339     int value = 0;
1340 
1341     if (m_ui.leftCapCheckBox->checkState() == Qt::Checked)
1342       value |= MXT_CAP_LEFT;
1343 
1344     if (m_ui.rightCapCheckBox->checkState() == Qt::Checked)
1345       value |= MXT_CAP_RIGHT;
1346 
1347     m_calcOptions.setCapping(value);
1348 
1349     //   qDebug() << "capping: " << m_calcOptions.capping();
1350 
1351 
1352     //   qDebug() << "polymer entities: " << m_calcOptions.polymerEntities();
1353 
1354     // IONIZATION RULE
1355 
1356     QString text = m_ui.ionizationFormulaLineEdit->text();
1357 
1358     if (!text.isEmpty())
1359       {
1360         const PolChemDef *polChemDef = mpa_polymer->polChemDef();
1361         const QList<Atom *> &refList = polChemDef->atomList();
1362 
1363         Formula formula(text);
1364 
1365         if(!formula.validate(refList))
1366           {
1367             QMessageBox::warning(this,
1368                                  tr("massXpert - Polymer Sequence Editor"),
1369                                  tr("Ionization rule formula is not valid."),
1370                                  QMessageBox::Ok);
1371 
1372             m_ui.ionizationFormulaLineEdit->setFocus();
1373 
1374             return;
1375           }
1376 
1377         m_ionizeRule.setFormula(text);
1378         //       qDebug() << "ionization formula: " << m_ionizeRule.formula();
1379 
1380       }
1381 
1382     m_ionizeRule.setCharge(m_ui.ionizationChargeSpinBox->value());
1383     //   qDebug() << "ionization charge: " << m_ionizeRule.charge();
1384 
1385     m_ionizeRule.setLevel(m_ui.ionizationLevelSpinBox->value());
1386     //   qDebug() << "ionization level: " << m_ionizeRule.level();
1387 
1388     // Update the masses|m/z ratios label.
1389     if (!m_ionizeRule.level())
1390       {
1391         m_ui.massesOrMzGroupBox->setTitle(tr("Masses"));
1392       }
1393     else
1394       {
1395         m_ui.massesOrMzGroupBox->setTitle(tr("m/z ratios"));
1396       }
1397 
1398     updateWholeSequenceMasses();
1399     updateSelectedSequenceMasses();
1400   }
1401 
1402 
1403   // returns -1 in case of error, otherwise returns the number of
1404   // coordinates effectively present in the coordinateList
1405   int
coordinatesManuallyEdited(const QString & text)1406   SequenceEditorWnd::coordinatesManuallyEdited(const QString &text)
1407   {
1408     // The user is editing new selection coordinates, so make the
1409     // corresponding selection in the sequence editor window.
1410 
1411     QString oldCoordText = m_ui.selectionLineEdit->text();
1412     QString newCoordText = text;
1413 
1414     CoordinateList oldCoordList;
1415     mpa_editorGraphicsView->selectionIndices(&oldCoordList);
1416 
1417     CoordinateList newCoordList;
1418     int res = newCoordList.setCoordinates(newCoordText);
1419     if (res == -1)
1420       {
1421         m_ui.selectionLineEdit->setText(oldCoordText);
1422         return -1;
1423       }
1424 
1425     // Make sure the coordinates can fit the current selection model.
1426 
1427     bool isMultiRegion = m_ui.multiRegionSelectionCheckBox->checkState();
1428     bool isMultiSelection = m_ui.multiSelectionRegionCheckBox->checkState();
1429 
1430     if(newCoordList.size() > 1 && !isMultiRegion)
1431       {
1432         m_ui.selectionLineEdit->setText(oldCoordText);
1433         return -1;
1434       }
1435 
1436     if(newCoordList.overlap() && !isMultiSelection)
1437       {
1438         m_ui.selectionLineEdit->setText(oldCoordText);
1439         return -1;
1440       }
1441 
1442     // At this point, we should be able to change the coordinates of
1443     // the selection according to the user's wishes.
1444 
1445     mpa_editorGraphicsView->resetSelection();
1446     mpa_editorGraphicsView->setSelection(newCoordList,
1447                                          isMultiRegion,
1448                                          isMultiSelection);
1449 
1450     return newCoordList.size();
1451   }
1452 
1453 
1454   void
leftModifOptionsChanged()1455   SequenceEditorWnd::leftModifOptionsChanged()
1456   {
1457     // POLYMER MODIFICATION
1458     int flags = m_calcOptions.polymerEntities();
1459 
1460     if (m_ui.leftModifCheckBox->checkState() == Qt::Checked)
1461       {
1462         flags |= MXT_POLYMER_CHEMENT_LEFT_END_MODIF;
1463       }
1464     else
1465       {
1466         flags &= ~MXT_POLYMER_CHEMENT_LEFT_END_MODIF;
1467 
1468         if(m_ui.forceLeftModifCheckBox->checkState() == Qt::Checked)
1469           {
1470             m_ui.forceLeftModifCheckBox->toggle();
1471             flags &= ~MXT_POLYMER_CHEMENT_FORCE_LEFT_END_MODIF;
1472           }
1473       }
1474 
1475     m_calcOptions.setPolymerEntities(flags);
1476 
1477     updateWholeSequenceMasses();
1478     updateSelectedSequenceMasses(false);
1479   }
1480 
1481 
1482   void
forceLeftModifOptionsChanged()1483   SequenceEditorWnd::forceLeftModifOptionsChanged()
1484   {
1485     // POLYMER MODIFICATION
1486     int flags = m_calcOptions.polymerEntities();
1487 
1488     if (m_ui.forceLeftModifCheckBox->checkState() == Qt::Checked)
1489       {
1490         if(m_ui.leftModifCheckBox->checkState() != Qt::Checked)
1491           {
1492             m_ui.leftModifCheckBox->toggle();
1493             flags |= MXT_POLYMER_CHEMENT_LEFT_END_MODIF;
1494           }
1495 
1496         flags |= MXT_POLYMER_CHEMENT_FORCE_LEFT_END_MODIF;
1497       }
1498     else
1499       {
1500         flags &= ~MXT_POLYMER_CHEMENT_FORCE_LEFT_END_MODIF;
1501       }
1502 
1503     m_calcOptions.setPolymerEntities(flags);
1504 
1505     updateWholeSequenceMasses();
1506     updateSelectedSequenceMasses(false);
1507   }
1508 
1509 
1510   void
rightModifOptionsChanged()1511   SequenceEditorWnd::rightModifOptionsChanged()
1512   {
1513     // POLYMER MODIFICATION
1514     int flags = m_calcOptions.polymerEntities();
1515 
1516     if (m_ui.rightModifCheckBox->checkState() == Qt::Checked)
1517       {
1518         flags |= MXT_POLYMER_CHEMENT_RIGHT_END_MODIF;
1519       }
1520     else
1521       {
1522         flags &= ~MXT_POLYMER_CHEMENT_RIGHT_END_MODIF;
1523 
1524         if(m_ui.forceRightModifCheckBox->checkState() == Qt::Checked)
1525           {
1526             m_ui.forceRightModifCheckBox->toggle();
1527             flags &= ~MXT_POLYMER_CHEMENT_FORCE_RIGHT_END_MODIF;
1528           }
1529       }
1530 
1531     m_calcOptions.setPolymerEntities(flags);
1532 
1533     updateWholeSequenceMasses();
1534     updateSelectedSequenceMasses(false);
1535   }
1536 
1537 
1538   void
forceRightModifOptionsChanged()1539   SequenceEditorWnd::forceRightModifOptionsChanged()
1540   {
1541     // POLYMER MODIFICATION
1542     int flags = m_calcOptions.polymerEntities();
1543 
1544     if (m_ui.forceRightModifCheckBox->checkState() == Qt::Checked)
1545       {
1546         if(m_ui.rightModifCheckBox->checkState() != Qt::Checked)
1547           {
1548             m_ui.rightModifCheckBox->toggle();
1549             flags |= MXT_POLYMER_CHEMENT_RIGHT_END_MODIF;
1550           }
1551 
1552         flags |= MXT_POLYMER_CHEMENT_FORCE_RIGHT_END_MODIF;
1553       }
1554     else
1555       {
1556         flags &= ~MXT_POLYMER_CHEMENT_FORCE_RIGHT_END_MODIF;
1557       }
1558 
1559     m_calcOptions.setPolymerEntities(flags);
1560 
1561     updateWholeSequenceMasses();
1562     updateSelectedSequenceMasses(false);
1563   }
1564 
1565 
1566   void
multiRegionSelectionOptionChanged(int checkState)1567   SequenceEditorWnd::multiRegionSelectionOptionChanged(int checkState)
1568   {
1569     if (checkState == Qt::Unchecked)
1570       {
1571         // 	qDebug() << __FILE__ << __LINE__
1572         // 		  << "multiRegionSelectionOptionChanged: unchecked";
1573 
1574         // No multi-region selection... We remove all selections but
1575         // the last one.
1576         mpa_editorGraphicsView->resetSelectionButLastRegion();
1577         mpa_editorGraphicsView->setOngoingMouseMultiSelection(false);
1578 
1579         // Note that if no multi region selections are allowed,
1580         // multi-selection regions should be disallowed also. But we
1581         // do not want to uncheck the checkbox, we just inactivate it.
1582 
1583         m_ui.multiSelectionRegionCheckBox->setEnabled(false);
1584       }
1585     else
1586       {
1587         // Note that if multi region selections are allowed,
1588         // multi-selection regions should be possible also.
1589 
1590         m_ui.multiSelectionRegionCheckBox->setEnabled(true);
1591       }
1592 
1593     updateSelectedSequenceMasses(false);
1594   }
1595 
1596 
1597   bool
isMultiRegionSelection()1598   SequenceEditorWnd::isMultiRegionSelection()
1599   {
1600     return m_ui.multiRegionSelectionCheckBox->isChecked();
1601   }
1602 
1603 
1604   void
multiSelectionRegionOptionChanged(int checkState)1605   SequenceEditorWnd::multiSelectionRegionOptionChanged(int checkState)
1606   {
1607     if (checkState == Qt::Unchecked)
1608       {
1609         // 	qDebug() << __FILE__ << __LINE__
1610         // 		  << "multiSelectionRegionOptionChanged: unchecked";
1611 
1612         // No multi-selection regions... We remove all selections but
1613         // the first one.
1614         mpa_editorGraphicsView->resetMultiSelectionRegionsButFirstSelection();
1615         mpa_editorGraphicsView->setOngoingMouseMultiSelection(false);
1616       }
1617 
1618     updateSelectedSequenceMasses(false);
1619   }
1620 
1621 
1622   bool
isMultiSelectionRegion()1623   SequenceEditorWnd::isMultiSelectionRegion()
1624   {
1625     return m_ui.multiSelectionRegionCheckBox->isChecked();
1626   }
1627 
1628 
1629   void
regionSelectionOligomerOptionChanged(bool checked)1630   SequenceEditorWnd::regionSelectionOligomerOptionChanged(bool checked)
1631   {
1632     if (checked)
1633       {
1634         // 	qDebug() << __FILE__ << __LINE__
1635         // 		  << "regionSelectionOligomerOptionChanged checked";
1636 
1637         m_calcOptions.setSelectionType(SELECTION_TYPE_OLIGOMERS);
1638       }
1639     else
1640       m_calcOptions.setSelectionType(SELECTION_TYPE_RESIDUAL_CHAINS);
1641 
1642     updateSelectedSequenceMasses(false);
1643   }
1644 
1645 
1646   void
regionSelectionResChainOptionChanged(bool checked)1647   SequenceEditorWnd::regionSelectionResChainOptionChanged(bool checked)
1648   {
1649     if (checked)
1650       {
1651         // 	qDebug() << __FILE__ << __LINE__
1652         // 		  << "regionSelectionResChainOptionChanged checked";
1653 
1654         m_calcOptions.setSelectionType(SELECTION_TYPE_RESIDUAL_CHAINS);
1655       }
1656     else
1657       m_calcOptions.setSelectionType(SELECTION_TYPE_OLIGOMERS);
1658 
1659     updateSelectedSequenceMasses(false);
1660   }
1661 
1662 
1663   void
monomerModifOptionChanged(int checkState)1664   SequenceEditorWnd::monomerModifOptionChanged(int checkState)
1665   {
1666     int flags = m_calcOptions.monomerEntities();
1667 
1668     if (checkState == Qt::Checked)
1669       flags |= MXT_MONOMER_CHEMENT_MODIF;
1670     else
1671       flags &= ~MXT_MONOMER_CHEMENT_MODIF;
1672 
1673     //   if (flags & MXT_MONOMER_CHEMENT_MODIF)
1674     //     qDebug() << __FILE__ << __LINE__
1675     // 	      << "monomerEntities set for MXT_MONOMER_CHEMENT_MODIF";
1676     //   else
1677     //     qDebug() << __FILE__ << __LINE__
1678     // 	      << "monomerEntities NOT set for MXT_MONOMER_CHEMENT_MODIF";
1679 
1680     m_calcOptions.setMonomerEntities(flags);
1681 
1682     updateWholeSequenceMasses(true);
1683     updateSelectedSequenceMasses(true);
1684   }
1685 
1686 
1687 
1688   void
monomerCrossLinkOptionChanged(int checkState)1689   SequenceEditorWnd::monomerCrossLinkOptionChanged(int checkState)
1690   {
1691     int flags = m_calcOptions.monomerEntities();
1692 
1693     if (checkState == Qt::Checked)
1694       flags |= MXT_MONOMER_CHEMENT_CROSS_LINK;
1695     else
1696       {
1697         flags &= ~MXT_MONOMER_CHEMENT_CROSS_LINK;
1698 
1699         m_ui.incompleteCrossLinkWarningLabel->setText
1700           (tr("Not accounting for cross-links"));
1701       }
1702 
1703     //   if (flags & MXT_MONOMER_CHEMENT_CROSS_LINK)
1704     //     qDebug() << __FILE__ << __LINE__
1705     // 	      << "monomerEntities set for MXT_MONOMER_CHEMENT_CROSS_LINK";
1706     //   else
1707     //     qDebug() << __FILE__ << __LINE__
1708     // 	      << "monomerEntities NOT set for MXT_MONOMER_CHEMENT_CROSS_LINK";
1709 
1710     m_calcOptions.setMonomerEntities(flags);
1711 
1712 
1713     // When cross-links are to be accounted for, the multi-selection
1714     // region feature has to be inactivated.
1715 
1716     m_ui.multiSelectionRegionCheckBox->setChecked(false);
1717 
1718     updateWholeSequenceMasses(true);
1719     updateSelectedSequenceMasses(true);
1720   }
1721 
1722 
1723 
1724   bool
populateMonomerCodeList(bool reset)1725   SequenceEditorWnd::populateMonomerCodeList(bool reset)
1726   {
1727     QListWidgetItem *item = 0;
1728 
1729     const PolChemDef *polChemDef = mpa_polymer->polChemDef();
1730 
1731     if (!polChemDef)
1732       return true;
1733 
1734     if (reset)
1735       {
1736         while( m_ui.vignetteListWidget->count())
1737           {
1738             item = m_ui.vignetteListWidget->takeItem(0);
1739             // 	    qDebug() << __FILE__ << __LINE__
1740             // 		      << item->text().toAscii();
1741 
1742             delete item;
1743           }
1744       }
1745 
1746     for (int iter = 0; iter < polChemDef->monomerList().size(); ++iter)
1747       {
1748         Monomer *monomer = polChemDef->monomerList().at(iter);
1749 
1750         QString text = monomer->code() + "=" + monomer->name();
1751         m_ui.vignetteListWidget->addItem(text);
1752       }
1753 
1754     // It would be interesting to know which item is double-clicked.
1755 
1756     connect(m_ui.vignetteListWidget,
1757             SIGNAL(itemDoubleClicked(QListWidgetItem *)),
1758             this,
1759             SLOT(vignetteListWidgetItemDoubleClicked(QListWidgetItem *)));
1760 
1761     return true;
1762   }
1763 
1764 
1765   const PolChemDef *
polChemDef() const1766   SequenceEditorWnd::polChemDef() const
1767   {
1768     return mpa_polymer->polChemDef();
1769   }
1770 
1771 
1772   PolChemDef *
polChemDef()1773   SequenceEditorWnd::polChemDef()
1774   {
1775     return mp_polChemDef;
1776   }
1777 
1778 
1779   Polymer*
polymer()1780   SequenceEditorWnd::polymer()
1781   {
1782     return mpa_polymer;
1783   }
1784 
1785 
1786   QList<Prop *> *
propList()1787   SequenceEditorWnd::propList()
1788   {
1789     return &m_propList;
1790   }
1791 
1792 
1793   const CalcOptions &
calcOptions() const1794   SequenceEditorWnd::calcOptions() const
1795   {
1796     return m_calcOptions;
1797   }
1798 
1799 
1800   const IonizeRule &
ionizeRule() const1801   SequenceEditorWnd::ionizeRule() const
1802   {
1803     return m_ionizeRule;
1804   }
1805 
1806 
1807   void
clearCompletionsListSelection()1808   SequenceEditorWnd::clearCompletionsListSelection()
1809   {
1810     m_ui.vignetteListWidget->clearSelection();
1811   }
1812 
1813 
1814   void
completionsListSelectAt(int index)1815   SequenceEditorWnd::completionsListSelectAt(int index)
1816   {
1817     if (index == -1)
1818       {
1819         m_ui.vignetteListWidget->selectAll();
1820         return;
1821       }
1822 
1823     QListWidgetItem *item = m_ui.vignetteListWidget->item(index);
1824     item->setSelected(true);
1825   }
1826 
1827 
1828   void
setWindowModified(bool isModified)1829   SequenceEditorWnd::setWindowModified(bool isModified)
1830   {
1831     emit polymerSequenceModifiedSignal();
1832 
1833     QWidget::setWindowModified(isModified);
1834   }
1835 
1836 
1837   void
updateWindowTitle()1838   SequenceEditorWnd::updateWindowTitle()
1839   {
1840     if (mpa_polymer->filePath().isEmpty())
1841       setWindowTitle(tr("%1 %2[*]")
1842                      .arg(tr("massXpert - Polymer Sequence Editor:"))
1843                      .arg(tr("Untitled")));
1844     else
1845       {
1846         QFileInfo fileInfo(mpa_polymer->filePath());
1847 
1848         setWindowTitle(tr("%1 %2[*]")
1849                        .arg(tr("massXpert - Polymer Sequence Editor:"))
1850                        .arg(fileInfo.fileName()));
1851       }
1852   }
1853 
1854 
1855   void
getsFocus()1856   SequenceEditorWnd::getsFocus()
1857   {
1858     focusInEvent(0);
1859   }
1860 
1861 
1862   void
updateMonomerPosition(int value)1863   SequenceEditorWnd::updateMonomerPosition(int value)
1864   {
1865     QString str;
1866 
1867     if (value > 0)
1868       str.setNum(value);
1869     else
1870       str = tr("N/A");
1871 
1872     m_ui.monomerPositionLineEdit->setText(str);
1873   }
1874 
1875 
1876   void
updateWholeSequenceMasses(bool deep)1877   SequenceEditorWnd::updateWholeSequenceMasses(bool deep)
1878   {
1879     //     qDebug() << __FILE__ << __LINE__
1880     // 	      << "updateWholeSequenceMasses";
1881 
1882     m_calcOptions.setCoordinateList(Coordinates(0, mpa_polymer->size()));
1883     m_calcOptions.setDeepCalculation(deep);
1884 
1885     mpa_polymer->deionize();
1886     mpa_polymer->calculateMasses(m_calcOptions);
1887     mpa_polymer->ionize(m_ionizeRule);
1888 
1889     Application *application = static_cast<Application *>(qApp);
1890 
1891     m_ui.monoWholeMassLineEdit->
1892       setText(mpa_polymer->mono(application->locale(),
1893                                 MXP_POLYMER_DEC_PLACES));
1894     m_ui.avgWholeMassLineEdit->
1895       setText(mpa_polymer->avg(application->locale(),
1896                                MXP_POLYMER_DEC_PLACES));
1897   }
1898 
1899 
1900   void
updateSelectedSequenceMasses(bool deep)1901   SequenceEditorWnd::updateSelectedSequenceMasses(bool deep)
1902   {
1903     //     qDebug() << __FILE__ << __LINE__
1904     // 	      << "updateSelectedSequenceMasses";
1905 
1906     // If there is a factual selection(that is the selection is marked
1907     // by the selection mark, then the indexes are according to this schema:
1908 
1909     // [ATGC] -> start: 0 end: 3
1910 
1911     // while if the selection is fake, that is no actual selection is
1912     // performed, then , if cursor is located at ATGC|, then the indexes
1913     // are according to this schemaa:
1914 
1915     // ATGC| -> start: 0 end: 4
1916 
1917     // Because the calculations in the polymer are based on a for loop,
1918     // we need to adjust the values prior to setting them in the
1919     // calculation options vehicle. Note that the values set in start
1920     // and end are already "sorted", so that start <= end.
1921 
1922     CoordinateList coordinateList;
1923 
1924     // Should always return at least one item, that is the
1925     // pseudo-selection(start of sequence up to cursor index).
1926     bool realSelections =
1927       mpa_editorGraphicsView->selectionIndices(&coordinateList);
1928 
1929     if (realSelections)
1930       {
1931         // We have increment all end indices for the items in the
1932         // coordinateList by one unit. See above for the explanation.
1933         // for(int iter = 0; iter < coordinateList.size(); ++iter)
1934         //  {
1935         // No, we do not need to do this anymore.
1936         // coordinateList.at(iter)->incrementEnd();
1937         //  }
1938       }
1939 
1940     m_calcOptions.setCoordinateList(coordinateList);
1941 
1942     m_calcOptions.setDeepCalculation(deep);
1943 
1944     mpa_polymer->deionize();
1945     mpa_polymer->calculateMasses(m_calcOptions);
1946     mpa_polymer->ionize(m_ionizeRule);
1947 
1948     Application *application = static_cast<Application *>(qApp);
1949 
1950     m_ui.monoSelectionMassLineEdit->
1951       setText(mpa_polymer->mono(application->locale(),
1952                                 MXP_OLIGOMER_DEC_PLACES));
1953     m_ui.avgSelectionMassLineEdit->
1954       setText(mpa_polymer->avg(application->locale(),
1955                                MXP_OLIGOMER_DEC_PLACES));
1956 
1957     // At this point, we can display the selection coordinates:
1958     QString selectionPositions = coordinateList.positionsAsText();
1959 
1960     //     qDebug() << __FILE__ << __LINE__
1961     // 	     << "positions:" << selectionPositions;
1962 
1963     m_ui.selectionLineEdit->setText(selectionPositions);
1964   }
1965 
1966 
1967   void
wholeSequenceMasses(double * mono,double * avg)1968   SequenceEditorWnd::wholeSequenceMasses(double *mono, double *avg)
1969   {
1970     QString mass;
1971     bool ok = false;
1972     Application *application = static_cast<Application *>(qApp);
1973 
1974     // First get the text string and make a double out of it.
1975 
1976     if(mono)
1977       {
1978         mass = m_ui.monoWholeMassLineEdit->text();
1979         *mono = application->locale().toDouble(mass, &ok);
1980       }
1981 
1982     if(avg)
1983       {
1984         mass = m_ui.avgWholeMassLineEdit->text();
1985         *avg = application->locale().toDouble(mass, &ok);
1986       }
1987 
1988 
1989   }
1990 
1991 
1992 
1993   void
selectedSequenceMasses(double * mono,double * avg)1994   SequenceEditorWnd::selectedSequenceMasses(double *mono, double *avg)
1995   {
1996     QString mass;
1997     bool ok = false;
1998     Application *application = static_cast<Application *>(qApp);
1999 
2000     if(mono)
2001       {
2002         mass = m_ui.monoSelectionMassLineEdit->text();
2003         *mono = application->locale().toDouble(mass, &ok);
2004       }
2005 
2006     if(avg)
2007       {
2008         mass = m_ui.avgSelectionMassLineEdit->text();
2009         *avg = application->locale().toDouble(mass, &ok);
2010       }
2011 
2012 
2013   }
2014 
2015 
2016 
2017 
2018 
2019   bool
maybeSave()2020   SequenceEditorWnd::maybeSave()
2021   {
2022     // Returns true if we can continue(either saved ok or discard). If
2023     // save failed or cancel we return false to indicate to the caller
2024     // that something is wrong.
2025 
2026     if (isWindowModified())
2027       {
2028         QMessageBox::StandardButton ret;
2029         ret = QMessageBox::warning
2030           (this, tr("massXpert - Polymer Sequence Editor"),
2031            tr("The document %1 has been modified.\n"
2032               "Do you want to save your changes?")
2033            .arg(mpa_polymer->filePath()),
2034            QMessageBox::Save | QMessageBox::Discard
2035            | QMessageBox::Cancel);
2036 
2037         if(ret == QMessageBox::Save)
2038           {
2039             // We want to save the file. If the file has no existing
2040             // file associate the save as function will be called
2041             // automatically.
2042             return save();
2043           }
2044         else if (ret == QMessageBox::Discard)
2045           return true;
2046         else if (ret == QMessageBox::Cancel)
2047           return false;
2048       }
2049 
2050     return true;
2051   }
2052 
2053 
2054 
2055   ////////////////////////////// SLOTS ///////////////////////////////
2056   bool
save()2057   SequenceEditorWnd::save()
2058   {
2059     // We must save to an xml file. It might be that the polymer
2060     // sequence is totally new, in which case the filePath() call will
2061     // return something invalid as a QFile object. In that case we ask
2062     // the saveAs() to do the job.
2063 
2064     if (!QFile::exists(mpa_polymer->filePath()))
2065       return saveAs();
2066 
2067     if (!mpa_polymer->writeXmlFile())
2068       {
2069         statusBar()->showMessage(tr("File save failed."));
2070 
2071         QMessageBox msgBox;
2072 
2073         msgBox.setText("Failed to save the document.");
2074         msgBox.setInformativeText("Please, check that you have "
2075                                   "write permissions on the file.");
2076         msgBox.setStandardButtons(QMessageBox::Ok);
2077 
2078         msgBox.exec();
2079 
2080         return false;
2081       }
2082 
2083     statusBar()->showMessage(tr("File save succeeded."));
2084     setWindowModified(false);
2085     //  updateWindowTitle();
2086 
2087     return true;
2088   }
2089 
2090 
2091   bool
saveAs()2092   SequenceEditorWnd::saveAs()
2093   {
2094 
2095     // We must save the new contents of the polymer sequence to an xml
2096     // file that is not the file from which this sequence might have
2097     // been saved.
2098 
2099     QString filePath =
2100       QFileDialog::getSaveFileName(this, tr("Save Polymer Sequence File"),
2101                                    QDir::homePath(),
2102                                    tr("mXp files(*.mxp *.mXp *.MXP)"));
2103 
2104     if (filePath.isEmpty())
2105       {
2106         statusBar()->showMessage(tr("Filepath is empty."));
2107         return false;
2108       }
2109 
2110     mpa_polymer->setFilePath(filePath);
2111 
2112     if (!mpa_polymer->writeXmlFile())
2113       {
2114         statusBar()->showMessage(tr("File save failed."));
2115 
2116         QMessageBox msgBox;
2117 
2118         msgBox.setText("Failed to save the document.");
2119         msgBox.setInformativeText("Please, check that you have "
2120                                   "write permissions on the file.");
2121         msgBox.setStandardButtons(QMessageBox::Ok);
2122 
2123         msgBox.exec();
2124 
2125         return false;
2126       }
2127 
2128     statusBar()->showMessage(tr("File save succeeded."));
2129     setWindowModified(false);
2130     updateWindowTitle();
2131 
2132     return true;
2133   }
2134 
2135 
2136   void
importRaw()2137   SequenceEditorWnd::importRaw()
2138   {
2139     // Open a text file and make a QMxtSequence out of it. If errors,
2140     // then show the purification dialog.
2141 
2142     Sequence sequence;
2143 
2144     QString filePath;
2145     QString name;
2146 
2147 
2148     filePath =
2149       QFileDialog::getOpenFileName(this, tr("Import Raw Text File"),
2150                                    QDir::homePath(),
2151                                    tr("Any file type(*)"));
2152 
2153     if (!QFile::exists(filePath))
2154       return;
2155 
2156     // Read the sequence.
2157 
2158     QFile file(filePath);
2159 
2160     if (!file.open(QFile::ReadOnly))
2161       return;
2162 
2163     QTextStream stream(&file);
2164 
2165     while(!stream.atEnd())
2166       {
2167         QString line = stream.readLine(1000);
2168 
2169         // 	qDebug() << __FILE__ << __LINE__
2170         // 		 << "line:" << line;
2171 
2172         sequence.appendMonomerText(line);
2173       }
2174 
2175     // And now make sure the sequence is correct.
2176 
2177     //     qDebug() << __FILE__ << __LINE__
2178     // 	     << "Done streaming file."
2179 
2180     QList<int> errorList ;
2181 
2182     if (sequence.makeMonomerList(mpa_polymer->polChemDef(),
2183                                  true, &errorList) == -1)
2184       {
2185         SequencePurificationDlg *dlg =
2186           new SequencePurificationDlg(this, &sequence, &errorList);
2187 
2188         dlg->show();
2189 
2190         return;
2191       }
2192 
2193     // At this point we can paste...
2194     int size = sequence.monomerList().size();
2195     int ret = mpa_editorGraphicsView->insertSequenceAtPoint(sequence);
2196 
2197     if ( ret != size)
2198       {
2199         QMessageBox::critical(this,
2200                               tr("massXpert - Polymer Sequence Editor"),
2201                               tr("Failed to import the sequence."),
2202                               QMessageBox::Ok);
2203       }
2204   }
2205 
2206 
2207   void
importPdbProt()2208   SequenceEditorWnd::importPdbProt()
2209   {
2210     // We want to import the protein sequence data out of a PDB file.
2211 
2212     // Format of a line:
2213     // SEQRES 1 A 21 GLY ILE VAL GLU GLN CYS CYS THR SER ILE CYS SER LEU
2214 
2215     // PDB File Format - Contents Guide Version 3.20 (Sept 15, 2008)
2216     // http://www.wwpdb.org/docs.html
2217     // File:
2218     // ftp://ftp.wwpdb.org/pub/pdb/doc/format_descriptions/Format_v32_A4.pdf
2219     //
2220     // The monomer codes are 3 uppercase letters-long.
2221 
2222     // First, get to know what the PDB file is:
2223 
2224     QString filePath =
2225       QFileDialog::getOpenFileName(this,
2226                                    tr("massXpert - Import PDB Protein Sequence"),
2227                                    QDir::homePath(),
2228                                    tr("PDB files (*.PDB *.Pdb *.pdb)"));
2229 
2230     QFile file(filePath);
2231 
2232     if (!file.exists(filePath))
2233       {
2234         QMessageBox::critical(this,
2235                               tr("massXpert - Import PDB Protein Sequence"),
2236                               tr("File not found."),
2237                               QMessageBox::Ok);
2238         return;
2239       }
2240 
2241     // Make sure we can read the sequence.
2242 
2243     if (!file.open(QIODevice::ReadOnly))
2244       return;
2245 
2246     QTextStream stream(&file);
2247 
2248     // Now, ensure that we can translate the ILE-formatted codes to
2249     // the codes we are currently using in our sequence.
2250 
2251     int codeLength = polChemDef()->codeLength();
2252 
2253     // Load the dictionary file "pdb-protein-to-mxp.dic" (from
2254     // dataDir/dictionaries) by creating a dictionary object.
2255 
2256     Application *application = static_cast<Application *>(qApp);
2257     ConfigSettings *configurationSettings = application->configSettings();
2258 
2259     QString dictionaryFilePath(configurationSettings->systemDataDir() +
2260                                QDir::separator() + "dictionaries" +
2261                                QDir::separator() + "pdb-protein-to-mxp.dic");
2262 
2263     MonomerDictionary dictionary;
2264 
2265     dictionary.setFilePath(dictionaryFilePath);
2266     dictionary.setInputCodeLength(3);
2267     dictionary.setOutputCodeLength(codeLength);
2268 
2269     if (!dictionary.loadDictionary())
2270       {
2271         QMessageBox::critical(this,
2272                               tr("massXpert - Import PDB Protein Sequence"),
2273                               tr("Failed to load the dictionary."),
2274                               QMessageBox::Ok);
2275         return;
2276       }
2277 
2278     // At this point we have loaded the dictionary, let's process the
2279     // file contents to extract the protein sequence data out of it.
2280 
2281     // Create a string list where to store all the chain sequences
2282     // parsed in the file and a parallel one where to store the
2283     // corresponding chain ID.
2284     QStringList *chainStringList = new QStringList();
2285     QStringList *chainStringListId = new QStringList();
2286 
2287     // Create a string to hold the sequence that is going to be
2288     // extracted from the PDB file's lines.
2289     QString codes;
2290 
2291     // Create a sequence object we'll use to store the monomers we
2292     // have successfully parsed in.
2293     Sequence sequence;
2294 
2295     // Iterate in the file and for each line check that it matches
2296     // lines containing SEQRES records. If so, process the line to
2297     // extract the monomer codes in the style MET LEU THR GLN LYS THR
2298 
2299     // Define a regular expression that will sort out from the file
2300     // all the lines that hold a SEQRES record, and specifically one
2301     // such record that holds a protein sequence (and not a nucleic
2302     // acid sequence), which we know because codes for aminoacids are
2303     // 3-letters long in the form of GLY.  "MET LEU THR GLN LYS THR
2304     // LYS ASP ILE VAL LYS ALA THR $");
2305 
2306     QRegExp regExp("^SEQRES "
2307                    "[0-9 ][0-9 ][0-9 ] "
2308                    "[A-Z ] "
2309                    "[0-9 ][0-9 ][0-9 ][0-9 ]  "
2310                    "([A-Z][A-Z][A-Z] ){1,13}[ ]*$");
2311 
2312     QRegExpValidator validator(regExp, 0);
2313     int pos = 0;
2314 
2315     QChar currentId;
2316 
2317     while (!stream.atEnd())
2318       {
2319         // PDF file format stipulates that:
2320 
2321         // Each line in the PDB entry file consists of 80 columns. The
2322         // last character in each PDB entry should be an end-of- line
2323         // indicator.
2324 
2325         QString line = stream.readLine(80);
2326 
2327         // 	qDebug() << __FILE__ << __LINE__
2328         // 		 << "line:" << line;
2329 
2330         // Test now if the currently parsed line matches the regular
2331         // expression.
2332 
2333         if (validator.validate(line, pos) != QValidator::Acceptable)
2334           {
2335             // 	    qDebug() << __FILE__ << __LINE__
2336             // 		     << "line is faulty:" << line;
2337 
2338             // The line does not match a protein SEQRES record. Go to
2339             // next line in the file.
2340             continue;
2341           }
2342 
2343         // At this point the line is worth working on: it is a line
2344         // describing a portion of a protein sequence.
2345 
2346         // Note that the file format specification stipulates that
2347         // there might be any number of chain sequences. Their ID is
2348         // the 'B' at index 11 in the line "SEQRES 29 B 403 TYR TYR
2349         // ILE CYS GLY PRO ".
2350 
2351         QChar id = line.at(11);
2352 
2353         if (id != currentId)
2354           {
2355             // We are initiating a new chain sequence. Store the
2356             // previously chain sequence into the string list. The
2357             // syntax is that a first string with the chain ID is
2358             // added to the string list and then its sequence.
2359 
2360             if (!codes.isEmpty())
2361               {
2362                 chainStringList->append(codes);
2363                 chainStringListId->append(QString(currentId));
2364 
2365                 // 		qDebug() << __FILE__ << __LINE__
2366                 // 			 << "appending codes:"
2367                 // 			 << codes;
2368 
2369                 // Now clear the string so that we can start filling
2370                 // it with a new chain sequence.
2371                 codes.clear();
2372               }
2373 
2374             // Now update the current id.
2375             currentId = id;
2376           }
2377 
2378         //    qDebug() << __FILE__ << __LINE__
2379         //     << "line is fine:" << line;
2380 
2381 
2382         // Create a temporary string with the line.
2383         QString tempSeq(line);
2384 
2385         // 	qDebug() << __FILE__ << __LINE__
2386         // 		 << "tempSeq:" << tempSeq;
2387 
2388         // From this tempSeq string, only retain the monomer codes:
2389         // This is the string:
2390         //"SEQRES  29 B  403  TYR TYR ILE CYS GLY PRO "
2391         // follows: "ILE PRO PHE MET ARG MET GLN          "
2392 
2393         // This is what we want to retain:
2394         // "TYR TYR ILE CYS GLY PRO ILE PRO PHE MET ARG MET GLN"
2395 
2396         int firstResidueIndex =
2397           tempSeq.indexOf (QRegExp ("([A-Z][A-Z][A-Z] ){1,13}[ ]*$"), 0 );
2398 
2399         int lastResidueIndex = tempSeq.lastIndexOf(QRegExp("[A-Z][A-Z][A-Z]"));
2400 
2401         int stringLength = lastResidueIndex - firstResidueIndex;
2402 
2403         //  	qDebug() << __FILE__ << __LINE__
2404         //  		 << "firstResidueIndex:" << firstResidueIndex
2405         // 		 << "lastResidueIndex:" << lastResidueIndex;
2406 
2407         QString tempCodes = QString (" %1")
2408           .arg(tempSeq.mid(firstResidueIndex, stringLength + 3));
2409 
2410         // This is what tempCodes contains now (not the leading
2411         // space): " TYR TYR ILE CYS GLY PRO ILE PRO PHE MET ARG MET
2412         // GLN"
2413 
2414         // 	qDebug() << __FILE__ << __LINE__
2415         // 		 << " tempCodes:" <<  tempCodes;
2416 
2417         // Appends the codes from the currently parsed line into the
2418         // string to hold all the sequence in the file.
2419 
2420         codes.append(tempCodes);
2421       }
2422 
2423     // At this point all the PDB file has been read through. We still
2424     // have one ongoing codes string, if not empty which we ought to
2425     // add to the chainStringList.
2426 
2427     if (!codes.isEmpty())
2428       {
2429         chainStringList->append(codes);
2430         chainStringListId->append(QString(currentId));
2431 
2432         // 	qDebug() << __FILE__ << __LINE__
2433         // 		 << "appending codes:"
2434         // 		 << codes;
2435 
2436         // Clear the codes string to free memory.
2437         codes.clear();
2438       }
2439 
2440     // Now feed the chain string list to the translator. The returned
2441     // string list is allocated. We will transfer its ownership to the
2442     // dialog, if we use that dialog below.
2443     QStringList *translatedChainStringList =
2444       dictionary.translate(*chainStringList);
2445 
2446     if (!translatedChainStringList)
2447       {
2448         QMessageBox::critical(this,
2449                               tr("massXpert - Import PDB Protein Sequence"),
2450                               tr("Failed to import the sequence(s)."),
2451                               QMessageBox::Ok);
2452 
2453         delete chainStringList;
2454         delete chainStringListId;
2455 
2456         return;
2457       }
2458 
2459     //     for (int iter = 0 ; iter < translatedChainStringList->size(); ++iter)
2460     //       {
2461     // 	qDebug() << __FILE__ << __LINE__
2462     // 		 << chainStringListId->at(iter)
2463     // 		 << translatedChainStringList->at(iter);
2464     //       }
2465 
2466     // At this point, we will not need the initial list anymore, we
2467     // can free it and set it to 0.
2468     delete chainStringList;
2469     chainStringList = 0;
2470 
2471     // If there is only one sequence that's been imported (the file
2472     // format allows for any number of chain sequences), then we have
2473     // nothing more than to make an actual sequence with it.
2474 
2475     if (translatedChainStringList->size() == 1)
2476       {
2477         Sequence sequence(translatedChainStringList->first());
2478 
2479         // Immediately free the string lists and set them to 0 as we
2480         // are not going to use them anymore.
2481 
2482         delete chainStringListId;
2483         chainStringListId = 0;
2484         delete translatedChainStringList;
2485         translatedChainStringList = 0;
2486 
2487         // Now test if the sequence is correct.
2488 
2489         QList<int> errorList ;
2490 
2491         if (sequence.makeMonomerList(mpa_polymer->polChemDef(),
2492                                      true, &errorList) == -1)
2493           {
2494             SequencePurificationDlg *dlg =
2495               new SequencePurificationDlg(this, &sequence, &errorList);
2496 
2497             dlg->show();
2498 
2499             return;
2500           }
2501 
2502         // At this point we can paste...
2503         int size = sequence.monomerList().size();
2504         int ret = mpa_editorGraphicsView->insertSequenceAtPoint(sequence);
2505 
2506         if ( ret != size)
2507           {
2508             QMessageBox::critical(this,
2509                                   tr("massXpert - Polymer Sequence Editor"),
2510                                   tr("Failed to import the sequence."),
2511                                   QMessageBox::Ok);
2512           }
2513 
2514         return;
2515       }
2516 
2517     // If we are here, then that means that there were more than one
2518     // sequence imported from the PDB file. We have to display these
2519     // sequences to the user.
2520 
2521     // First, make sure that we have the same number of items in the
2522     // chain sequence string list and in the chain id string list.
2523 
2524     if (chainStringListId->size() != translatedChainStringList->size())
2525       {
2526         QMessageBox::critical(this,
2527                               tr("massXpert - Polymer Sequence Editor"),
2528                               tr("Failed to import the sequence:\n"
2529                                  "Number of chain sequences differs "
2530                                  "from number of chain IDs."),
2531                               QMessageBox::Ok);
2532       }
2533 
2534     // We should open the window that is specialized for displaying
2535     // imported sequences to the user.
2536 
2537     // Note that the translatedChainStringList ownership is transferred to
2538     // the dialog. We will not delete it.
2539     SequenceImportDlg *dlg = new SequenceImportDlg(this,
2540                                                    translatedChainStringList,
2541                                                    chainStringListId);
2542     dlg->show();
2543 
2544     return;
2545   }
2546 
2547 
2548   void
clipboardCopy(QClipboard::Mode mode)2549   SequenceEditorWnd::clipboardCopy(QClipboard::Mode mode)
2550   {
2551     CoordinateList coordList;
2552 
2553     if (mpa_editorGraphicsView->selectionIndices(&coordList))
2554       {
2555 
2556         // We do not do this below, because it turned out to be
2557         // impractical when working with primary selections.
2558 
2559         int selectionCount = coordList.size();
2560 
2561         // There is a real selection, which is what
2562         // we want, but if there are more than one region selections,
2563         // then inform the user.
2564 
2565         // if(selectionCount > 1)
2566         //   {
2567         //     QMessageBox::warning(this,
2568         //                          tr("massXpert - Polymer Sequence Editor"),
2569         //                          tr("The sequence exported to the clipboard "
2570         //                             "will comprise %1 region selections.")
2571         //                          .arg(selectionCount),
2572         //                          QMessageBox::Ok);
2573         //   }
2574 
2575         QString *text = new QString;
2576 
2577         for(int iter = 0; iter < selectionCount; ++iter)
2578           {
2579             Coordinates *coordinates = coordList.at(iter);
2580 
2581             QString *sequence =
2582               mpa_polymer->monomerText(coordinates->start(),
2583                                        coordinates->end(), false);
2584 
2585             *text += *sequence;
2586 
2587             delete sequence;
2588           }
2589 
2590         QClipboard *clipboard = QApplication::clipboard();
2591         clipboard->setText(*text, mode);
2592 
2593         delete text;
2594       }
2595   }
2596 
2597 
2598   void
clipboardCut(QClipboard::Mode mode)2599   SequenceEditorWnd::clipboardCut(QClipboard::Mode mode)
2600   {
2601     CoordinateList coordList;
2602 
2603     if (mpa_editorGraphicsView->selectionIndices(&coordList))
2604       {
2605         // There is a real selection, which is what we want, but if
2606         // there are more than one region selections than we cannot
2607         // perform the action.
2608         int selectionCount = coordList.size();
2609 
2610         if(selectionCount > 1)
2611           {
2612             QMessageBox::information(this,
2613                                      tr("massXpert - Polymer Sequence Editor"),
2614                                      tr("Cut operations are not supported "
2615                                         "in multi-region selection mode."),
2616                                      QMessageBox::Ok);
2617 
2618             return;
2619           }
2620 
2621         Coordinates *coordinates = coordList.last();
2622 
2623         QString *text =
2624           mpa_polymer->monomerText(coordinates->start(),
2625                                    coordinates->end(), false);
2626         Q_ASSERT(text);
2627 
2628         QClipboard *clipboard = QApplication::clipboard();
2629         clipboard->setText(*text, mode);
2630 
2631         delete text;
2632 
2633         mpa_editorGraphicsView->removeSelectedOligomer();
2634       }
2635   }
2636 
2637 
2638   void
clipboardPaste(QClipboard::Mode mode)2639   SequenceEditorWnd::clipboardPaste(QClipboard::Mode mode)
2640   {
2641     CoordinateList coordList;
2642 
2643     if (mpa_editorGraphicsView->selectionIndices(&coordList))
2644       {
2645         // There is a real selection, which is what we want, but if
2646         // there are more than one region selections than we cannot
2647         // perform the action.
2648         int selectionCount = coordList.size();
2649 
2650         if(selectionCount > 1)
2651           {
2652             QMessageBox::information(this,
2653                                      tr("massXpert - Polymer Sequence Editor"),
2654                                      tr("Paste operations are not supported "
2655                                         "in multi-region selection mode."),
2656                                      QMessageBox::Ok);
2657 
2658             return;
2659           }
2660 
2661         // There is one region selected, we have to first remove it
2662         // and then we insert the pasted sequence, which equals to a
2663         // sequence replacement.
2664 
2665         mpa_editorGraphicsView->removeSelectedOligomer();
2666       }
2667 
2668     QClipboard *clipboard = QApplication::clipboard();
2669     QString text;
2670 
2671     if (mode == QClipboard::Selection)
2672       text = clipboard->text(QClipboard::Selection);
2673     else
2674       text = clipboard->text(QClipboard::Clipboard);
2675 
2676     if (text.isEmpty())
2677       return;
2678 
2679     Sequence sequence(text);
2680 
2681     QList<int> errorList ;
2682 
2683     if (sequence.makeMonomerList(mpa_polymer->polChemDef(),
2684                                  true, &errorList) == -1)
2685       {
2686         SequencePurificationDlg *dlg =
2687           new SequencePurificationDlg(this, &sequence, &errorList);
2688 
2689         dlg->show();
2690 
2691         return;
2692       }
2693 
2694     // At this point we can paste...
2695     int size = sequence.monomerList().size();
2696     int ret = mpa_editorGraphicsView->insertSequenceAtPoint(sequence);
2697 
2698     if (ret != size)
2699       {
2700         QMessageBox::critical(this,
2701                               tr("massXpert - Polymer Sequence Editor"),
2702                               tr("Failed to paste the sequence."),
2703                               QMessageBox::Ok);
2704       }
2705   }
2706 
2707 
2708   void
clipboardClear(QClipboard::Mode mode)2709   SequenceEditorWnd::clipboardClear(QClipboard::Mode mode)
2710   {
2711     QClipboard *clipboard = QApplication::clipboard();
2712 
2713     clipboard->clear(mode);
2714   }
2715 
2716 
2717   void
findSequence()2718   SequenceEditorWnd::findSequence()
2719   {
2720     SequenceEditorFindDlg *dlg =
2721       new SequenceEditorFindDlg(this, mpa_polymer);
2722 
2723     dlg->show();
2724 
2725     return;
2726   }
2727 
2728 
2729   void
vignetteSizeChanged()2730   SequenceEditorWnd::vignetteSizeChanged()
2731   {
2732     int size = m_ui.vignetteSizeSpinBox->value();
2733 
2734     if (!m_postInitialized)
2735       return;
2736 
2737     mpa_editorGraphicsView->requestVignetteSize(size);
2738   }
2739 
2740 
2741   void
nameLineEditChanged(const QString & text)2742   SequenceEditorWnd::nameLineEditChanged(const QString & text)
2743   {
2744     mpa_polymer->setName(text);
2745     setWindowModified(true);
2746     updateWindowTitle();
2747   }
2748 
2749 
2750   void
modifMonomer()2751   SequenceEditorWnd::modifMonomer()
2752   {
2753     MonomerModificationDlg *dlg = new MonomerModificationDlg(this);
2754 
2755     dlg->show();
2756   }
2757 
2758 
2759   void
modifPolymer()2760   SequenceEditorWnd::modifPolymer()
2761   {
2762 
2763     PolymerModificationDlg *dlg = new PolymerModificationDlg(this);
2764 
2765     dlg->show();
2766   }
2767 
2768 
2769   void
modifLeftEnd()2770   SequenceEditorWnd::modifLeftEnd()
2771   {
2772 
2773     PolymerModificationDlg *dlg =
2774       new PolymerModificationDlg(this, MXT_END_LEFT);
2775 
2776     dlg->show();
2777   }
2778 
2779 
2780   void
modifRightEnd()2781   SequenceEditorWnd::modifRightEnd()
2782   {
2783 
2784     PolymerModificationDlg *dlg =
2785       new PolymerModificationDlg(this, MXT_END_RIGHT);
2786 
2787     dlg->show();
2788   }
2789 
2790 
2791 
2792   void
crossLinkMonomers()2793   SequenceEditorWnd::crossLinkMonomers()
2794   {
2795     MonomerCrossLinkDlg *dlg = new MonomerCrossLinkDlg(this);
2796 
2797     dlg->show();
2798 
2799     // Make the connection of the signal/slot pair, so that when a
2800     // crossLink changes in the polymer sequence, the
2801     // MonomerCrossLinkDlg will be triggered to redisplay the data.
2802 
2803     QObject::connect(mpa_polymer,
2804                      SIGNAL(crossLinkChangedSignal(Polymer *)),
2805                      dlg,
2806                      SLOT(crossLinkChangedSlot(Polymer *)));
2807 
2808     QObject::connect(this,
2809                      SIGNAL(polymerSequenceModifiedSignal()),
2810                      dlg,
2811                      SLOT(polymerSequenceModifiedSlot()));
2812   }
2813 
2814 
2815   void
cleave()2816   SequenceEditorWnd::cleave()
2817   {
2818     CleavageDlg *dlg = new CleavageDlg(this,
2819                                        mpa_polymer,
2820                                        mpa_polymer->polChemDef(),
2821                                        m_calcOptions,
2822                                        &m_ionizeRule);
2823     dlg->show();
2824   }
2825 
2826 
2827   void
fragment()2828   SequenceEditorWnd::fragment()
2829   {
2830     // At the moment we can only perform fragmentation on single
2831     // region selections.
2832 
2833     CoordinateList coordList;
2834 
2835     bool res = mpa_editorGraphicsView->selectionIndices(&coordList);
2836 
2837     if (!res)
2838       {
2839         QMessageBox::information(this, tr("massxpert"),
2840                                  tr("No oligomer is selected. "
2841                                     "Select an oligomer first"),
2842                                  QMessageBox::Ok);
2843         return;
2844       }
2845 
2846     if (coordList.size() > 1)
2847       {
2848         QMessageBox::information(this, tr("massxpert"),
2849                                  tr("Fragmentation simulations are not "
2850                                     "supported\nin multi-region selection "
2851                                     "mode."),
2852                                  QMessageBox::Ok);
2853         return;
2854       }
2855 
2856     if (m_calcOptions.monomerEntities() & MXT_MONOMER_CHEMENT_CROSS_LINK)
2857       {
2858         // Check what is the situation with respect to the cross-links,
2859         // which, from a fragmentation standpoint are particularly
2860         // difficult to handle. In particular, alert the user if the
2861         // select oligomer has a partial cross-link (a cross-link that
2862         // links on oligomer monomer to a monomer outside of that
2863         // oligomer).
2864 
2865         int startIndex = coordList[0]->start();
2866         int endIndex = coordList[0]->end();
2867 
2868         // Get the cross-links for the region.
2869         QList<CrossLink *> crossLinkList;
2870 
2871         int partials = 0;
2872 
2873         mpa_polymer->crossLinkList(startIndex, endIndex,
2874                                    &crossLinkList, &partials);
2875 
2876         if(partials)
2877           {
2878             QMessageBox::warning(this, tr("massxpert"),
2879                                  tr("Fragmentation calculations do not\n"
2880                                     "take into account partial cross-links.\n"
2881                                     "These partial cross-links are ignored."),
2882                                  QMessageBox::Ok);
2883           }
2884       }
2885 
2886     // No need to do this here, because the dialog window will have
2887     // its own calcOptions copy and will make this work anyway upon
2888     // initialization.
2889 
2890     //    m_calcOptions.setCoordinateList(coordList);
2891 
2892     FragmentationDlg *dlg =
2893       new FragmentationDlg(this,
2894                            mpa_polymer,
2895                            mpa_polymer->polChemDef(),
2896                            m_calcOptions,
2897                            &m_ionizeRule);
2898 
2899     dlg->show();
2900   }
2901 
2902 
2903   void
massSearch()2904   SequenceEditorWnd::massSearch()
2905   {
2906     MassSearchDlg *dlg = new MassSearchDlg(this,
2907                                            mpa_polymer,
2908                                            m_calcOptions,
2909                                            &m_ionizeRule);
2910 
2911     dlg->show();
2912   }
2913 
2914 
2915   void
mzCalculation()2916   SequenceEditorWnd::mzCalculation()
2917   {
2918     // We should try to feed the mz calculation dialog with the mass
2919     // data from the sequence editor window. If there is a real
2920     // selection, than use the masses of the selected
2921     // region(s). Otherwise use the masses for the whole sequence.
2922 
2923     CoordinateList coordList;
2924     double mono = 0;
2925     double avg = 0;
2926 
2927     if (mpa_editorGraphicsView->selectionIndices(&coordList))
2928       {
2929         // There is real selection, set the masses for that selection
2930         // in the newly created dialog window.
2931 
2932         selectedSequenceMasses(&mono, &avg);
2933       }
2934     else
2935       {
2936         wholeSequenceMasses(&mono, &avg);
2937         qDebug() << __FILE__ << __LINE__;
2938       }
2939 
2940     qDebug() << __FILE__ << __LINE__
2941              << "mono:" << mono << "avg:" << avg;
2942 
2943 
2944     MzCalculationDlg *dlg =
2945       new MzCalculationDlg(this,
2946                            mpa_polymer->polChemDef(),
2947                            &m_ionizeRule, mono, avg);
2948 
2949     dlg->show();
2950   }
2951 
2952 
2953   void
compositions()2954   SequenceEditorWnd::compositions()
2955   {
2956     CompositionsDlg *dlg =
2957       new CompositionsDlg(this,
2958                           mpa_polymer,
2959                           &m_calcOptions,
2960                           &m_ionizeRule);
2961 
2962     dlg->show();
2963   }
2964 
2965 
2966   void
pkaPhPi()2967   SequenceEditorWnd::pkaPhPi()
2968   {
2969     // Make sure we can read the data file.
2970 
2971     // Where is the data file?
2972     QString filePath = mpa_polymer->polChemDef()->dirPath() +
2973       QDir::separator() + QString("pka_ph_pi.xml");
2974 
2975     //   qDebug() << __FILE__ << __LINE__
2976     // 	    << "pkaPhPi file is" << filePath;
2977 
2978     // Allocate the lists in which to store the different monomers and
2979     // modifs allocated upon parsing of the xml file.
2980 
2981     QList<Monomer *> *monomerList = new(QList<Monomer *>);
2982     QList<Modif *> *modifList = new(QList<Modif *>);
2983 
2984     // Create the parser using the above filePath.
2985     PkaPhPiDataParser parser(mpa_polymer->polChemDef(),
2986                              filePath);
2987 
2988     // Ask that the rendering be performed.
2989     if (!parser.renderXmlFile(monomerList, modifList))
2990       {
2991         delete monomerList;
2992         delete modifList;
2993 
2994         QMessageBox::warning(this,
2995                              tr("massXpert - pKa pH pI"),
2996                              tr("%1@%2\n"
2997                                 "Failed to render xml file(%3).")
2998                              .arg(__FILE__)
2999                              .arg(__LINE__)
3000                              .arg(filePath),
3001                              QMessageBox::Ok);
3002         return;
3003       }
3004 
3005     // Allocate a new PkaPhPi instance that we'll pass to the
3006     // dialog. Ownership of that object is taken by the dialog, which
3007     // will free it.
3008     PkaPhPi *pkaPhPi = new PkaPhPi(*mpa_polymer,
3009                                    m_calcOptions,
3010                                    monomerList,
3011                                    modifList);
3012 
3013     PkaPhPiDlg *dlg = new PkaPhPiDlg(this,
3014                                      pkaPhPi,
3015                                      *mpa_polymer,
3016                                      m_calcOptions);
3017 
3018     dlg->show();
3019   }
3020 
3021 
3022   void
decimalPlacesOptions()3023   SequenceEditorWnd::decimalPlacesOptions()
3024   {
3025     DecimalPlacesOptionsDlg *dlg = new DecimalPlacesOptionsDlg(this);
3026 
3027     dlg->show();
3028   }
3029 
3030 
3031   void
newCalculatorWholeSequenceMasses()3032   SequenceEditorWnd::newCalculatorWholeSequenceMasses()
3033   {
3034     // We want to start a new calculator preseeded with the whole
3035     // sequence mono/avg masses. Let's get them right away.
3036 
3037     QString mono = m_ui.monoWholeMassLineEdit->text();
3038     QString avg = m_ui.avgWholeMassLineEdit->text();
3039 
3040     newCalculator(mono, avg);
3041   }
3042 
3043 
3044   void
newCalculatorSelectedSequenceMasses()3045   SequenceEditorWnd::newCalculatorSelectedSequenceMasses()
3046   {
3047     // We want to start a new calculator preseeded with the selected
3048     // sequence mono/avg masses. Let's get them right away.
3049 
3050     QString mono = m_ui.monoSelectionMassLineEdit->text();
3051     QString avg = m_ui.avgSelectionMassLineEdit->text();
3052 
3053     newCalculator(mono, avg);
3054   }
3055 
3056 
3057   void
newCalculator(QString mono,QString avg)3058   SequenceEditorWnd::newCalculator(QString mono, QString avg)
3059   {
3060     // To start a new calculator, we have to get the name of the file
3061     // that contains the polymer chemistry definition. Let's get that
3062     // filePath from the polymer chemistry definition of *this*
3063     // sequence editor window.
3064 
3065     QString filePath = mp_polChemDef->filePath();
3066 
3067     // Open a calculator window with the proper polymer chemistry
3068     // definition and also with the proper masses to preseed.
3069 
3070     CalculatorWnd *calculatorWnd = new CalculatorWnd(filePath, mono, avg);
3071 
3072     // At this time we have a calculator window that is fully
3073     // initialized and functional. We can add its pointer to the list
3074     // of such windows that is stored in the application object.
3075     Application *application = static_cast<Application *>(qApp);
3076     application->calculatorWndList()->append(calculatorWnd);
3077   }
3078 
3079 
3080   void
crossLinksPartiallyEncompassedSlot(int count)3081   SequenceEditorWnd::crossLinksPartiallyEncompassedSlot(int count)
3082   {
3083     m_ui.incompleteCrossLinkWarningLabel->setText
3084       (tr("Incomplete cross-links: %1").
3085        arg(count));
3086   }
3087 
3088 
3089   void
keyPressEvent(QKeyEvent * event)3090   SequenceEditorWnd::keyPressEvent(QKeyEvent *event)
3091   {
3092     event->accept();
3093   }
3094 
3095 
3096   void
updatePolymerEndsModifs()3097   SequenceEditorWnd::updatePolymerEndsModifs()
3098   {
3099 
3100     QString text;
3101 
3102     text = mpa_polymer->leftEndModif().name();
3103 
3104     if (!text.isEmpty())
3105       {
3106         m_ui.leftEndModifPushButton->setText(text);
3107       }
3108     else
3109       {
3110         m_ui.leftEndModifPushButton->setText(tr("NOT_SET"));
3111       }
3112 
3113     text = mpa_polymer->rightEndModif().name();
3114 
3115     if (!text.isEmpty())
3116       {
3117         m_ui.rightEndModifPushButton->setText(text);
3118       }
3119     else
3120       {
3121         m_ui.rightEndModifPushButton->setText(tr("NOT_SET"));
3122       }
3123   }
3124 
3125   void
vignetteListWidgetItemDoubleClicked(QListWidgetItem * item)3126   SequenceEditorWnd::vignetteListWidgetItemDoubleClicked(QListWidgetItem *item)
3127   {
3128     QString text = item->text();
3129 
3130     // The line of text has the following format:
3131 
3132     // Xcode=Name
3133 
3134     // Thus, we want to get the string prior to the '='
3135 
3136     QStringList elements = text.split("=");
3137 
3138     QString code = elements.first();
3139 
3140     // Now that we know the code, we can insert it at the right
3141     // place. First create a sequence with that code.
3142 
3143     Sequence sequence(code);
3144     QList<int> errorList;
3145 
3146     sequence.makeMonomerList(mp_polChemDef, false, &errorList);
3147 
3148     mpa_editorGraphicsView->insertSequenceAtPoint(sequence);
3149   }
3150 
3151 } // namespace massXpert
3152