1 /* Copyright (c) 2015 Gerald Knizia
2 *
3 * This file is part of the IboView program (see: http://www.iboview.org)
4 *
5 * IboView is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3.
8 *
9 * IboView is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with bfint (LICENSE). If not, see http://www.gnu.org/licenses/
16 *
17 * Please see IboView documentation in README.txt for:
18 * -- A list of included external software and their licenses. The included
19 * external software's copyright is not touched by this agreement.
20 * -- Notes on re-distribution and contributions to/further development of
21 * the IboView software
22 */
23
24 #include <algorithm>
25 #include <QTextStream>
26 #include <QItemSelectionModel>
27 #include <QProgressDialog>
28
29 #include "Iv.h"
30 #include "IvEditFramesForm.h"
31 #include "ui_EditFramesForm.h"
32 #include "IvDocument.h"
33
34
35
FFrameListModel(FDocument * pDocument_,QObject * Parent_)36 FFrameListModel::FFrameListModel(FDocument *pDocument_, QObject *Parent_)
37 : QAbstractTableModel(Parent_), m_pDocument(pDocument_)
38 {
39 ConnectForwardSignals();
40 }
41
~FFrameListModel()42 FFrameListModel::~FFrameListModel()
43 {
44 }
45
46
rowCount(const QModelIndex & parent) const47 int FFrameListModel::rowCount(const QModelIndex &parent) const
48 {
49 return m_pDocument->GetNumFrames();
50 IR_SUPPRESS_UNUSED_WARNING(parent);
51 }
52
columnCount(const QModelIndex & parent) const53 int FFrameListModel::columnCount(const QModelIndex &parent) const
54 {
55 return 2; // ID, measure, name
56 IR_SUPPRESS_UNUSED_WARNING(parent);
57 }
58
headerData(int section,Qt::Orientation orientation,int role) const59 QVariant FFrameListModel::headerData(int section, Qt::Orientation orientation, int role) const
60 {
61 if (role == Qt::DisplayRole && orientation == Qt::Vertical) {
62 FFrame
63 *pFrame = m_pDocument->GetFrame(section, false);
64 if (pFrame == 0)
65 return QVariant("#UNK");
66 return IvFmt("%1", section); // frame number.
67 }
68 if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
69 if (section == 0)
70 return QVariant("s (Arc)");
71 if (section == 1)
72 return QVariant("Frame");
73 return QVariant("#UNK");
74 }
75 if ( role == Qt::FontRole ) {
76 QFont CaptionFont;
77 CaptionFont.setBold(true);
78 return CaptionFont;
79 }
80 return QVariant();
81 }
82
data(const QModelIndex & index,int role) const83 QVariant FFrameListModel::data(const QModelIndex &index, int role) const
84 {
85 if (role == Qt::DisplayRole) {
86 // FMeasure const
87 // *pMeasure = GetMeasure(index);
88 // FFrame
89 // *pFrame = m_pDocument->GetFrame(index.row(),false);
90 // if (pFrame == 0 || pMeasure == 0)
91 // return QVariant("#UNK");
92 // return QVariant(pMeasure->MeasureFrame(pFrame,FMeasure::FORMAT_ValueOnly));
93 FFrame
94 *pFrame = m_pDocument->GetFrame(index.row(), false);
95 if (index.column() == 1 && pFrame != 0)
96 return QVariant(pFrame->GetFullInputFileName());
97 }
98 if (role == Qt::TextAlignmentRole) {
99 if (index.column() == 0)
100 return QVariant(Qt::AlignRight);
101 else
102 return QVariant(Qt::AlignLeft);
103 }
104 return QVariant();
105 }
106
removeRows(int row,int count,const QModelIndex & parent)107 bool FFrameListModel::removeRows(int row, int count, const QModelIndex &parent)
108 {
109 if (count < 0 || row + count >= rowCount()) {
110 IV_NOTIFY(NOTIFY_Warning, "Attempted to remove non-existent frame.");
111 return false;
112 }
113 // beginRemoveRows(parent, row, row + count - 1);
114 // FMeasureList::iterator
115 // itFirst = m_List.begin() + row,
116 // itLast = itFirst + count;
117 // m_List.erase(itFirst, itLast);
118 // endRemoveRows();
119 // return true;
120 return false;
121 IR_SUPPRESS_UNUSED_WARNING(parent);
122 }
123
makeRowIndex(int iRow)124 QModelIndex FFrameListModel::makeRowIndex(int iRow)
125 {
126 return createIndex(iRow, 0, (void*)0);
127 }
128
129
ConnectForwardSignals()130 void FFrameListModel::ConnectForwardSignals()
131 {
132 // forward those things... to deal with newly inserted frames etc.
133 connect(m_pDocument, SIGNAL(layoutAboutToBeChanged()), this, SLOT(parentLayoutAboutToBeChanged()));
134 connect(m_pDocument, SIGNAL(layoutChanged()), this, SLOT(parentLayoutChanged()));
135 }
136
parentLayoutAboutToBeChanged()137 void FFrameListModel::parentLayoutAboutToBeChanged()
138 {
139 emit layoutAboutToBeChanged();
140 }
141
parentLayoutChanged()142 void FFrameListModel::parentLayoutChanged()
143 {
144 emit layoutChanged();
145 }
146
147
148
FEditFramesForm(FDocument * document,QWidget * parent)149 FEditFramesForm::FEditFramesForm(FDocument *document, QWidget *parent)
150 : QDialog(parent),
151 ui(new Ui::EditFramesForm),
152 m_pDocument(document),
153 m_pFrameList(new FFrameListModel(document, this)),
154 m_OrbitalRelinkNeeded(false)
155 {
156 ui->setupUi(this);
157 // ui->label_WorkSpace->setVisible(false);
158 // ui->spinBox_WorkSpacePerThread->setVisible(false);
159 // ui->spinBox_WorkSpacePerThread->setEnabled(false);
160
161 // ui->tabWidget->layout()->setContentsMargins(0, 0, 0, 0); // left, top, right, bottom
162 // connect(ui->checkBox_RunIbba, SIGNAL(toggled(bool)), this, SLOT(ToggleIbbaPage(bool)));
163
164 ui->tableView_FrameList->setModel(m_pFrameList);
165
166 UpdateExcludedAtoms();
167
168 connect(ui->toolButton_MoveUp, SIGNAL(clicked()), this, SLOT(moveFramesUp()));
169 connect(ui->toolButton_MoveDown, SIGNAL(clicked()), this, SLOT(moveFramesDown()));
170 connect(ui->toolButton_MoveToTop, SIGNAL(clicked()), this, SLOT(moveFramesToTop()));
171 connect(ui->toolButton_MoveToBottom, SIGNAL(clicked()), this, SLOT(moveFramesToBottom()));
172 connect(ui->toolButton_ReverseFrames, SIGNAL(clicked()), this, SLOT(reverseFrameOrder()));
173 connect(ui->toolButton_DeleteSelected, SIGNAL(clicked()), this, SLOT(deleteFrames()));
174 connect(ui->toolButton_AlignFrames, SIGNAL(clicked()), this, SLOT(alignFrames()));
175
176 ui->buttonBox->setFocus();
177 // setAttribute(Qt::WA_DeleteOnClose); // should I do this?
178 }
179
~FEditFramesForm()180 FEditFramesForm::~FEditFramesForm()
181 {
182 delete ui;
183 }
184
185
UpdateExcludedAtoms()186 void FEditFramesForm::UpdateExcludedAtoms()
187 {
188 // todo: go though atom flags of document and check which ones have the flag set.
189 int
190 nAt = m_pDocument->nAtomsWithFlags();
191 QString
192 s;
193 QTextStream
194 str(&s);
195 str << "Atoms Excluded: ";
196 int nAtomsExcluded = 0;
197 for (int iAt = 0; iAt < nAt; ++ iAt) {
198 if (m_pDocument->IsAtomExcludedFromAlignment(iAt)) {
199 if (nAtomsExcluded != 0)
200 str << " | ";
201 str << m_pDocument->AtomLabel(iAt);
202 nAtomsExcluded += 1;
203 }
204 }
205 // if (nAtomsExcluded <= 0) {
206 // ui->label_ExcludedAtoms->setText("(no atoms excluded.)");
207 // } else {
208 // ui->label_ExcludedAtoms->setText(s);
209 // }
210 if (nAtomsExcluded <= 0)
211 str << "None";
212 ui->label_ExcludedAtoms->setText(s);
213 }
214
215
InvertOrder(FFrameIndexList & iAll_,FFrameIndexList & iSelected_)216 static void InvertOrder(FFrameIndexList &iAll_, FFrameIndexList &iSelected_)
217 {
218 FFrameIndexList
219 iAll,
220 iSelected;
221 iAll.reserve(iAll_.size());
222 size_t
223 n = iAll_.size(),
224 s = iSelected_.size();
225 for (size_t i = n-1; i < n; -- i)
226 iAll.push_back(iAll_[i]);
227 iSelected.reserve(iSelected_.size());
228 for (size_t i = s-1; i < s; -- i)
229 iSelected.push_back(n - iSelected_[i] - 1);
230 iAll_.swap(iAll);
231 iSelected_.swap(iSelected);
232 }
233
MoveSelectedUp(FFrameIndexList & iAll,FFrameIndexList const & iSelected)234 static void MoveSelectedUp(FFrameIndexList &iAll, FFrameIndexList const &iSelected)
235 {
236 if (iAll.empty() || iSelected.empty())
237 return; // no frames there or nothing to move.
238 // assert(iAll[0] == 0 || iAll.empty());
239 assert(iAll[0] == 0 || iAll[0] == int(iAll.size()-1));
240
241 // note: this logic is for handling cases like that:
242 // #Frame Selected?
243 // 0 yes
244 // 1 yes
245 // 2 no
246 // 3 no
247 // 4 yes
248 // 5 yes
249 // In this case everything which can be moved up, should be. The new order would
250 // become: 0 1 2 4 5 3. But Frames 0 and 1 cannot be moved up, since they already
251 // are at the top positions.
252 int i = 0;
253 // while (iSelected[i] == iAll[i] && i < int(iSelected.size()))
254 while (iSelected[i] == i && i < int(iSelected.size()))
255 ++ i;
256 for (; i < int(iSelected.size()); ++ i)
257 {
258 int
259 iOld = iSelected[i],
260 iNew = iOld - 1;
261 assert(iNew > 0);
262 std::swap(iAll[iOld], iAll[iNew]);
263 }
264 }
265
isSelected(FFrameIndexList::value_type iVal,FFrameIndexList const & iSelected)266 static bool isSelected(FFrameIndexList::value_type iVal, FFrameIndexList const &iSelected)
267 {
268 FFrameIndexList::const_iterator
269 itSel = std::lower_bound(iSelected.begin(), iSelected.end(), iVal);
270 return itSel != iSelected.end() && *itSel == iVal;
271 }
272
MoveSelectedToTop(FFrameIndexList & iAll_,FFrameIndexList const & iSelected)273 static void MoveSelectedToTop(FFrameIndexList &iAll_, FFrameIndexList const &iSelected)
274 {
275 // algorithm requires selected items to be sorted and mutually distinct.
276 // #ifdef _DEBUG
277 for (size_t i = 1; i < iSelected.size(); ++ i)
278 assert(iSelected[i-1] < iSelected[i]);
279 // #endif
280 // start out with the selected items.
281 FFrameIndexList
282 iAll;
283 iAll.reserve(iAll_.size());
284 for (size_t i = 0; i < iSelected.size(); ++ i)
285 iAll.push_back(iAll_[size_t(iSelected[i])]);
286
287 // add all other items in original order, unless they were already included in the
288 // sorted subset.
289 for (size_t i = 0; i < iAll_.size(); ++ i) {
290 if (!isSelected(int(i), iSelected))
291 iAll.push_back(iAll_[i]);
292 }
293 iAll_.swap(iAll);
294 }
295
296
moveFramesUp()297 void FEditFramesForm::moveFramesUp()
298 {
299 FFrameIndexList
300 iAll = GetBaseFrameOrder(),
301 iSelected = GetSelectedFrameIndices();
302 MoveSelectedUp(iAll, iSelected);
303 SetNewFrameOrder(iAll);
304 }
305
moveFramesDown()306 void FEditFramesForm::moveFramesDown()
307 {
308 FFrameIndexList
309 iAll = GetBaseFrameOrder(),
310 iSelected = GetSelectedFrameIndices();
311 InvertOrder(iAll, iSelected);
312 MoveSelectedUp(iAll, iSelected);
313 InvertOrder(iAll, iSelected);
314 SetNewFrameOrder(iAll);
315 }
316
moveFramesToTop()317 void FEditFramesForm::moveFramesToTop()
318 {
319 FFrameIndexList
320 iAll = GetBaseFrameOrder(),
321 iSelected = GetSelectedFrameIndices();
322 MoveSelectedToTop(iAll, iSelected);
323 SetNewFrameOrder(iAll);
324 }
325
moveFramesToBottom()326 void FEditFramesForm::moveFramesToBottom()
327 {
328 FFrameIndexList
329 iAll = GetBaseFrameOrder(),
330 iSelected = GetSelectedFrameIndices();
331 InvertOrder(iAll, iSelected);
332 MoveSelectedToTop(iAll, iSelected);
333 InvertOrder(iAll, iSelected);
334 SetNewFrameOrder(iAll);
335 }
336
reverseFrameOrder()337 void FEditFramesForm::reverseFrameOrder()
338 {
339 FFrameIndexList
340 iAll = GetBaseFrameOrder(),
341 iSelected = GetSelectedFrameIndices();
342 if (iAll.empty())
343 return;
344 if (iSelected.empty() || iSelected.size() == 1u)
345 // nothing selected? invert entire list.
346 iSelected = iAll;
347 assert(iAll[0] == 0 && iAll.back() == int(iAll.size() - 1));
348
349 // go through the first half of the selected elements, and exchange
350 // them with the second half. In case of odd number, center element stays
351 // where it is.
352 for (size_t i = 0; i != iSelected.size()/2; ++ i) {
353 size_t
354 j = iSelected.size() - i - 1;
355 assert(i < j);
356 size_t
357 iFrame = iSelected[i],
358 jFrame = iSelected[j];
359 assert(iFrame << iAll.size() && jFrame < iAll.size());
360 std::swap(iAll[iFrame], iAll[jFrame]);
361 }
362 // for (size_t i = 0; i != iAll.size(); ++ i)
363 // IvEmit(" NewFrame %1: %2", i, iAll[i] );
364 SetNewFrameOrder(iAll);
365 }
366
deleteFrames()367 void FEditFramesForm::deleteFrames()
368 {
369 FFrameIndexList
370 iAll = GetBaseFrameOrder(),
371 iNew,
372 iSelected = GetSelectedFrameIndices();
373 if (iAll.empty() || iSelected.empty())
374 return;
375
376 // go through the first half of the selected elements, and exchange
377 // them with the second half. In case of odd number, center element stays
378 // where it is.
379 for (size_t i = 0; i != iAll.size(); ++ i) {
380 if (!isSelected(i, iSelected))
381 iNew.push_back(iAll[i]);
382 }
383 SetNewFrameOrder(iNew, false);
384 // ^- false: do not update selection. In practice
385 // this means that the cursor stays in the row it was in before, which
386 // now points to the next frame. (good for deleting stuff sequentially)
387 }
388
alignFrames()389 void FEditFramesForm::alignFrames()
390 {
391 QString
392 AlignMode;
393 switch(ui->comboBox_AlignWeight->currentIndex()) {
394 case 0: AlignMode = "mass"; break;
395 case 1: AlignMode = "charge"; break;
396 case 2: AlignMode = "coords"; break; // every atom gets the same weight (except excluded ones)
397 }
398 m_pDocument->AlignFrames(AlignMode);
399 m_OrbitalRelinkNeeded = true;
400
401 // FIXME: once IRC coords are printed, we should also update them now...
402
403 // what about LinkVisualConfigs?
404 }
405
406
SetNewFrameOrder(FFrameIndexList const & NewIndices,bool UpdateSelection)407 void FEditFramesForm::SetNewFrameOrder(FFrameIndexList const &NewIndices, bool UpdateSelection)
408 {
409 FFrameIndexList
410 iSelectedOld = GetSelectedFrameIndices();
411
412 m_pDocument->ReorderOrRestrictFrameSet(NewIndices);
413
414 if (UpdateSelection) {
415 // keep track of selection: Select new subset of frames which was selected before.
416 QItemSelection
417 newSelection;
418 for (size_t i = 0; i < NewIndices.size(); ++ i) {
419 if (isSelected(NewIndices[i], iSelectedOld))
420 newSelection.push_back(QItemSelectionRange(m_pFrameList->makeRowIndex(int(i))));
421 }
422 QItemSelectionModel
423 *selectionModel = ui->tableView_FrameList->selectionModel();
424 selectionModel->select(newSelection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
425 }
426 m_OrbitalRelinkNeeded = true;
427 }
428
GetBaseFrameOrder()429 FFrameIndexList FEditFramesForm::GetBaseFrameOrder()
430 {
431 FFrameIndexList r;
432 r.reserve(m_pDocument->GetNumFrames());
433 for (int iFrame = 0; iFrame < m_pDocument->GetNumFrames(); ++ iFrame)
434 r.push_back(iFrame);
435 return r;
436 }
437
GetSelectedFrameIndices()438 FFrameIndexList FEditFramesForm::GetSelectedFrameIndices()
439 {
440 QItemSelectionModel
441 *selectionModel = ui->tableView_FrameList->selectionModel();
442 QModelIndexList
443 selectedRows = selectionModel->selectedRows();
444 FFrameIndexList
445 r;
446 r.reserve(selectedRows.size());
447 for (int i = 0; i < selectedRows.size(); ++ i)
448 r.push_back(selectedRows[i].row());
449 std::sort(r.begin(), r.end());
450 return r;
451 }
452
453
454 // this doesn't work reliably...
455 // only called if the window is closed by pressing X, not via "ok"/"cancel",
456 // or other means.
457
458 // void FEditFramesForm::closeEvent(QCloseEvent *event)
459 // {
460 // IvEmit("ENTERED CLOSE-EVENT.");
461 // if (m_OrbitalRelinkNeeded) {
462 // IvEmit("ENTERED RE-LINK ORBITALS.");
463 // QProgressDialog
464 // progress("Re-Linking corresponding orbitals...", QString(), 0, m_pDocument->GetNumFrames(), this);
465 // progress.setMinimumDuration(0);
466 // progress.setWindowModality(Qt::WindowModal);
467 // progress.setValue(0);
468 // m_pDocument->LinkOrbitals();
469 // progress.setValue(m_pDocument->GetNumFrames());
470 // }
471 //
472 // QDialog::closeEvent(event);
473 // }
474
DoPostProcessingOnClose()475 void FEditFramesForm::DoPostProcessingOnClose()
476 {
477 IvEmit("ENTERED POST-PROCESS-EVENT.");
478 if (m_OrbitalRelinkNeeded) {
479 IvEmit("ENTERED RE-LINK ORBITALS.");
480 QProgressDialog
481 progress("Re-Linking corresponding orbitals...", QString(), 0, m_pDocument->GetNumFrames(), this);
482 progress.setMinimumDuration(0);
483 progress.setWindowModality(Qt::WindowModal);
484 progress.setValue(0);
485 m_pDocument->LinkOrbitals();
486 progress.setValue(m_pDocument->GetNumFrames());
487 }
488 }
489