1 /*
2  ----------------------------------------------------------------------------
3  "THE BEER-WARE LICENSE" (Revision 42):
4  <dkratzert@gmx.de> wrote this file. As long as you retain
5  this notice you can do whatever you want with this stuff. If we meet some day,
6  and you think this stuff is worth it, you can buy me a beer in return.
7  Daniel Kratzert
8  ----------------------------------------------------------------------------
9 */
10 
11 #include <QtGui>
12 #include "dsreditwindow.h"
13 #include "dsrglwindow.h"
14 #include "deprecation.h"
15 
16 /*
17    TODO:
18    - create a database handler class.
19 */
20 
DSREditWindow(Molecule * mol,DSRMol * header,QString dsr_db_path,QString nametag,QVector<QStringList> fragmentsList,DSRGui * parent)21 DSREditWindow::DSREditWindow(Molecule *mol, DSRMol *header,
22                              QString dsr_db_path, QString nametag,
23                              QVector<QStringList> fragmentsList, DSRGui *parent)
24   : QWidget(parent)
25 {
26   RESTRAINT_CARDS << "SIMU" << "RIGU" << "DELU" << "SAME" << "FREE" << "DFIX" << "BUMP"
27                   << "HFIX" << "BIND" << "SADI" << "CHIV" << "FLAT" << "DEFS" << "ISOR"
28                   << "NCSY" << "DANG" << "EADP" << "EXYZ";
29   this->setParent(parent);
30   m_gui = parent;
31   m_mol = mol;
32   m_header = header;
33   mydsrdbpath = dsr_db_path;
34   m_frags = fragmentsList;
35   userdb = false;
36   m_nametag = nametag;
37   a = 0;
38   b = 0;
39   c = 0;
40   alpha = 0;
41   beta = 0;
42   gamma = 0;
43   oldatoms = new QString;
44   oldrestraints = new QString;
45   previous_atoms = new QStringList;
46   first_atnames = new QStringList;
47   setWindowFlags(Qt::Window);
48   setWindowTitle(QString("Edit Fragment"));
49   mainVLayout = new QVBoxLayout(this);
50   maingrid = new QGridLayout;
51   mainVLayout->addLayout(maingrid);
52   makeUnitCellEdit();
53   QLabel *cellabel = new QLabel("Unit Cell");
54   QLabel *namelabel = new QLabel("Name:");
55   nameEdit = new QLineEdit;
56   QLabel *restrlabel = new QLabel("Restraints:");
57   restrEdit = new QTextEdit();
58   QLabel *atomslabel = new QLabel("Atoms:");
59   atomsEdit = new QTextEdit;
60   atomsEdit->setAcceptRichText(false);
61   QLabel *resiclabel = new QLabel("Residue Class:");
62   resicledit = new QLineEdit;
63   resicledit->setMaximumWidth(getCharWidth(6));
64   resicledit->setMinimumWidth(getCharWidth(6));
65   use_selected = new QPushButton("Use selected Atoms");
66   use_selected->setMaximumWidth(getCharWidth(30));
67   myedgl = new DSRGlWindow(this, mol, *header, nametag);
68   updateButton = new QPushButton("Update Fragment");
69   addButton = new QPushButton("Add as New");
70   deleteButton = new QPushButton("Delete Fragment");
71   mailButton = new QPushButton("Mail Fragment home");
72   clearButton = new QPushButton("Clear All");
73   renameButton = new QPushButton("Enter Rename Mode");
74   accept_renameButton = new QPushButton("OK");
75   //accept_renameButton->setStyleSheet("QPushButton{background: rgb(124, 255, 117);}");
76   abort_renameButton = new QPushButton("Abort");
77   //abort_renameButton->setStyleSheet("QPushButton{background: rgb(255, 180, 180);}");
78   buttonlayout = new QGridLayout;
79   infoLabel = new QLabel;
80   //    buttongrid          row column spanrow spancolumn:
81   buttonlayout->addWidget(addButton,     0, 0);
82   buttonlayout->addWidget(updateButton,  0, 1);
83   buttonlayout->addWidget(deleteButton,  0, 2);
84   buttonlayout->addWidget(mailButton,    1, 0);
85   buttonlayout->addWidget(clearButton,   1, 1);
86   buttonlayout->addWidget(renameButton,  1, 2);
87   buttonlayout->addWidget(infoLabel,     2, 0, 2, 3);
88   //infoLabel->setStyleSheet("border: 1px solid lightgrey");
89   addButton->setDisabled(true);
90 
91     //    maingrid          row column spanrow spancolumn:
92   maingrid->setRowStretch(1, 2);
93   maingrid->setRowStretch(3, 2);
94   maingrid->setRowStretch(4, 2);
95   maingrid->setColumnStretch(0, 0);
96   maingrid->setColumnStretch(1, 2);
97   maingrid->setColumnStretch(2, 0);
98   maingrid->setColumnStretch(3, 0);
99   maingrid->setColumnStretch(4, 2);
100   maingrid->addWidget(myedgl,      0, 4, 4, 3);
101   maingrid->addWidget(cellabel,    0, 0, 1, 1);
102   maingrid->addLayout(cellLayout,  0, 1, 1, 3);
103   maingrid->addWidget(namelabel,   1, 0, 1, 1);
104   maingrid->addWidget(nameEdit,    1, 1, 1, 1);
105   maingrid->addWidget(resiclabel,  1, 2, 1, 1);
106   maingrid->addWidget(resicledit,  1, 3, 1, 1);
107   maingrid->addWidget(use_selected,2, 1, 1, 2);
108   maingrid->addWidget(atomslabel,  3, 0, 1, 1);
109   maingrid->addWidget(atomsEdit,   3, 1, 1, 3);
110   maingrid->addWidget(restrlabel,  4, 0, 2, 1);
111   maingrid->addWidget(restrEdit,   4, 1, 2, 3);
112   maingrid->addLayout(buttonlayout,4, 4, 2, 3, Qt::AlignTop);
113 
114   addButton->setToolTip("Add this fragment to the database.\n"
115                         "Fragments from the database supplied by DSR will\n"
116                         "always be stored as a new fragment in the user database.");
117   deleteButton->setToolTip("Deletes the currently opened fragment. \n"
118                            "It can only delete fragments from the user database in dsr_usr_db.txt");
119   nameEdit->setToolTip("Insert any chemical name or sum formula here.");
120   resicledit->setToolTip("A residue class has to start with a letter \n"
121                          "and can be up to four characters including numbers.");
122   use_selected->setToolTip("Uses the selected atoms in ShlelXle as new atoms\n"
123                            "for the atom list below.");
124   atomsEdit->setToolTip("Insert atoms with following syntax:\n"
125                         "Name   Atomic Number    x    y    z\n"
126                         "C6     6     0.00460    2.39814    1.55732");
127   updateButton->setToolTip("Updates the fragment with the current changes.");
128   deleteButton->setToolTip("Erases the current fragment from the database.\n"
129                            "(Only user-made fragments can be deleted)");
130   mailButton->setToolTip("Sends the current fragment to the author of DSR.\n"
131                          "By sending the fragment to Daniel Kratzert, you\n"
132                          "allow him to distribute it with the DSR program.");
133   renameButton->setToolTip("Enter rename mode to rename atoms.\n"
134                            "Restraints will be updated according to the new atom names.\n"
135                            "You can not add/delete atoms in rename mode.\n"
136                            "Renaming is only available if no previous changes were made.");
137   clearButton->setToolTip("Clear all settings to define a new fragment.");
138   accept_renameButton->setToolTip("Accept the changes and return to fragment editor.");
139   abort_renameButton->setToolTip("Revert all changes and return to fragment editor.");
140   restrEdit->setToolTip("Insert any valid SHELXL restraint.");
141   use_selected->setStyleSheet("QPushButton {"
142                                "font-weight: bold; "
143                               "}");
144   infoLabel->setText("");
145   QFont labelfont("Arial bold", 12);
146   infoLabel->setFont(labelfont);
147   infoLabel->setWordWrap(true); // otherwise the widget gets elongated by long text.
148   myedgl->updateGL();
149   // Only do this if we selected a fragment in the list!
150   // Before, we have no information from the db header.
151   if (!nametag.isEmpty() && !header->cell.isEmpty()) {
152     setCellFromDB(header);
153   }
154   if (!nametag.isEmpty() && !header->comment.isEmpty()) {
155     setNamefromDB(header);
156   }
157   if (!nametag.isEmpty() && !header->residue.isEmpty()) {
158     setResidueFromDB(header);
159   }
160   if (!nametag.isEmpty() && !header->atoms.isEmpty()) {
161     setAtomsFromDB(header);
162   }
163   if (!nametag.isEmpty() && !header->restr.isEmpty()) {
164     setRestraintsFromDB(header);
165   }
166   if (!nametag.isEmpty() && !header->dbtype.isEmpty()) {
167     setDBTypeFromDB(header);
168   }
169   if (!userdb) {
170     deleteButton->setEnabled(false);
171   }
172   if (m_nametag.isEmpty()) {
173     updateButton->setDisabled(true);
174   }
175   connectSignalsAndSlots();
176   connect(deleteButton, SIGNAL(clicked()),
177           parent->mygl, SLOT(clear_molecule()));
178 }
179 
~DSREditWindow()180 DSREditWindow::~DSREditWindow(){
181 }
182 
connectSignalsAndSlots()183 void DSREditWindow::connectSignalsAndSlots()
184 //! handles all signals and slots
185 {
186   // check and set residue class from edit field:
187   connect(resicledit, SIGNAL(textChanged(QString)),
188           this, SLOT(checkResiClassInput(QString)));
189   // set fragname variable from edit field:
190   connect(nameEdit, SIGNAL(textChanged(QString)),
191           this, SLOT(setFragmentName(QString)));
192   // set cell from edit fields:
193   connect(cell_a, SIGNAL(textChanged(QString)),
194           this, SLOT(set_a_value(QString)));
195   connect(cell_b, SIGNAL(textChanged(QString)),
196           this, SLOT(set_b_value(QString)));
197   connect(cell_c, SIGNAL(textChanged(QString)),
198           this, SLOT(set_c_value(QString)));
199   connect(cell_alpha, SIGNAL(textChanged(QString)),
200           this, SLOT(set_alpha_value(QString)));
201   connect(cell_beta, SIGNAL(textChanged(QString)),
202           this, SLOT(set_beta_value(QString)));
203   connect(cell_gamma, SIGNAL(textChanged(QString)),
204           this, SLOT(set_gamma_value(QString)));
205   // set MyAtoms from atomsEdit field:
206   connect(atomsEdit, SIGNAL(textChanged()),
207           this, SLOT(setAtoms()));
208   connect(restrEdit, SIGNAL(textChanged()),
209           this, SLOT(setRestraints()));
210   // display an error message if there is any error in the
211   // format of atoms or restraints:
212   connect(this, SIGNAL(line_error(QString)),
213           this, SLOT(setInfoLabel(QString)));
214   connect(addButton, SIGNAL(clicked()),
215           this, SLOT(addFragment()));
216   connect(deleteButton, SIGNAL(clicked()),
217           this, SLOT(deleteCurrentFragment()));
218   connect(deleteButton, SIGNAL(clicked()),
219           this, SLOT(updateFraglist()));
220   connect(deleteButton, SIGNAL(clicked()),  // the information in this window gets useless,
221           this, SLOT(close()));             // thus it is closed.
222   connect(updateButton, SIGNAL(clicked()),
223           this, SLOT(updateFragment()));
224   connect(use_selected, SIGNAL(clicked()),
225           this, SLOT(setSelectedAtoms()));
226   connect(mailButton, SIGNAL(clicked()),
227           this, SLOT(mailHome()));
228   connect(renameButton, SIGNAL(clicked()),
229           this, SLOT(enterRenameMode()));
230   connect(accept_renameButton, SIGNAL(clicked()),
231           this, SLOT(leaveRenameMode()));
232   connect(clearButton, SIGNAL(clicked()),
233           this, SLOT(clearAll()));
234 }
235 
236 
minimumSizeHint() const237 QSize DSREditWindow::minimumSizeHint() const
238 {
239   return QSize(700, 700);
240 }
241 
sizeHint() const242 QSize DSREditWindow::sizeHint() const
243 {
244   return QSize(900, 700);
245 }
246 
247 
clearAll()248 void DSREditWindow::clearAll(){
249   cell_a->clear();
250   cell_b->clear();
251   cell_c->clear();
252   cell_alpha->clear();
253   cell_beta->clear();
254   cell_gamma->clear();
255   nameEdit->clear();
256   resicledit->clear();
257   atomsEdit->clear();
258   restrEdit->clear();
259   restrEdit->append("Add at least one restraint here. (And delete this text)");
260   addButton->setDisabled(true);  // Otherwise, it is possible to create a damaged fragment.
261   updateButton->setDisabled(true);
262   myedgl->clear_molecule();
263 }
264 
enterRenameMode()265 void DSREditWindow::enterRenameMode(){
266   //! prepare window to rename atoms
267   first_atnames->clear();
268   foreach (QString line, atomsEdit->toPlainText().split(QRegExp("\n|\r\n|\r"))) {
269     first_atnames->append(line.split(' ').at(0));
270   }
271   addButton->hide();
272   updateButton->hide();
273   mailButton->hide();
274   restrEdit->setDisabled(true);
275   nameEdit->setDisabled(true);
276   resicledit->setDisabled(true);
277   cell_a->setDisabled(true);
278   cell_b->setDisabled(true);
279   cell_c->setDisabled(true);
280   cell_alpha->setDisabled(true);
281   cell_beta->setDisabled(true);
282   cell_gamma->setDisabled(true);
283   renameButton->hide();
284   deleteButton->hide();
285   use_selected->setDisabled(true);
286   buttonlayout->addWidget(accept_renameButton,  0, 0, 1, 2);
287   buttonlayout->addWidget(abort_renameButton,  1, 0, 1, 2);
288   accept_renameButton->show();
289   abort_renameButton->show();
290   atomsEdit->disconnect();
291   oldatoms->clear();
292   oldatoms->append(atomsEdit->toPlainText());
293   oldrestraints->clear();
294   oldrestraints->append(restrEdit->toPlainText());
295   connect(atomsEdit, SIGNAL(textChanged()),
296           this, SLOT(renameAtoms()));
297   connect(abort_renameButton, SIGNAL(clicked()),
298           this, SLOT(abortRenameMode()));
299   restrEdit->disconnect();
300   new_header = new DSRMol;
301   new_header->atoms.clear();
302 }
303 
leaveRenameMode()304 void DSREditWindow::leaveRenameMode(){
305   //! go out of rename mode and rename the restraints
306   //rename_restraints()
307   renameButton->show();
308   deleteButton->show();
309   restrEdit->setEnabled(true);
310   nameEdit->setEnabled(true);
311   resicledit->setEnabled(true);
312   cell_a->setEnabled(true);
313   cell_b->setEnabled(true);
314   cell_c->setEnabled(true);
315   cell_alpha->setEnabled(true);
316   cell_beta->setEnabled(true);
317   cell_gamma->setEnabled(true);
318   use_selected->setEnabled(true);
319   mailButton->show();
320   updateButton->show();
321   addButton->show();
322   accept_renameButton->hide();
323   abort_renameButton->hide();
324   atomsEdit->disconnect();
325   restrEdit->disconnect();
326   // re-attach signal after rename mode
327   connect(atomsEdit, SIGNAL(textChanged()),
328           this, SLOT(setAtoms()));
329   connect(restrEdit, SIGNAL(textChanged()),
330           this, SLOT(setRestraints()));
331   setAtoms();
332   setRestraints();
333 }
334 
abortRenameMode()335 void DSREditWindow::abortRenameMode(){
336   //! go out of rename mode and do nothing
337   //rename_restraints()
338   renameButton->show();
339   deleteButton->show();
340   restrEdit->setEnabled(true);
341   nameEdit->setEnabled(true);
342   resicledit->setEnabled(true);
343   cell_a->setEnabled(true);
344   cell_b->setEnabled(true);
345   cell_c->setEnabled(true);
346   cell_alpha->setEnabled(true);
347   cell_beta->setEnabled(true);
348   cell_gamma->setEnabled(true);
349   use_selected->setEnabled(true);
350   mailButton->show();
351   updateButton->show();
352   addButton->show();
353   accept_renameButton->hide();
354   abort_renameButton->hide();
355   atomsEdit->disconnect();
356   restrEdit->disconnect();
357   // re-attach signal after rename mode
358   connect(atomsEdit, SIGNAL(textChanged()),
359           this, SLOT(setAtoms()));
360   connect(restrEdit, SIGNAL(textChanged()),
361           this, SLOT(setRestraints()));
362   atomsEdit->setText(oldatoms->toLatin1());
363   restrEdit->setText(oldrestraints->toLatin1());
364   myedgl->display_fragment(*m_header);
365 }
366 
updateFraglist()367 void DSREditWindow::updateFraglist(){
368   emit updated(m_nametag);
369 }
370 
mailHome()371 void DSREditWindow::mailHome() {
372   //! mail the current fragment to dkratzert@gmx.de
373   //! Formating according to RFC 6068
374   //! Mac and Windows like to have different mailstrings
375   QString mailstring;
376 #if defined Q_WS_WIN32 || defined Q_OS_WIN
377       mailstring = "mailto:dkratzert@gmx.de?"
378                    "subject=New%20fragment%20for%20DSR"
379                    "&body=Dear%20Daniel,%0D%0A"
380                    "please%20add%20this%20fragment%20to%20the%20DSR%20database:%0D%0A%0D%0A"
381                    +QString("%1").arg(combineDataToNewFragment().join("%0D%0A"))
382                    +"%0D%0A";
383 #else
384       mailstring = "mailto:dkratzert@gmx.de?"
385                    "subject=New fragment for DSR"
386                    "&body=Dear Daniel,\n"
387                    "please add this fragment to the DSR database:\n\n"
388                    +QString("%1").arg(combineDataToNewFragment().join("\n"))
389                    +"\n";
390 #endif
391   QDesktopServices::openUrl(QUrl(mailstring, QUrl::TolerantMode));
392 }
393 
addFragment()394 void DSREditWindow::addFragment() {
395   //! combines new fragment and write db to the harddisk
396   QStringList origdb;
397   QStringList newdb;
398   if (!validateCell()) {
399       emit line_error(QString(tr("<font color=red><b>No valid unit cell!</font>")));
400       return;
401   }
402   if (nameEdit->text().isEmpty()) {
403       emit line_error(QString(tr("<font color=red><b>Please give the fragment a name.</font>")));
404       return;
405   }
406   origdb = readFragmentDB();
407   newdb = combineDataToNewFragment();
408   origdb.append(newdb);
409   userdb = true;
410   writeFragmentDB(origdb);
411   this->updateFraglist();
412   this->close();
413 }
414 
updateFragment()415 void DSREditWindow::updateFragment() {
416   //! this method deletes the old and writes the new fragment
417   QStringList origdb;
418   QStringList newFragment;
419   QString delfrag; // I will delete this later
420   delfrag = m_nametag;
421   if (!validateCell()) {
422       emit line_error(QString(tr("<font color=red><b>No valid unit cell!</font>")));
423       return;
424   }
425   if (nameEdit->text().isEmpty()) {
426       emit line_error(QString(tr("<font color=red><b>Please give the fragment a name.</font>")));
427       return;
428   }
429   origdb = readFragmentDB();
430   newFragment = combineDataToNewFragment();
431   origdb.append(newFragment);
432   userdb = true;
433   writeFragmentDB(origdb);
434   deleteFragment(delfrag);
435   this->updateFraglist();
436   this->close();
437 }
438 
combineDataToNewFragment()439 QStringList DSREditWindow::combineDataToNewFragment(){
440   //! combines all collected data to a fragment string for the database
441   QStringList fragStringList;
442   m_nametag = inventNameTag(5);
443   if (resiclass.isEmpty()) {
444     resiclass = inventNameTag(4);
445   }
446   fragStringList.append("\n<"+m_nametag+">");
447   fragStringList.append("rem Name: "+fragname+"");
448   fragStringList.append("rem Src: ShelXle DSR GUI");
449   fragStringList.append("RESI "+resiclass);
450   foreach (QStringList line, myRestraints) {
451     fragStringList.append(line.join(" "));
452   }
453   fragStringList.append("FRAG 17 "+QString("%1 %2 %3 %4 %5 %6")
454                     .arg(a).arg(b).arg(c)
455                     .arg(alpha).arg(beta).arg(gamma));
456   QString atline;
457   foreach (QStringList line, myAtoms) {
458     if (line.length() < 5 ) continue;
459     atline.clear();
460     atline = QString("%1 %2 %3 %4 %5")
461               .arg(line.at(0), -5, ' ')
462               .arg(line.at(1), -3, ' ')
463               .arg(line.at(2).toDouble(), 10, 'f', 5)
464               .arg(line.at(3).toDouble(), 10, 'f', 5)
465               .arg(line.at(4).toDouble(), 10, 'f', 5);
466     fragStringList.append(atline);
467   }
468   fragStringList.append("</"+m_nametag+">");
469   return fragStringList;
470 }
471 
validateCell()472 bool DSREditWindow::validateCell() {
473   //! validates if the cell parameter make sense
474   if (a > 0 && b > 0 && c > 0) {
475     if ((alpha > 0 && beta > 0 && gamma > 0) && (alpha+beta+gamma <= 360)) {
476       return true;
477     }
478   }
479   return false;
480 }
481 
setDBTypeFromDB(DSRMol * header)482 void DSREditWindow::setDBTypeFromDB(DSRMol *header) {
483   //! set userdb false or true if "dsr_db" or "dsr_user_db"
484   if (header->dbtype == QString("dsr_user_db")) {
485     userdb = true;
486   }
487 }
488 
setInfoLabel(QString myerror)489 void DSREditWindow::setInfoLabel(QString myerror) {
490   //! display the error of the error signa in infoLabel
491   infoLabel->setText(myerror);
492 }
493 
setNamefromDB(DSRMol * header)494 void DSREditWindow::setNamefromDB(DSRMol *header) {
495   //! Set the name of the linedit field nameEdit to the fragment name from the database
496   //! This method runs if you select a fragment and then click on the edit button.
497   QString name;
498   name = header->comment;
499   nameEdit->setText(name);
500   fragname = name;
501 }
502 
setResidueFromDB(DSRMol * header)503 void DSREditWindow::setResidueFromDB(DSRMol *header) {
504   //! get residue from header and populate resiclass variable and edit field
505   QString residueClass;
506   residueClass = header->residue;
507   resicledit->setText(residueClass);
508   resiclass = residueClass;
509 }
510 
setAtomsFromDB(DSRMol * header)511 void DSREditWindow::setAtomsFromDB(DSRMol *header) {
512   //! Fills the atoms edit field with atom data.
513   QFont font("Courier");
514   atomsEdit->setLineWrapMode(QTextEdit::NoWrap);
515   font.setStyleHint(QFont::TypeWriter);
516   atomsEdit->setMinimumHeight(200);
517   atomsEdit->setFont(font);
518   QTextCursor cursor = atomsEdit->textCursor();
519   QString atomEditorLine;
520   foreach (QStringList atom, header->atoms) {
521     myAtoms.append(atom);
522     atomEditorLine.clear();
523     int num = 0;
524     // This relies on the .format() by DSR!:
525     foreach (QString column, atom) { // format each column:
526       switch (num) {
527         case 0:  // name
528           column = column.leftJustified(6, ' ');
529           break;
530         case 1:  // sfac
531           column = column.leftJustified(4, ' ');
532           break;
533         case 2:  // x
534           column = column.rightJustified(9, ' ')+"  ";
535           break;
536         case 3:  // y
537           column = column.rightJustified(9, ' ')+"  ";
538           break;
539         case 4:  // z
540           column = column.rightJustified(9, ' ');
541           break;
542         default:  // anything else, just in case...
543           column = column.leftJustified(9, ' ');
544           break;
545       }
546       atomEditorLine = atomEditorLine+column;
547       num++;
548     }
549     atomsEdit->append(atomEditorLine);
550   }
551   cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
552   atomsEdit->setTextCursor(cursor);
553 }
554 
setRestraintsFromDB(DSRMol * header)555 void DSREditWindow::setRestraintsFromDB(DSRMol *header) {
556   //! Fils the restraints edit field with restraints from the db
557   QFont font("Courier");
558   restrEdit->setLineWrapMode(QTextEdit::NoWrap);
559   font.setStyleHint(QFont::TypeWriter);
560   restrEdit->setMinimumHeight(200);
561   restrEdit->setFont(font);
562   QTextCursor cursor = restrEdit->textCursor();
563   QStringList restr;
564   restr = header->restr;
565   myRestraints.clear();
566   for (int i=0; i<restr.size(); i++) {
567     restrEdit->append(restr.at(i));
568     myRestraints.append(restr.at(i).split(" ", skipEmptyParts));
569   }
570   cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
571   restrEdit->setTextCursor(cursor);
572 }
573 
setCellFromDB(DSRMol * header)574 void DSREditWindow::setCellFromDB(DSRMol *header) {
575   // 2
576   //! Sets the cell information from the dsr db.
577   //! Usually a 1 1 1 90 90 90 cell should come from DSR
578   //! because it exports cartesian coordinates for this GUI
579   a = header->cell.at(0);
580   b = header->cell.at(1);
581   c = header->cell.at(2);
582   alpha = header->cell.at(3);
583   beta = header->cell.at(4);
584   gamma = header->cell.at(5);
585   cell_a->setText(QString("%1").arg(a));
586   cell_b->setText(QString("%1").arg(b));
587   cell_c->setText(QString("%1").arg(c));
588   cell_alpha->setText(QString("%1").arg(alpha));
589   cell_beta->setText(QString("%1").arg(beta));
590   cell_gamma->setText(QString("%1").arg(gamma));
591   cell_a->setMaximumWidth(getCharWidth(10));
592   cell_a->setMinimumWidth(getCharWidth(6));
593   cell_b->setMaximumWidth(getCharWidth(10));
594   cell_b->setMinimumWidth(getCharWidth(6));
595   cell_c->setMaximumWidth(getCharWidth(10));
596   cell_c->setMinimumWidth(getCharWidth(6));
597   cell_alpha->setMaximumWidth(getCharWidth(10));
598   cell_alpha->setMinimumWidth(getCharWidth(6));
599   cell_beta->setMaximumWidth(getCharWidth(10));
600   cell_beta->setMinimumWidth(getCharWidth(6));
601   cell_gamma->setMaximumWidth(getCharWidth(10));
602   cell_gamma->setMinimumWidth(getCharWidth(6));
603 }
604 
checkResiClassInput(QString text)605 void DSREditWindow::checkResiClassInput(QString text) {
606   //! check wether the input text to residue class field is valid.
607   if (!text[0].isLetter()) {
608     resicledit->clear(); // first character has to be a letter!
609     resiclass.clear();
610     return;
611   }
612   resiclass.clear();
613   resiclass = text.toUpper();
614   resicledit->setText(text.left(4).toUpper()); // resi class allows up to 4 characters!
615 }
616 
setFragmentName(QString name)617 void DSREditWindow::setFragmentName(QString name) {
618   //! runs if fragment name is changed
619   fragname = name;
620 }
621 
622 
renameRestraints(QStringList new_atnames,int cursopos)623 void DSREditWindow::renameRestraints(QStringList new_atnames, int cursopos){
624   //! renames the restraints in rename mode
625   QTextCursor cursor = atomsEdit->textCursor();
626   cursor.setPosition(cursopos, QTextCursor::MoveAnchor);
627   QStringList origrestr;
628   origrestr = restrEdit->toPlainText().split(QRegExp("\n|\r\n|\r"));
629   atomsEdit->setTextCursor(cursor);
630   // the previous atoms are the names before I typed a new one and renaming starts
631   // I want to rename previous names with new names
632   if (new_atnames.length() == previous_atoms->length()) {
633     restrEdit->clear();
634     foreach (QString rline, origrestr) {
635       QStringList rline_list;
636       rline_list = rline.split(' ');
637       // go through all new atoms and replace previous names with new names in restraints list:
638       int num = 0;
639       foreach (QString newatom, new_atnames) {
640         for(int i = 0; i < rline_list.length(); i++) {
641           if (rline_list.at(i).toUpper() == previous_atoms->at(num).toUpper()) {
642             rline_list[i] = newatom.toUpper().toLatin1();
643           }
644         }
645         num++;
646       }
647       restrEdit->append(rline_list.join(" "));
648     }
649   }
650 }
651 
renameAtoms()652 void DSREditWindow::renameAtoms(){
653   //! Takes the text from atomEdit and fills myAtoms Qvector<QStringlist>
654   //! The special thing in this method is that only the Names can be changed
655   //! The text after the fourth character will constantly be overwritten by
656   //! the previous coordinates.
657   //! It should be impossble to delete an atom.
658   int cursopos = atomsEdit->textCursor().position();
659   myAtoms.clear();
660   atomsEdit->disconnect();
661   badLine = false;
662   QString error;
663   QStringList line_split;
664   QStringList new_atnames; // <- the new atom names
665   int num = 0;
666   error = QString(tr(""));
667   emit line_error(error);
668   int length = atomsEdit->toPlainText().split(QRegExp("\n|\r\n|\r")).length();
669   if (previous_atoms->length() == 0) {
670     previous_atoms->append(*first_atnames);
671   }
672   foreach (QString line, atomsEdit->toPlainText().split(QRegExp("\n|\r\n|\r"))) {
673     if (length != previous_atoms->length()) { // make sure not to delete an atom!
674       atomsEdit->undo();                      // In this case, undo last action
675       connect(atomsEdit, SIGNAL(textChanged()),
676               this, SLOT(renameAtoms()));
677       return;
678     }
679     line_split = line.split(" ", skipEmptyParts);
680     if (line_split.length() > 0){
681       line_split[0] = line_split.at(0).left(4);
682       new_atnames.append(line_split.at(0).toUpper()); // collect new atom names after after a character was changed
683     } else {  // This is to make sure there is no empty line. An empty line will cause the rename mode to abort.
684         this->atomsEdit->undo();
685         this->abortRenameMode();
686         error = QString(tr("<font color=red><b>Do not delete atoms in rename mode!</font>"));
687         emit line_error(error);
688         return;
689     }
690     num++;
691     if (new_atnames.count(line_split.at(0)) >= 2) {
692       // warn user about duplicates and do not rename atom:
693       error = QString(tr("<font color=red><b>Attempt to create duplicate atom \"%1\" detected. "
694                          "<br>Please make them unique.</font>").arg(line_split.at(0)));
695       emit line_error(error);
696       atomsEdit->undo();           // In this case, undo last action. Makes duplicates impossible
697       connect(atomsEdit, SIGNAL(textChanged()),
698               this, SLOT(renameAtoms()));
699       return;
700     }
701     if (!line_split.at(0).contains(QRegExp("^([A-Za-z]{1,3})\\d{0,3}[A-Za-z\\'\\\"\\#]{0,2}\\d{0,1}"))) {
702       error = QString(tr("<font color=red><b>Invalid atom name found.</font>"));
703       emit line_error(error);
704       connect(atomsEdit, SIGNAL(textChanged()),
705               this, SLOT(renameAtoms()));
706       return;
707     }
708   }
709   atomsEdit->clear();
710   int onum = 0;
711   foreach (QString line, oldatoms->split(QRegExp("\n|\r\n|\r"))) {
712     // use the new names + the previous atoms
713     line = new_atnames.at(onum).leftJustified(4, ' ').left(4)+line.remove(0, 4);
714     atomsEdit->append(line);
715     myAtoms.append(line.split(' ', skipEmptyParts));
716     onum++;
717   }
718   renameRestraints(new_atnames, cursopos);
719   previous_atoms->clear();
720   previous_atoms->append(new_atnames);
721   new_header->atoms.clear();
722   foreach(QStringList line, myAtoms) {
723     new_header->atoms.append(line);
724   }
725   connect(atomsEdit, SIGNAL(textChanged()),
726           this, SLOT(renameAtoms()));
727   myedgl->display_fragment(*new_header);
728 }
729 
730 
setAtoms()731 void DSREditWindow::setAtoms(){
732   //! takes the text from atomEdit and fills myAtoms Qvector<QStringlist>
733   renameButton->setDisabled(true);
734   myAtoms.clear();
735   badLine = false;
736   QString error;
737   QStringList line_split;
738   QStringList atnames;
739   int num = 0;
740   foreach (QString line, atomsEdit->toPlainText().split(QRegExp("\n|\r\n|\r"))) {
741     line_split = line.split(" ", skipEmptyParts);
742     line_split = checkAtomLine(line_split, num+1);
743     myAtoms.append(line_split);
744     if (line_split.length() > 0){
745       atnames.append(line_split.at(0).toUpper());
746       if (atnames.count(line_split.at(0).toUpper()) >= 2) {
747         error = QString(tr("<font color=red><b>Duplicate atom \"%1\" found! Please make them unique.</font>").arg(line_split.at(0)));
748         // Can not do undo(), because it would prevent adding duplicate names with "add selected atoms"
749         // and this has to be possible because of residues:
750         //atomsEdit->undo();
751         emit line_error(error);
752         badLine = true;
753         //return;
754       }
755     }
756     num++;
757   }
758   if (myAtoms.size() <= 1 && myAtoms[0].isEmpty()) {
759     addButton->setDisabled(true);
760     updateButton->setDisabled(true);
761     error = QString(tr("<font color=red><b>Please add at least one atom to add/update the fragment.</font>"));
762     emit line_error(error);
763     return;
764   } else {
765     addButton->setEnabled(true);
766     updateButton->setEnabled(true);
767   }
768   if (!badLine) {
769     emit line_error("");
770     addButton->setEnabled(true);
771     updateButton->setEnabled(true);
772   } else {
773     updateButton->setDisabled(true);
774     addButton->setDisabled(true);
775     return;
776   }
777 }
778 
setSelectedAtoms()779 void DSREditWindow::setSelectedAtoms() {
780   //! uses selected atoms for atom field and sets cell accordingly
781   atomsEdit->clear();
782   int numselect = m_mol->selectedatoms.size();
783   QString line;
784   QString atname;
785   QString firstatom;
786   QString lastatom;
787   for (int i = 0; i < numselect; i++) {
788     line.clear();
789     atname.clear();
790     // The two split() make sure no illegal suffices remain on the labels:
791     atname = m_mol->selectedatoms.at(i).Label.split('_').at(0).split(QChar(187)).at(0);
792     if (i == 0) {
793       firstatom = atname;
794     }
795     if (i+1 == numselect) {
796       lastatom = atname;
797     }
798     line = QString("%1 %2 %3 %4 %5")
799         .arg(atname, -5)  // Atom name
800         .arg(m_mol->selectedatoms.at(i).an+1, -2) // strangely ShelXle stores AN-1 here
801         .arg(m_mol->selectedatoms.at(i).pos.x, 10, 'f', 5)  // X
802         .arg(m_mol->selectedatoms.at(i).pos.y, 10, 'f', 5)  // Y
803         .arg(m_mol->selectedatoms.at(i).pos.z, 10, 'f', 5); // Z
804     atomsEdit->append(line);
805   }
806   // make sure at least some restraints end up in the database:
807   if ((restrEdit->toPlainText().simplified().size() < 4)
808       && (firstatom.size() > 0) && (lastatom.size() > 0)) {
809     restrEdit->clear();
810     restrEdit->append(QString("RIGU %1 > %2").arg(firstatom, lastatom));
811     restrEdit->append(QString("SIMU %1 > %2").arg(firstatom, lastatom));
812   }
813   cell_a->setText("1");
814   cell_b->setText("1");
815   cell_c->setText("1");
816   cell_alpha->setText("90");
817   cell_beta->setText("90");
818   cell_gamma->setText("90");
819   updateButton->show();
820   addButton->show();
821 }
822 
checkAtomLine(QStringList line,int num)823 QStringList DSREditWindow::checkAtomLine(QStringList line, int num) {
824   //! check an atom text line for consistency
825   //! like if there are all xyz parameters or the name makes sense
826   QString error;
827   error.clear();
828   bool isAtFloat = false;
829   if (!line.isEmpty()) {
830     line.at(0).toFloat(&isAtFloat);
831   }
832   bool is_scatt_int = true;
833   if (line.length() >= 2) {
834     line.at(1).toFloat(&is_scatt_int);
835   }
836   if (line.size() > 0 && line.size() < 5) {
837     error = QString(tr("<font color=red><b>Error in atom line %1: Too few parameters.</b></font>")).arg(num);
838     emit line_error(error);
839     badLine = true;
840   } else if (line.size() > 5) {
841     error = QString(tr("<font color=red><b>Error in atom line %1: Too many parameters.</font>")).arg(num);
842     emit line_error(error);
843     badLine = true;
844   } else if (line.join("").contains(",")) {
845     error = QString(tr("<font color=red><b>Error in atom line %1: Numbers should not contain comma.</font>")).arg(num);
846     emit line_error(error);
847     badLine = true;
848   } else if (!line.isEmpty() && line[0][0].isDigit()) {
849     error = QString(tr("<font color=red><b>Error in atom line %1: Atoms can not begin with a number.</font>")).arg(num);
850     emit line_error(error);
851     badLine = true;
852   } else if (!line.isEmpty() && line[0].length() > 4) {
853     error = QString(tr("<font color=red><b>Error in atom line %1: Atom name must not have more than four characters.</font>")).arg(num);
854     emit line_error(error);
855     badLine = true;
856   } else if (!line.isEmpty() && line[0].contains("_")) {
857     error = QString(tr("<font color=red><b>Error in atom line %1: Atom name must not have underscores.</font>")).arg(num);
858     emit line_error(error);
859     badLine = true;
860   } else if (isAtFloat) {
861     error = QString(tr("<font color=red><b>Error in atom line %1: Atoms can not be only numbers.</font>")).arg(num);
862     emit line_error(error);
863     badLine = true;
864   } else if (!myisAscii(line.join(""))) {
865     error = QString(tr("<font color=red><b>Error in atom line %1: Non-ascii characters are not allowed.</font>")).arg(num);
866     emit line_error(error);
867     badLine = true;
868   } else if (!is_scatt_int) {
869     error = QString(tr("<font color=red><b>Error in atom line %1: Scattering factor can be only integer numbers.</font>")).arg(num);
870     emit line_error(error);
871     badLine = true;
872   } else if (!validateCell()) {
873     error = QString(tr("<font color=red><b>Please define proper unit cell parameters!</font>"));
874     emit line_error(error);
875     badLine = true;
876   }else {
877     int num2 = 0;
878     foreach (QString item, line) {
879       bool ok = false;
880       item.toFloat(&ok);
881       if (num2 >= 2 && !ok){
882         error = QString(tr("<font color=red><b>Error in atom line %1: columns 3 to 5 contain non-numerical character(s).</font>")).arg(num);
883         emit line_error(error);
884         badLine = true;
885       }
886       num2++;
887     }
888   }
889   if (badLine == true) {
890       addButton->setDisabled(true);
891       updateButton->setDisabled(true);
892   } else {
893       addButton->setEnabled(true);
894       updateButton->setEnabled(true);
895   }
896   return line;
897 }
898 
myisAscii(QString line)899 bool DSREditWindow::myisAscii(QString line) {
900   //! test if line contains non-ascii characters
901   for (int num=0; num<line.length(); num++) {
902     if (line.at(num).unicode() > 127) return false;
903   }
904   return true;
905 }
906 
setRestraints()907 void DSREditWindow::setRestraints() {
908   //! takes text from restrEdit and fills myRestraints Qvector<QStringlist>
909   renameButton->setDisabled(true);
910   myRestraints.clear();
911   badLine = false;
912   QString error;
913   QStringList line_split;
914   int num = 0;
915   foreach (QString line, restrEdit->toPlainText().split(QRegExp("\n|\r\n|\r"))) {
916     line_split = line.split(" ", skipEmptyParts);
917     checkRestraintLine(line_split, num+1);
918     myRestraints.append(line_split);
919     num++;
920   }
921   if (!badLine) {
922     emit line_error("");
923   } else {
924     return;
925   }
926   if (myRestraints[0].isEmpty()) {
927     addButton->setDisabled(true);
928     updateButton->setDisabled(true);
929     error = QString(tr("<font color=red><b>Please add at least one restraint to add/update the fragment.</font>"));
930     emit line_error(error);
931   } else {
932     addButton->setEnabled(true);
933     updateButton->setEnabled(true);
934   }
935 }
936 
checkRestraintLine(QStringList restrline,int num)937 void DSREditWindow::checkRestraintLine(QStringList restrline, int num) {
938   //! Check each line of the restraints for errors. Especially atoms that
939   //! are not in the atoms list.
940   bool ok;
941   QString error;
942   QStringList atomList;
943   foreach (QStringList atomline, myAtoms) {
944     if (atomline.isEmpty()) continue;
945     atomList.append(atomline.at(0).toUpper());
946   }
947   foreach (QString item, restrline.mid(0, 1)) {
948     if (!RESTRAINT_CARDS.contains(item.toUpper())){
949       error = QString(tr("<font color=red><b>Invalid restraint %1 in line %2.</font>")).arg(item).arg(num);
950       updateButton->setDisabled(true);
951       addButton->setDisabled(true);
952       emit line_error(error);
953       badLine = true;
954     }
955     if (badLine == true) {
956       return;
957     }
958   }
959   foreach (QString item, restrline.mid(1, -1)) {
960     item.toFloat(&ok);
961     if (ok) {continue;}
962     if (item == ">") {continue;}
963     if (item == "<") {continue;}
964     if (item == "_") {continue;}
965     if (item == "$") {continue;}
966     if (!atomList.contains(item.toUpper())) {
967       error = QString(tr("<font color=red><b>Atom %1 in restraints line %2 not in atom list!</font>")).arg(item).arg(num);
968       updateButton->setDisabled(true);
969       addButton->setDisabled(true);
970       emit line_error(error);
971       badLine = true;
972     }
973     if (badLine == true) {
974       return;
975     }
976     error = "";
977     emit line_error(error);
978   }
979   num++;
980 }
981 
makeUnitCellEdit()982 void DSREditWindow::makeUnitCellEdit(){
983   //! The unit cell input field
984   cellLayout = new QHBoxLayout;
985   cell_a = new QLineEdit;
986   cell_b = new QLineEdit;
987   cell_c = new QLineEdit;
988   cell_alpha = new QLineEdit;
989   cell_beta = new QLineEdit;
990   cell_gamma = new QLineEdit;
991   QLabel *alabel = new QLabel("a:");
992   QLabel *blabel = new QLabel("b:");
993   QLabel *clabel = new QLabel("c:");
994   QLabel *alphalabel = new QLabel(QChar(0xb1, 0x03)+QString(":"));
995   QLabel *betalabel = new QLabel(QChar(0xb2, 0x03)+QString(":"));
996   QLabel *gammalabel = new QLabel(QChar(0xb3, 0x03)+QString(":"));
997   cellLayout->addWidget(alabel);
998   cellLayout->addWidget(cell_a);
999   cellLayout->addWidget(blabel);
1000   cellLayout->addWidget(cell_b);
1001   cellLayout->addWidget(clabel);
1002   cellLayout->addWidget(cell_c);
1003   cellLayout->addWidget(alphalabel);
1004   cellLayout->addWidget(cell_alpha);
1005   cellLayout->addWidget(betalabel);
1006   cellLayout->addWidget(cell_beta);
1007   cellLayout->addWidget(gammalabel);
1008   cellLayout->addWidget(cell_gamma);
1009   // TODO: better validator needed:
1010   QDoubleValidator *cellaValidator = new QDoubleValidator(0, 999, 6, cell_a);
1011   cellaValidator->setNotation(QDoubleValidator::StandardNotation);
1012   QDoubleValidator *cellbValidator = new QDoubleValidator(0, 999, 6, cell_b);
1013   cellbValidator->setNotation(QDoubleValidator::StandardNotation);
1014   QDoubleValidator *cellcValidator = new QDoubleValidator(0, 999, 6, cell_c);
1015   cellcValidator->setNotation(QDoubleValidator::StandardNotation);
1016   QDoubleValidator *cellalValidator = new QDoubleValidator(0, 360, 6, cell_alpha);
1017   cellalValidator->setNotation(QDoubleValidator::StandardNotation);
1018   QDoubleValidator *cellbeValidator = new QDoubleValidator(0, 360, 6, cell_beta);
1019   cellbeValidator->setNotation(QDoubleValidator::StandardNotation);
1020   QDoubleValidator *cellgaValidator = new QDoubleValidator(0, 360, 6, cell_gamma);
1021   cellgaValidator->setNotation(QDoubleValidator::StandardNotation);
1022   //connect(cell_a, SIGNAL(textChanged(QString)),cell_a, SLOT(validate_cellinput()));
1023   cell_a->setValidator(cellaValidator);
1024   cell_b->setValidator(cellbValidator);
1025   cell_c->setValidator(cellcValidator);
1026   cell_alpha->setValidator(cellalValidator);
1027   cell_beta->setValidator(cellbeValidator);
1028   cell_gamma->setValidator(cellgaValidator);
1029   cell_a->setMaximumWidth(getCharWidth(7));
1030   cell_b->setMaximumWidth(getCharWidth(7));
1031   cell_c->setMaximumWidth(getCharWidth(7));
1032   cell_alpha->setMaximumWidth(getCharWidth(7));
1033   cell_beta->setMaximumWidth(getCharWidth(7));
1034   cell_gamma->setMaximumWidth(getCharWidth(7));
1035 }
1036 
set_a_value(QString a)1037 void DSREditWindow::set_a_value(QString a) {
1038   cell_a->setStyleSheet("QLineEdit");
1039   if (a.contains(",")){
1040     a.replace(",", ".");
1041     cell_a->setText(a);
1042   }
1043   bool ok;
1044   this->a = a.toDouble(&ok);
1045   if (!ok) {
1046     cell_a->setStyleSheet("QLineEdit{background: rgb(255, 180, 180);}");
1047   }
1048   if (validateCell()) {
1049     emit line_error("");
1050   } else {
1051     emit line_error(QString(tr("<font color=red><b>Please define proper unit cell parameters!</font>")));
1052   }
1053 }
1054 
set_b_value(QString b)1055 void DSREditWindow::set_b_value(QString b) {
1056   cell_b->setStyleSheet("QLineEdit");
1057   if (b.contains(",")){
1058     b.replace(",", ".");
1059     cell_b->setText(b);
1060   }
1061   bool ok;
1062   this->b = b.toDouble(&ok);
1063   if (!ok) {
1064     cell_b->setStyleSheet("QLineEdit{background: rgb(255, 180, 180);}");
1065   }
1066   if (validateCell()) {
1067     emit line_error("");
1068   } else {
1069     emit line_error(QString(tr("<font color=red><b>Please define proper unit cell parameters!</font>")));
1070   }
1071 }
set_c_value(QString c)1072 void DSREditWindow::set_c_value(QString c) {
1073   cell_c->setStyleSheet("QLineEdit");
1074   if (c.contains(",")){
1075     c.replace(",", ".");
1076     cell_c->setText(c);
1077   }
1078   bool ok;
1079   this->c = c.toDouble(&ok);
1080   if (!ok) {
1081     cell_c->setStyleSheet("QLineEdit{background: rgb(255, 180, 180);}");
1082   }
1083   if (validateCell()) {
1084     emit line_error("");
1085   } else {
1086     emit line_error(QString(tr("<font color=red><b>Please define proper unit cell parameters!</font>")));
1087   }
1088 }
1089 
set_alpha_value(QString alpha)1090 void DSREditWindow::set_alpha_value(QString alpha) {
1091   cell_alpha->setStyleSheet("QLineEdit");
1092   if (alpha.contains(",")){
1093     alpha.replace(",", ".");
1094     cell_alpha->setText(alpha);
1095   }
1096   bool ok;
1097   this->alpha = alpha.toDouble(&ok);
1098   if (!ok) {
1099     cell_alpha->setStyleSheet("QLineEdit{background: rgb(255, 180, 180);}");
1100   }
1101   if (validateCell()) {
1102     emit line_error("");
1103   } else {
1104     emit line_error(QString(tr("<font color=red><b>Please define proper unit cell parameters!</font>")));
1105   }
1106 }
1107 
set_beta_value(QString beta)1108 void DSREditWindow::set_beta_value(QString beta) {
1109   cell_beta->setStyleSheet("QLineEdit");
1110   if (beta.contains(",")){
1111     beta.replace(",", ".");
1112     cell_beta->setText(beta);
1113   }
1114   bool ok;
1115   this->beta = beta.toDouble(&ok);
1116   if (!ok) {
1117     cell_beta->setStyleSheet("QLineEdit{background: rgb(255, 180, 180);}");
1118   }
1119   if (validateCell()) {
1120     emit line_error("");
1121   } else {
1122     emit line_error(QString(tr("<font color=red><b>Please define proper unit cell parameters!</font>")));
1123   }
1124 }
1125 
set_gamma_value(QString gamma)1126 void DSREditWindow::set_gamma_value(QString gamma) {
1127   cell_gamma->setStyleSheet("QLineEdit");
1128   if (gamma.contains(",")){
1129     gamma.replace(",", ".");
1130     cell_gamma->setText(gamma);
1131   }
1132   bool ok;
1133   this->gamma = gamma.toDouble(&ok);
1134   if (!ok) {
1135     cell_gamma->setStyleSheet("QLineEdit{background: rgb(255, 180, 180);}");
1136   }
1137   if (validateCell()) {
1138     emit line_error("");
1139   } else {
1140     emit line_error(QString(tr("<font color=red><b>Please define proper unit cell parameters!</font>")));
1141   }
1142 }
1143 
getCharWidth(int numchars)1144 int DSREditWindow::getCharWidth(int numchars) {
1145   //! returns the width of a numchar amount of characters
1146   QString buchstaben;
1147   buchstaben.clear();
1148   for (int i=1; i<=numchars; i++) {
1149     buchstaben += "#";
1150   }
1151 
1152 #if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
1153   return QFontMetrics(this->font()).horizontalAdvance(buchstaben);
1154 }
1155 #else
1156     return QFontMetrics(this->font()).width(buchstaben);
1157 }
1158 #endif
1159 
deleteCurrentFragment()1160 void DSREditWindow::deleteCurrentFragment(){
1161   //! delete the currently open fragment
1162   deleteFragment(m_nametag);
1163 }
1164 
deleteFragment(QString nametag)1165 void DSREditWindow::deleteFragment(QString nametag){
1166   //! deletes the fragment with tag "nametag" from and only
1167   //! from the dsr_user_db.txt.
1168   QStringList udb =  readFragmentDB();
1169   QStringList udb2;
1170   udb2.clear();
1171   int startlineNumber, endlineNumber;
1172   startlineNumber = -1;
1173   endlineNumber = -1;
1174   for (int i=0; i<udb.size(); i++) {
1175     if (udb[i].toUpper().startsWith("<"+nametag.toUpper()+">")){
1176       if (startlineNumber > 0) {
1177         setInfoLabel(QString(tr("Duplicate entry for %1 found in the users database")).arg(nametag));
1178         return;} // in this case we have a dublicate entry
1179       startlineNumber = i;
1180     }
1181     if (udb[i].toUpper().startsWith("</"+nametag.toUpper()+">")){
1182       if (endlineNumber > 0) {
1183         setInfoLabel(QString(tr("Duplicate entry for %1 found in the users database")).arg(nametag));
1184         return;} // in this case we have a dublicate entry
1185       endlineNumber = i;
1186     }
1187   }
1188   if ((startlineNumber < 0) || (endlineNumber < 0)) {
1189     setInfoLabel(QString(tr("Start or end tag %1 missing in the users database")).arg(nametag));
1190     return; // in this case any of the db tags was not found
1191   }
1192   if (startlineNumber > 0) {
1193     udb2 = udb.mid(0, startlineNumber);
1194   }
1195   udb2 = udb2+udb.mid(endlineNumber+1, -1);
1196   writeFragmentDB(udb2);
1197 }
1198 
readFragmentDB()1199 QStringList DSREditWindow::readFragmentDB() {
1200   //! read the entire user databse into a stringlist
1201   QFile file(mydsrdbpath+"/dsr_user_db.txt");
1202   QStringList stringList;
1203   if (file.open(QFile::ReadOnly | QFile::Text)) {
1204     QTextStream textStream(&file);
1205     while (true) {
1206       QString line = textStream.readLine();
1207       if (line.isNull()) {
1208           break;
1209       }
1210       if (line.isEmpty()) {
1211         continue;
1212       } else {
1213         stringList.append(line);
1214       }
1215     }
1216   }
1217   file.close();
1218   return stringList;
1219 }
1220 
writeFragmentDB(QStringList database)1221 void DSREditWindow::writeFragmentDB(QStringList database) {
1222   //! write the entire user databse into a text file
1223   if (userdb == false) {
1224     return;
1225   } // never delete from main DB
1226   QFile file(mydsrdbpath+"/dsr_user_db.txt");
1227   if (file.open(QFile::ReadWrite | QFile::Text)) {
1228     QTextStream out(&file);
1229     file.resize(0); // delete original
1230     out << database.join("\n");
1231   }
1232   file.close();
1233 }
1234 
doesFragExist(QString name,bool fullname=false)1235 bool DSREditWindow::doesFragExist(QString name, bool fullname=false) {
1236   //! checks if a fragment nametag is already in the database
1237   //! setting fullname to true checks for the full name instead of the tag
1238   int column = 0;
1239   if (fullname) {
1240     column = 1;
1241   }
1242   foreach (QStringList line, m_frags) {
1243     if (line.at(column).toLower() == name.toLower()) {
1244       return true;
1245     }
1246   }
1247   return false;
1248 }
1249 
randString(int len)1250 QString DSREditWindow::randString(int len) {
1251   //! returns a random string of size len
1252   //! from http://stackoverflow.com/questions/3244999/create-a-random-string-or-number-in-qt4
1253   QString str;
1254   str.resize(len);
1255   for (int s = 0; s < len ; ++s) {
1256     str[s] = QChar('A' + char(qrand() % ('Z' - 'A')));
1257   }
1258   return str;
1259 }
1260 
inventNameTag(int length=5)1261 QString DSREditWindow::inventNameTag(int length=5){
1262   //! invents a random name tag for a new fragment
1263   QString name;
1264   bool there = true;
1265   while (there) {
1266     name = randString(length);
1267     there = doesFragExist(name, false);
1268   }
1269   return name;
1270 }
1271 
1272