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 <QMessageBox>
40 
41 
42 /////////////////////// Local includes
43 #include "application.hpp"
44 #include "monomerModificationDlg.hpp"
45 
46 
47 namespace massXpert
48 {
49 
MonomerModificationDlg(QWidget * parent)50   MonomerModificationDlg::MonomerModificationDlg(QWidget *parent)
51     : QDialog(parent)
52   {
53     Q_ASSERT(parent);
54 
55     m_ui.setupUi(this);
56 
57     mp_editorWnd = static_cast<SequenceEditorWnd *>(parent);
58 
59     populateAvailableModifList();
60     populateMonomerList();
61     populateModifAndModifiedMonomerLists();
62 
63     updateSelectionData();
64     if (m_coordinateList.size() >= 1)
65       m_ui.currentSelectionRadioButton->setChecked(true);
66 
67 
68     QSettings settings
69       (static_cast<Application *>(qApp)->configSettingsFilePath(),
70        QSettings::IniFormat);
71 
72     settings.beginGroup("monomer_modification_dlg");
73 
74     restoreGeometry(settings.value("geometry").toByteArray());
75     m_ui.hSplitter->restoreState(settings.value("hSplitter").toByteArray());
76     m_ui.vSplitter->restoreState(settings.value("vSplitter").toByteArray());
77 
78     settings.endGroup();
79 
80     connect(m_ui.updateCurrentSelectionDataPushButton,
81             SIGNAL(clicked()),
82             this,
83             SLOT(updateSelectionData()));
84 
85     connect(m_ui.modifiedMonomerListWidget,
86             SIGNAL(itemSelectionChanged()),
87             this,
88             SLOT(modifiedMonomerListWidgetItemSelectionChanged()));
89 
90     connect(m_ui.modifListWidget,
91             SIGNAL(itemSelectionChanged()),
92             this,
93             SLOT(modifListWidgetItemSelectionChanged()));
94 
95     m_ui.displayAllModifsCheckBox->setChecked(true);
96     connect(m_ui.displayAllModifsCheckBox,
97             SIGNAL(stateChanged(int)),
98             this,
99             SLOT(displayAllModifsChanged(int)));
100 
101     connect(m_ui.modifyPushButton,
102             SIGNAL(clicked()),
103             this,
104             SLOT(modify()));
105 
106     connect(m_ui.unmodifyPushButton,
107             SIGNAL(clicked()),
108             this,
109             SLOT(unmodify()));
110 
111     connect(this,
112             SIGNAL(rejected()),
113             this,
114             SLOT(close()));
115   }
116 
117 
~MonomerModificationDlg()118   MonomerModificationDlg::~MonomerModificationDlg()
119   {
120   }
121 
122 
123   void
closeEvent(QCloseEvent * event)124   MonomerModificationDlg::closeEvent(QCloseEvent *event)
125   {
126     if (event)
127       printf("%s", "");
128 
129     QSettings settings
130       (static_cast<Application *>(qApp)->configSettingsFilePath(),
131        QSettings::IniFormat);
132 
133     settings.beginGroup("monomer_modification_dlg");
134 
135     settings.setValue("geometry", saveGeometry());
136     settings.setValue("hSplitter", m_ui.hSplitter->saveState());
137     settings.setValue("vSplitter", m_ui.vSplitter->saveState());
138 
139     settings.endGroup();
140   }
141 
142 
143 
144   bool
populateAvailableModifList()145   MonomerModificationDlg::populateAvailableModifList()
146   {
147     PolChemDef *polChemDef = mp_editorWnd->polChemDef();
148     Q_ASSERT(polChemDef);
149 
150     for (int iter = 0; iter < polChemDef->modifList().size(); ++iter)
151       {
152         Modif *modif = polChemDef->modifList().at(iter);
153         Q_ASSERT(modif);
154 
155         m_ui.availableModifListWidget->addItem(modif->name());
156       }
157 
158     return true;
159   }
160 
161 
162   bool
populateModifAndModifiedMonomerLists()163   MonomerModificationDlg::populateModifAndModifiedMonomerLists()
164   {
165     // We'll need a pointer to the polymer sequence.
166     Polymer *polymer = mp_editorWnd->polymer();
167 
168     m_ui.modifiedMonomerListWidget->clear();
169     m_ui.modifListWidget->clear();
170 
171     // For all the monomers in the polymer sequence, check which
172     // modifications they bear. If any, add their name and monomer
173     // pointer to the list.
174 
175     for (int iter = 0; iter < polymer->size(); ++iter)
176       {
177         const Monomer *monomer = polymer->at(iter);
178 
179         QList<Modif *> *list = monomer->modifList();
180 
181         if(!list || list->isEmpty())
182           continue;
183 
184         // At this point we know the monomer is modified. Let's take the
185         // opportunity to add this monomer to the list of modified
186         // monomers.
187 
188         QString text = QString("%1/%2/%3")
189           .arg(monomer->code())
190           .arg(iter + 1)
191           .arg((quintptr) monomer);
192 
193         m_ui.modifiedMonomerListWidget->addItem(text);
194 
195         for(int jter = 0; jter < list->size(); ++jter)
196           {
197             Modif *modif = list->at(jter);
198 
199             QString text = QString("%1/%2/%3/%4/%5")
200               .arg(modif->name())
201               .arg(monomer->code())
202               .arg(iter + 1)
203               .arg((quintptr) modif)
204               .arg((quintptr) monomer);
205 
206             m_ui.modifListWidget->addItem(text);
207           }
208       }
209 
210     return true;
211   }
212 
213 
214   bool
populateModifiedMonomerList()215   MonomerModificationDlg::populateModifiedMonomerList()
216   {
217     // We'll need a pointer to the polymer sequence.
218     Polymer *polymer = mp_editorWnd->polymer();
219 
220     m_ui.modifiedMonomerListWidget->clear();
221 
222     // For all the monomers in the polymer sequence, check if they are
223     // modified. If so, add their code position and monomer pointer to
224     // the list.
225 
226     for (int iter = 0; iter < polymer->size(); ++iter)
227       {
228         const Monomer *monomer = polymer->at(iter);
229 
230         QList<Modif *> *list = monomer->modifList();
231 
232         if(!list || list->isEmpty())
233           continue;
234 
235         QString text = QString("%1/%2/%3")
236           .arg(monomer->code())
237           .arg(iter + 1)
238           .arg((quintptr) monomer);
239 
240         m_ui.modifiedMonomerListWidget->addItem(text);
241       }
242 
243     return true;
244   }
245 
246 
247   bool
populateModifList(bool all)248   MonomerModificationDlg::populateModifList(bool all)
249   {
250     // We'll need a pointer to the polymer sequence.
251     Polymer *polymer = mp_editorWnd->polymer();
252 
253     // First-off remove all the items.
254     m_ui.modifListWidget->clear();
255 
256     if (all)
257       {
258         // For all the monomers in the polymer sequence, check which
259         // modifications they bear. If any, add their name and monomer
260         // pointer to the list.
261 
262         for(int iter = 0; iter < polymer->size(); ++iter)
263           {
264             const Monomer *monomer = polymer->at(iter);
265 
266             QList<Modif *> *modifList = monomer->modifList();
267 
268             if (!modifList || modifList->isEmpty())
269               continue;
270 
271             for (int jter = 0; jter < modifList->size(); ++jter)
272               {
273                 Modif *modif = modifList->at(jter);
274 
275                 QString text = QString("%1/%2/%3/%4/%5")
276                   .arg(modif->name())
277                   .arg(monomer->code())
278                   .arg(iter + 1)
279                   .arg((quintptr) modif)
280                   .arg((quintptr) monomer);
281 
282                 m_ui.modifListWidget->addItem(text);
283               }
284           }
285       }
286     else
287       {
288         // We are interested only in the modifs for the currently
289         // selected cross-linked monomers(if any) in the
290         // modifiedMonomerListWidget.
291 
292         QList<QListWidgetItem *> selectedList =
293           m_ui.modifiedMonomerListWidget->selectedItems();
294 
295         if(!selectedList.size())
296           return true;
297 
298         for(int iter = 0; iter < selectedList.size(); ++iter)
299           {
300             QListWidgetItem *item = selectedList.at(iter);
301 
302             // What's the text of the item ?
303             QString text = item->text();
304 
305             // The string is in the form "code/pos/monomerPtr"
306 
307             QStringList stringList = text.split('/',
308                                                 QString::SkipEmptyParts,
309                                                 Qt::CaseSensitive );
310 
311             // The monomer position is the second string in the list.
312             bool ok = false;
313             int pos = stringList.at(1).toInt(&ok);
314 
315             if (!pos && !ok)
316               return false;
317 
318             // What if the sequence changed and the monomer is no more
319             // in a valid range? We want to avoid a crash. See below for
320             // an even better sanity check.
321             if (pos - 1 < 0 || pos > polymer->size())
322               {
323                 QMessageBox::warning(this,
324                                      tr("massXpert - Modify monomers"),
325                                      tr("%1@%2\n"
326                                         "The monomer index does not correspond "
327                                         "to a valid polymer sequence range.\n"
328                                         "Avoid modifying the sequence while "
329                                         "working with modifications.")
330                                      .arg(__FILE__)
331                                      .arg(__LINE__),
332                                      QMessageBox::Ok);
333 
334                 populateModifAndModifiedMonomerLists();
335 
336                 return false;
337               }
338 
339             // The monomer(quintptr) pointer is the third string in the list.
340             ok = false;
341             quintptr pointerCastToInt = stringList.at(2).toInt(&ok);
342             Monomer *monomer = reinterpret_cast<Monomer *>(pointerCastToInt);
343 
344             // Sanity check, are we dealing with the same monomer now
345             // compared to the one of which the item was displayed when
346             // the window was opened ?
347             if (monomer != polymer->at(pos - 1))
348               {
349                 QMessageBox::warning(this,
350                                      tr("massXpert - Modify monomers"),
351                                      tr("%1@%2\n"
352                                         "The monomer selected does not "
353                                         "correspond to a valid sequence "
354                                         "monomer.\n"
355                                         "Avoid modifying the sequence while "
356                                         "working with modifications.")
357                                      .arg(__FILE__)
358                                      .arg(__LINE__),
359                                      QMessageBox::Ok);
360 
361                 populateModifAndModifiedMonomerLists();
362 
363                 return false;
364               }
365 
366             // Iterate in the monomer's modifications and for each one
367             // create a new item.
368 
369             QList<Modif *> *modifList = monomer->modifList();
370 
371             // It is not possible that the monomer has no modif,
372             // otherwise how come would it be listed in the list of
373             // modified monomers.
374 
375             Q_ASSERT(modifList && !modifList->isEmpty());
376 
377             for (int jter = 0; jter < modifList->size(); ++jter)
378               {
379                 Modif *modif = modifList->at(jter);
380 
381                 QString text = QString("%1/%2/%3/%4/%5")
382                   .arg(modif->name())
383                   .arg(monomer->code())
384                   .arg(pos)
385                   .arg((quintptr) modif)
386                   .arg((quintptr) monomer);
387 
388                 m_ui.modifListWidget->addItem(text);
389               }
390           }
391         // End of
392         // for (int iter = 0; iter < selectedList.size(); ++iter)
393       }
394     // End of else , that is we only display modifs for selected
395     // monomers.
396 
397     return true;
398   }
399 
400 
401 
402   bool
populateMonomerList()403   MonomerModificationDlg::populateMonomerList()
404   {
405     PolChemDef *polChemDef = mp_editorWnd->polChemDef();
406     Q_ASSERT(polChemDef);
407 
408     for (int iter = 0; iter < polChemDef->monomerList().size(); ++iter)
409       {
410         Monomer *monomer = polChemDef->monomerList().at(iter);
411         Q_ASSERT(monomer);
412 
413         QString text = monomer->code() + '=' + monomer->name();
414 
415         m_ui.monomerListWidget->addItem(text);
416       }
417 
418     return true;
419   }
420 
421 
422   void
modifiedMonomerListWidgetItemSelectionChanged()423   MonomerModificationDlg::modifiedMonomerListWidgetItemSelectionChanged()
424   {
425     // When an item is selected in the list of modified monomers, then
426     // that means that the user does not want *all* the modifs to be
427     // listed.
428 
429     m_ui.displayAllModifsCheckBox->setChecked(false);
430 
431     // Update the modif list data by listing only the modifs of the
432     // currently selected monomer.
433 
434     populateModifList(false);
435   }
436 
437 
438   void
modifListWidgetItemSelectionChanged()439   MonomerModificationDlg::modifListWidgetItemSelectionChanged()
440   {
441     //   qDebug() << __FILE__ << __LINE__
442     // 	    << "MonomerModificationDlg::"
443     //     "modifListWidgetItemSelectionChanged()";
444   }
445 
446 
displayAllModifsChanged(int checkState)447   void MonomerModificationDlg::displayAllModifsChanged(int checkState)
448   {
449     // When checked, we should list all the modifs in the
450     // modifListWidget, and not only the modifs for the currently
451     // selected modified monomer.
452 
453     if (checkState == Qt::Checked)
454       populateModifList(true);
455     else
456       populateModifList(false);
457   }
458 
459 
460   bool
updateSelectionData()461   MonomerModificationDlg::updateSelectionData()
462   {
463     // There might be more than one region selections. So get all
464     // these coordinates !
465 
466     bool res = mp_editorWnd->mpa_editorGraphicsView->
467       selectionIndices(&m_coordinateList);
468 
469     if (res)
470       {
471         // If there are more than one region selection or if there is
472         // one region selection but spanning more than one monomer,
473         // then set the target to be "current selected sequence".
474 
475         if(m_coordinateList.size() > 1)
476           {
477             // Apparently there are multiple regions selected.
478 
479             m_ui.currentSelectionLineEdit->
480               setText(m_coordinateList.positionsAsText());
481           }
482         else
483           {
484             Coordinates *coordinates = m_coordinateList.first();
485 
486             if (coordinates->start() == coordinates->end())
487               {
488 
489                 // Construct a string with both the monomer code and
490                 // its position in the sequence.
491 
492                 Polymer *polymer = mp_editorWnd->polymer();
493                 const Monomer *monomer =
494                   polymer->monomerList().at(coordinates->start());
495 
496                 QString text = tr("%1 at pos. %2")
497                   .arg(monomer->code())
498                   .arg(coordinates->start() + 1);
499 
500                 m_ui.currentSelectionLineEdit->setText(text);
501               }
502             else
503               m_ui.currentSelectionLineEdit->
504                 setText(m_coordinateList.positionsAsText());
505           }
506       }
507 
508     // Return if there was a selection(multiple-region or not) or
509     // not.
510     return res;
511   }
512 
513 
514 
515   int
prepareIndicesList()516   MonomerModificationDlg::prepareIndicesList()
517   {
518     Polymer *polymer = mp_editorWnd->polymer();
519 
520     m_indicesList.clear();
521 
522     bool isSelectionPresent = updateSelectionData();
523 
524     if (m_ui.currentSelectionRadioButton->isChecked())
525       {
526         // If there is no selection.
527         if(!isSelectionPresent)
528           return 0;
529 
530         // Now, for each Coordinates in the CoordinateList, append the
531         // indices...
532 
533         for(int iter = 0; iter < m_coordinateList.size(); ++iter)
534           {
535             Coordinates *coordinates = m_coordinateList.at(iter);
536 
537             for (int iter = coordinates->start();
538                  iter < coordinates->end()  + 1; ++iter)
539               m_indicesList.append(iter);
540           }
541       }
542     else if (m_ui.sameCodeRadioButton->isChecked())
543       {
544         // Get the code of the currently selected monomer.
545         if(!isSelectionPresent)
546           return 0;
547 
548         if(m_coordinateList.size() > 1)
549           return 0;
550 
551         Coordinates *coordinates = m_coordinateList.at(0);
552 
553         if(coordinates->start() != coordinates->end())
554           return 0;
555 
556         QString code =
557           polymer->monomerList().at(coordinates->start())->code();
558 
559         for(int iter = 0; iter < polymer->monomerList().size(); ++iter)
560           {
561             const Monomer *monomer = polymer->monomerList().at(iter);
562             Q_ASSERT(monomer);
563 
564             if (monomer->code() == code)
565               m_indicesList.append(iter);
566           }
567       }
568     else if (m_ui.fromListRadioButton->isChecked())
569       {
570         QList<QListWidgetItem *> selectedList =
571           m_ui.monomerListWidget->selectedItems();
572 
573         if(!selectedList.size())
574           return 0;
575 
576         for(int iter = 0; iter < selectedList.size(); ++iter)
577           {
578             QListWidgetItem *item = selectedList.at(iter);
579 
580             QString text = item->text();
581 
582             int index = text.indexOf('=');
583             Q_ASSERT(index > 0);
584 
585             QString code = text.left(index);
586 
587             for (int jter = 0 ; jter < polymer->monomerList().size(); ++jter)
588               {
589                 const Monomer *monomer = polymer->monomerList().at(jter);
590                 Q_ASSERT(monomer);
591 
592                 if(monomer->code() == code)
593                   m_indicesList.append(jter);
594               }
595           }
596       }
597     else if (m_ui.allRadioButton->isChecked())
598       {
599         for(int iter = 0; iter < polymer->monomerList().size(); ++iter)
600           m_indicesList.append(iter);
601       }
602     else
603       {
604         QMessageBox::warning(this,
605                              tr("massXpert - Modify monomers"),
606                              tr("No target is selected."),
607                              QMessageBox::Ok);;
608         return 0;
609       }
610 
611     //   qDebug() << "Indices:" << m_indicesList.size();
612 
613     return m_indicesList.size();
614   }
615 
616 
617   bool
parseModifDefinition(Modif * modif)618   MonomerModificationDlg::parseModifDefinition(Modif *modif)
619   {
620     Q_ASSERT(modif);
621 
622     QString text = m_ui.modifNameLineEdit->text();
623 
624     modif->setName(text);
625 
626     text = m_ui.modifFormulaLineEdit->text();
627 
628     modif->setFormula(text);
629 
630     // Attention, we have to compute the masses of the modif !
631 
632     if (!modif->calculateMasses())
633       return false;
634 
635     text = m_ui.modifTargetsLineEdit->text();
636 
637     modif->setTargets(text);
638 
639     if (!modif->validate())
640       return false;
641 
642     return true;
643   }
644 
645 
646   void
modify()647   MonomerModificationDlg::modify()
648   {
649     QStringList errorList;
650 
651     Polymer *polymer = mp_editorWnd->polymer();
652 
653     // There are two ways to perform a modification: either select one
654     // modification from the list of available modifications as defined
655     // in the polymer chemistry definition, or perform a quick and dirty
656     // modification definition in the dialog.
657 
658     QString text;
659 
660     Modif *modif =  new Modif(polymer->polChemDef(),
661                               "NOT_SET",
662                               "NOT_SET");
663 
664     if (m_ui.defineModifGroupBox->isChecked())
665       {
666         // The user wants to use a self-defined modification.
667 
668         if(!parseModifDefinition(modif))
669           {
670             QMessageBox::warning(this,
671                                  tr("massXpert - Modify monomers"),
672                                  tr("The defined modification failed "
673                                     "to parse."),
674                                  QMessageBox::Ok);
675             return;
676           }
677 
678         // At this point the modification is correct.
679       }
680     else
681       {
682         // Get the modification currently selected.
683         QList<QListWidgetItem *> selectedList =
684           m_ui.availableModifListWidget->selectedItems();
685 
686         if(selectedList.size() != 1)
687           {
688             QMessageBox::warning(this,
689                                  tr("massXpert - Modify monomers"),
690                                  tr("No modification is selected "
691                                     "in the list."),
692                                  QMessageBox::Ok);
693             return;
694           }
695 
696         text = selectedList.at(0)->text();
697         Q_ASSERT(!text.isEmpty());
698 
699         // With the name of the modification get to the modif proper.
700 
701         bool res = polymer->polChemDef()->modif(text, modif);
702         if(!res)
703           qFatal("Fatal error at %s@%d. Program aborted.",
704                  __FILE__, __LINE__);
705 
706         // At this point the modification is correct. Set a bool to true
707         // to know later that the modif was from the polChemDef.
708       }
709 
710     // Is the modification work with allowed overriding of the target
711     // and the max count limitations?
712     bool override = m_ui.overrideLimitationsCheckBox->isChecked();
713 
714     // Construct a list of all the indices where the modification should
715     // apply.
716     if (!prepareIndicesList())
717       return;
718 
719     for (int iter = 0; iter < m_indicesList.size(); ++iter)
720       {
721         int index = m_indicesList.at(iter);
722 
723         const Monomer *monomer = polymer->monomerList().at(index);
724 
725         // Append the position and code of the currently iterated
726         // monomer. We will remove that string if the result is not
727         // bad. If the result below is bad, then the corresponding error
728         // will have been appended and we will not remove anything.
729 
730         errorList.append(tr("Pos. %1: %2 --------------")
731                          .arg(iter + 1)
732                          .arg(monomer->code()));
733 
734         // Upon modification, the monomer takes ownership of the modif,
735         // so we have to allocate one each time using the template
736         // prepared above.
737 
738         Modif *newModif = new Modif(*modif);
739 
740         int ret = const_cast<Monomer *>(monomer)->modify(newModif,
741                                                          override,
742                                                          errorList);
743 
744         // The modification went ok, which means we can remove the last
745         // string that we prudentially added in case an error was to be
746         // output. If the modification failed(ret == false) we do not
747         // perform any modification for the monomer dealt-with.
748 
749         if(!ret)
750           {
751             errorList.append("\n");
752           }
753         else
754           {
755             errorList.removeLast();
756 
757             mp_editorWnd->setWindowModified(true);
758 
759             // We have to make sure that the vignette knows for which
760             // chemical entity it is created.
761 
762             ret = mp_editorWnd->mpa_editorGraphicsView->
763               modifyVignetteAt(index, newModif);
764 
765             Q_ASSERT(ret);
766           }
767       }
768     // End of
769     // for (int iter = 0; iter < m_indicesList.size(); ++iter)
770 
771     m_ui.messagesTextEdit->append(tr("New operation: modify with %1\n")
772                                   .arg(modif->name()) + errorList.join(""));
773 
774     // At this point we can delete the modif template.
775     delete modif;
776 
777     populateModifAndModifiedMonomerLists();
778 
779     mp_editorWnd->updateWholeSequenceMasses(true);
780     mp_editorWnd->updateSelectedSequenceMasses(true);
781 
782     //  mp_editorWnd->mpa_editorGraphicsView->updateSequence();
783   }
784 
785 
786   void
unmodify()787   MonomerModificationDlg::unmodify()
788   {
789     // We only can unmodify in the following cases:
790 
791     // If one or more modifications are selected in the
792     // m_ui.modifListWidget(which is a multiple selection list).
793 
794     QList<QListWidgetItem *> selectedList =
795       m_ui.modifListWidget->selectedItems();
796 
797     if (!selectedList.size())
798       return;
799 
800     // For each item in the selection list, get the item, get to the
801     // monomer and perform the unmodification.
802 
803     for (int iter = 0; iter < selectedList.size(); ++iter)
804       {
805         QListWidgetItem *item = selectedList.at(iter);
806 
807         // Get the text which is of the form
808 
809         // "Phosphorylation/S/12/136958312/136678312",
810 
811         // which is the name of the modification, the code of the
812         // modified monomer and its position, the pointer to
813         // the modification and finally the pointer to
814         // the modified monomer".
815 
816         QString text = item->text();
817 
818         QStringList stringList = text.split('/',
819                                             QString::SkipEmptyParts,
820                                             Qt::CaseSensitive );
821 
822         bool ok = false;
823 
824         // The modif(quintptr) pointer is the third string in the list.
825         quintptr pointerCastToInt = stringList.at(3).toInt(&ok);
826         Modif *modif = reinterpret_cast<Modif *>(pointerCastToInt);
827 
828         // The monomer(quintptr) pointer is the fourth string in the
829         // list.
830         ok = false;
831         pointerCastToInt = stringList.at(4).toInt(&ok);
832         Monomer *monomer = reinterpret_cast<Monomer *>(pointerCastToInt);
833 
834         //Because the sequence might have changed since the moment
835         //this window was opened, we should make sure the monomer to
836         //unmodify is still there !
837 
838         int index = mp_editorWnd->polymer()->monomerIndex(monomer);
839 
840         if(index == -1)
841           {
842             // Hmmm, the monomer is no more in the sequence, the
843             // sequence has been modified and the monomer has been
844             // erased.
845 
846             QMessageBox::warning(this,
847                                  tr("massXpert - Modify monomers"),
848                                  tr("%1@%2\n"
849                                     "The monomer to unmodify does not exist "
850                                     "in the sequence anymore.\n"
851                                     "Avoid modifying the sequence while "
852                                     "working with modifications.")
853                                  .arg(__FILE__)
854                                  .arg(__LINE__),
855                                  QMessageBox::Ok);
856 
857             populateModifAndModifiedMonomerLists();
858 
859             return;
860           }
861 
862 
863         // The monomer index at the time the monomer modification window
864         // was opened. Note that the index might change if the user
865         // edits the sequence after having opened *this window. Also,
866         // note that for the reader to use the value in the list item,
867         // it was set as a position, not an index, which means we have
868         // to remove one from the value.
869         //	ok = false;
870         //	int index = stringList.at(2).toInt(&ok) - 1;
871 
872 
873         // At this point we know which modification we have to remove
874         // and from which monomer.
875 
876         int ret = monomer->unmodify(modif);
877         if(!ret)
878           qFatal("Fatal error at %s@%d. Program aborted.",
879                  __FILE__, __LINE__);
880 
881         mp_editorWnd->setWindowModified(true);
882 
883         // At this point, we have to make sure we remove the
884         // modification vignette.
885 
886         // Note that because a monomer might be modified more than once,
887         // and with the same modification, we ought to remove the proper
888         // vignette.
889 
890         bool val = mp_editorWnd->mpa_editorGraphicsView->
891           unmodifyVignetteAt(index, modif);
892 
893         if(!val)
894           qFatal("Fatal error at %s@%d. Program aborted.",
895                  __FILE__, __LINE__);
896 
897         //      mp_editorWnd->mpa_editorGraphicsView->updateSequence();
898       }
899 
900     populateModifAndModifiedMonomerLists();
901 
902     mp_editorWnd->updateWholeSequenceMasses(true);
903     mp_editorWnd->updateSelectedSequenceMasses(true);
904   }
905 
906 } // namespace massXpert
907