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 "Iv.h"
25 #include <iostream>
26 #include <QApplication>
27 #include <QDialog>
28 #include <QTableView>
29 #include <QTreeView>
30 #include <QIcon>
31 #include <QFileDialog>
32 #include <QTimer>
33 #include <QProgressDialog>
34 #include <QSizePolicy>
35 #include <QStyleFactory>
36 #include <QClipboard>
37 #include <QMessageBox>
38 #include <QFileInfo>
39 #include <QFile>
40 #include <QTextStream>
41 #include <QStringList>
42 #include <QUrl>
43 #include <QList>
44 #include <QSize>
45 #include <QMenu>
46 #include <QAction>
47 #include <QShortcut>
48 #include <QSettings>
49 // #include <QCommandLineParser>
50 // ^- it's for > QT 5.2 only :(.
51 #include <QThreadPool>
52 #include <QRunnable>
53 #include <QMimeData>
54 #include "optionparser.h" // lean mean c++ option parser (see: optionparser.sourceforge.net)
55
56 #include "QPropertyModel.h"
57
58 // #include <fstream>
59
60 #include <cmath>
61 #include <sstream>
62 #include <boost/format.hpp>
63
64 #include "IvMain.h"
65 #include "IvShowTextForm.h"
66 #include "IvSettings.h"
67 #include "IvLog.h"
68 #include "ui_MainForm2.h"
69 #include "ui_AboutForm.h"
70 #include "IvView3D.h"
71 #include "IvScript.h"
72 #include "IvIrc.h"
73 #include "IvComputeWfForm.h"
74 #include "IvTables.h"
75 #include "IvEditFramesForm.h"
76 #include "IvPreferencesForm.h"
77
78 #include "CtBasisLibrary.h"
79 #include "CxOpenMpProxy.h"
80 #include "CxColor.h"
81 // #include "CxOsInt.h"
82 using ct::FColor;
83
q2s(QString const & s)84 std::string q2s(QString const &s) {
85 return s.toStdString();
86 }
87
s2q(std::string const & s)88 QString s2q(std::string const &s) {
89 // return QString(s.c_str());
90 return QString::fromStdString(s);
91 }
92
93 static bool s_UseStyleFiles = false;
94 bool g_ShowVirtualOrbitals = true;
95 int g_nMaxOmpThreads = 1;
96
97 // bool ends_with(std::string const &s, std::string const &ending)
98 // {
99 // if (s.length() >= ending.length()) {
100 // return (0 == s.compare (s.length() - ending.length(), ending.length(), ending));
101 // } else {
102 // return false;
103 // }
104 // }
105
ThisAsIApp()106 IApplication *FMainWindow::ThisAsIApp()
107 {
108 assert(dynamic_cast<IApplication*>(this) != 0);
109 if (dynamic_cast<IApplication*>(this) == 0) {
110 std::cerr << "Cast to IApplication failed. Invoked via c'tor?" << std::endl;
111 }
112 return dynamic_cast<IApplication*>(this);
113 }
114
115
116
isScriptFile(QString const & FileName)117 bool isScriptFile(QString const &FileName) {
118 QFileInfo
119 FileInfo(FileName);
120 QString
121 FileExt = FileInfo.suffix();
122 return FileExt == "js" || FileExt == "chai";
123 }
124
load_file(QString const & FileName)125 void IApplication::load_file(QString const &FileName) {
126 QStringList
127 L;
128 L.append(FileName);
129 return FMainWindow::load_files_(L);
130 }
131
close_files()132 void IApplication::close_files()
133 {
134 return FMainWindow::close_files_();
135 }
136
load_files_(QStringList const & FileNames_)137 void FMainWindow::load_files_(QStringList const &FileNames_)
138 {
139 if (FileNames_.empty())
140 return;
141
142 QStringList
143 DataFiles;
144 foreach(QString FileName, FileNames_) {
145 if (FileName == "")
146 continue;
147 if (isScriptFile(FileName)) {
148 if (!DataFiles.empty()) {
149 // first load all the data files we still have in the cache.
150 document->Load(DataFiles);
151 DataFiles.clear();
152 }
153 // now execute the script.
154 ExecScript(ThisAsIApp(), this->view3d, FileName);
155 document->SetInputFileName(FileName);
156 } else {
157 // it's a data file. We keep on piling them up until we
158 // either have all of them or we encounter a script file.
159 DataFiles.append(FileName);
160 }
161 }
162
163 // load the remaining data files (if there should be any).
164 document->Load(DataFiles);
165 }
166
close_files_()167 void FMainWindow::close_files_()
168 {
169 document->Clear();
170 }
171
172
173
toStringList(QScriptValue const & ScriptList)174 QStringList toStringList(QScriptValue const &ScriptList)
175 {
176 QStringList
177 r;
178 int Length = ScriptList.property("length").toInteger();
179 for (int i = 0; i < Length; ++ i)
180 r.append(ScriptList.property(i).toString());
181 return r;
182 }
183
load_files(QScriptValue const & FileList)184 void IApplication::load_files(QScriptValue const &FileList)
185 {
186 FMainWindow::load_files_(toStringList(FileList));
187 // document->Load(toStringList(FileList));
188 // int Length = FileList.property("length").toInteger();
189 // for (int i = 0; i < Length; ++ i)
190 // load_file(FileList.property(i).toString());
191 }
192
193
orient_frames(QString const & Mode)194 void IApplication::orient_frames(QString const &Mode)
195 {
196 document->AlignFrames(Mode);
197 // this invalidates all already rendered data.
198 onRebuildIsoSurfacesClicked();
199 }
200
201
get_frame_name()202 QString IApplication::get_frame_name()
203 {
204 return document->GetCurrentInputBaseFileName();
205 }
206
207
pGetOrbital(int iMo)208 FOrbital *FMainWindow::pGetOrbital(int iMo)
209 {
210 if (document->GetNumFrames() == 0) {
211 IvNotify(NOTIFY_Warning, QString("pGetOrbital: no frames loaded. Cannot get MO #%1").arg(iMo));
212 return 0;
213 };
214 FOrbital *pOut = document->GetCurrentFrame()->pGetOrbital(iMo);
215 if (pOut == 0)
216 IvNotify(NOTIFY_Warning, QString("pGetOrbital: attemped to access non-existent MO #%1.").arg(iMo));
217 return pOut;
218 }
219
220
set_frame(int iFrame)221 void IApplication::set_frame(int iFrame)
222 {
223 if (iFrame >= document->GetNumFrames()) {
224 IvNotify(NOTIFY_Warning, QString("SetFrame: tried to select frame %i, but valid frame ids are only 0...%i.").arg(iFrame).arg(document->GetNumFrames()));
225 // std::cerr << boost::format("error: tried to select frame %i, but valid frame ids are only 0...%i.") % iFrame % document->GetNumFrames() << std::endl;
226 return;
227 }
228 // ^- wtf? how could that possibly have worked in the script?!
229 document->SetActiveCol(iFrame);
230 }
231
232
show_mo(int iMo,int cIsoPlus_,int cIsoMinus_)233 void IApplication::show_mo(int iMo, /*float fColorPhase, */int cIsoPlus_, int cIsoMinus_)
234 {
235 FOrbital
236 *pOrbital = pGetOrbital(iMo);
237 if (pOrbital == 0)
238 return;
239
240 FOrbitalVisualConfig
241 *pVisConfig = pOrbital->pVisConfig.get();
242 // ^- is that really right? it might have been set beforehand if the VisConfig
243 // object is linked (on the other hand, show_mo toggles the row in all frames...).
244 // pVisConfig->SetColorFromCentralHue(fColorPhase);
245 // pVisConfig->SetColorFromCentralHue(-1);
246 pVisConfig->AssignDefaultColor(pOrbital->GetDocument());
247 // if (cIsoPlus_ != -1) // hm... -1 is actually a valid value (full white).
248 pVisConfig->cIsoPlus = (uint32_t) cIsoPlus_;
249 // if (cIsoMinus_ != -1) // hm... -1 is actually a valid value (full white).
250 pVisConfig->cIsoMinus = (uint32_t) cIsoMinus_;
251
252 // pVisConfig->fIsoValue = fDefaultIsoValue;
253 // pVisConfig->iIsoType = DefaultIsoType;
254
255 if (!pOrbital->Active)
256 document->ToggleDataRow(iMo); // first (#0) is geometry. But we index MOs 1-based.
257
258 // ^- hm... that's not quite right. will not do anything if orbital had been there before...
259 QCoreApplication::processEvents();
260 }
261
hide_mo(int iMo)262 void IApplication::hide_mo(int iMo)
263 {
264 FOrbital
265 *pOrbital = pGetOrbital(iMo);
266 if (pOrbital == 0)
267 return;
268 if (pOrbital->Active)
269 document->ToggleDataRow(iMo);
270 QCoreApplication::processEvents();
271 }
272
Scale(double * p,std::size_t N,double f)273 static void Scale(double *p, std::size_t N, double f)
274 {
275 for (std::size_t i = 0; i < N; ++ i)
276 p[i] *= f;
277 }
278
scale_mo(int iMo,double fFactor)279 void IFrame::scale_mo(int iMo, double fFactor)
280 {
281 FOrbital
282 *pOrbital = pGetOrbital(iMo);
283 if (pOrbital == 0)
284 return;
285 Scale(&pOrbital->pCoeffs[0], pOrbital->pCoeffs.size(), fFactor);
286 }
287
288 template<class FScalar>
rot2x2(FScalar & A,FScalar & B,FScalar cs,FScalar ss)289 inline void rot2x2(FScalar &A, FScalar &B, FScalar cs, FScalar ss) {
290 FScalar
291 tA = A * cs + B * ss,
292 tB = -A * ss + B * cs;
293 A = tA;
294 B = tB;
295 }
296
297 template<class FScalar>
rot2x2(FScalar * pA,FScalar * pB,std::size_t N,FScalar cs,FScalar ss)298 inline void rot2x2(FScalar *pA, FScalar *pB, std::size_t N, FScalar cs, FScalar ss) {
299 for (std::size_t i = 0; i < N; ++ i)
300 rot2x2(pA[i], pB[i], cs, ss);
301 }
302
rot_mos_2x2(int iMo,int jMo,double fAngle)303 void IFrame::rot_mos_2x2(int iMo, int jMo, double fAngle)
304 {
305 FOrbital
306 *pOrbitalI = pGetOrbital(iMo),
307 *pOrbitalJ = pGetOrbital(jMo);
308
309 if (pOrbitalI == 0 || pOrbitalJ == 0)
310 return;
311 double
312 cs = std::cos(M_PI/180. * fAngle),
313 ss = std::sin(M_PI/180. * fAngle);
314 assert(pOrbitalI->pCoeffs.size() == pOrbitalJ->pCoeffs.size());
315 rot2x2(&pOrbitalI->pCoeffs[0], &pOrbitalJ->pCoeffs[0], pOrbitalI->pCoeffs.size(), cs, ss);
316 }
317
318
hide_mos()319 void IApplication::hide_mos()
320 {
321 for (uint iMo = 1; iMo < document->GetCurrentFrame()->m_Data.size(); ++ iMo) {
322 FOrbital
323 *pOrbital = pGetOrbital(iMo);
324 if (pOrbital == 0)
325 return;
326 if (pOrbital->Active)
327 document->ToggleDataRow(iMo);
328 }
329 QCoreApplication::processEvents();
330 }
331
332
quit()333 void IApplication::quit()
334 {
335 QCoreApplication::processEvents();
336 // QCoreApplication::quit();
337 close(); // should work as long as this is the only window and the event loop was already entered...
338 std::cout << "!Invoked quit() from script." << std::endl;
339 }
340
341 // // FElementOptionsList &IApplication::element_options()
342 // QObjectList IApplication::element_options()
343 // {
344 // // return document->GetElementOptions();
345 // QObjectList
346 // q;
347 // FElementOptionsList
348 // ol = document->GetElementOptions();
349 // for (int i = 0; i < ol.size(); ++ i)
350 // q.append(&*ol[i]);
351 // return q;
352 // }
353
element_options(int iElem)354 QObject *IApplication::element_options(int iElem)
355 {
356 // return document->GetElementOptions();
357 FElementOptionsList
358 &ol = document->GetElementOptions();
359 // return document->pElementOptions(iAt);
360 if (iElem >= 0 && iElem < ol.size())
361 return ol[iElem].data();
362 return 0;
363 }
364
reset_element_options(int iElem)365 void IApplication::reset_element_options(int iElem)
366 {
367 FElementOptionsList
368 &ol = document->GetElementOptions();
369 // return document->pElementOptions(iAt);
370 if (iElem >= 0 && iElem < ol.size()) {
371 FElementOptions Dummy(iElem, 0);
372 ol[iElem]->CopyPropertiesFrom(Dummy);
373 }
374 }
375
atom_options(int iAtom_)376 QObject* IApplication::atom_options(int iAtom_)
377 {
378 int
379 iAtom = iAtom_ - 1; // external atom ids expected starting with 1.
380 FAtomOptions
381 &ao = document->AtomOptions(iAtom);
382 if (ao.pPropertiesOverride)
383 return &*ao.pPropertiesOverride;
384 // detach atom options from element defaults.
385 FElementOptions
386 *pOrig = document->pElementOptions(iAtom);
387 if (!pOrig)
388 return 0;
389 ao.pPropertiesOverride = new FElementOptions(pOrig, 0);
390 return &*ao.pPropertiesOverride;
391 }
392
reset_atom_options(int iAtom_)393 void IApplication::reset_atom_options(int iAtom_)
394 {
395 int
396 iAtom = iAtom_ - 1; // external atom ids expected starting with 1.
397 FAtomOptions
398 &ao = document->AtomOptions(iAtom);
399 ao.pPropertiesOverride = 0;
400 }
401
update_views()402 void IApplication::update_views()
403 {
404 view3d->update();
405 }
406
407
408
pGetActiveGeometry()409 FGeometry *FMainWindow::pGetActiveGeometry()
410 {
411 if (document->GetNumFrames() == 0)
412 return 0;
413 return document->GetCurrentFrame()->pGetGeometry();
414 }
415
num_frames()416 uint IApplication::num_frames()
417 {
418 return document->GetNumFrames();
419 }
420
frame(int iFrame)421 QObject* IApplication::frame(int iFrame)
422 {
423 return document->GetFrame(iFrame);
424 }
425
frame()426 QObject* IApplication::frame()
427 {
428 return document->GetCurrentFrame();
429 }
430
add_bond(int iAt,int jAt,QString const & Flags)431 void IApplication::add_bond(int iAt, int jAt, QString const &Flags)
432 {
433 for (uint iFrame = 0; iFrame < uint(document->GetNumFrames()); ++ iFrame)
434 document->GetFrame(iFrame)->add_bond(iAt, jAt, Flags);
435 }
436
delete_bond(int iAt,int jAt)437 void IApplication::delete_bond(int iAt, int jAt)
438 {
439 for (uint iFrame = 0; iFrame < uint(document->GetNumFrames()); ++ iFrame)
440 document->GetFrame(iFrame)->delete_bond(iAt, jAt);
441 }
442
reset_bonds()443 void IApplication::reset_bonds()
444 {
445 document->ResetBondLines();
446 }
447
set_atom_mode(int iAt,QString const & Mode)448 void IApplication::set_atom_mode(int iAt, QString const &Mode)
449 {
450 // if (iAt == 0 || std::size_t(iAt) > pGeometry->pAtoms->size()) {
451 // std::cout << boost::format("!WARNING: Attempted to change mode of non-exitent atom #%i to '%s'.") % iAt % Mode << std::endl;
452 // return;
453 // }
454 // note:
455 // (1) atom flags are now shared across all frames.
456 // (2) it is allowed to set them *before* the geometry is loaded.
457 // (e.g., in order to change the alignment flags, which need to be present on load).
458 if (iAt == 0) {
459 IvNotify(NOTIFY_Warning, "Atomic index 0 is not valid. Encountered in set_atom_mode.");
460 return;
461 }
462 uint
463 &AtomFlags = document->AtomFlags(iAt-1),
464 AtomFlagsOrig = AtomFlags;
465 QStringList
466 FlagList = Mode.split("|", QString::SkipEmptyParts);
467 foreach(QString Flag, FlagList) {
468 if (Flag == "hidden")
469 AtomFlags = AtomFlags | ATOM_Hidden;
470 else if (Flag == "visible")
471 AtomFlags = AtomFlags & (~ATOM_Hidden);
472 else if (Flag == "noalign" || Flag == "no-align")
473 AtomFlags = AtomFlags | ATOM_NoAlign;
474 else if (Flag == "align")
475 AtomFlags = AtomFlags & (~ATOM_NoAlign);
476 else {
477 IvNotify(NOTIFY_Warning, QString("Atom mode '%1' not recognized (attemped to set for atom %2). Ignored!").arg(Flag).arg(iAt));
478 }
479 }
480 if (AtomFlags != AtomFlagsOrig) {
481 FGeometry
482 *pGeometry = pGetActiveGeometry();
483 if (pGeometry && size_t(iAt-1) <= pGeometry->pAtoms->size()) {
484 ct::FAtom
485 &Atom = (*pGeometry->pAtoms)[iAt-1];
486 IvEmit(" Changed atom mode %1 %2 to '%3'", iAt, Atom.ElementName(), Mode);
487 } else {
488 IvEmit(" Stored mode '%1' for use on atom #%2 once loaded.", Mode, iAt);
489 }
490 }
491 }
492
reset_atom_modes()493 void IApplication::reset_atom_modes()
494 {
495 document->ClearAtomFlags();
496 }
497
498
RemovePath(QString const & FileName)499 QString RemovePath(QString const &FileName) {
500 return QFileInfo(FileName).fileName();
501 }
502
RemoveExt(QString const & FileName)503 QString RemoveExt(QString const &FileName) {
504 return QFileInfo(FileName).baseName();
505 }
506
ReplaceExt(QString const & FileName,QString const & NewExt)507 QString ReplaceExt(QString const &FileName, QString const &NewExt) {
508 QString
509 BaseName = RemoveExt(FileName);
510 if (NewExt == "")
511 return BaseName;
512 if (NewExt.startsWith("."))
513 return BaseName + NewExt;
514 return QString("%1.%2").arg(BaseName).arg(NewExt);
515 }
516
517
518
MakeStateScript()519 QString FMainWindow::MakeStateScript()
520 {
521 if (!document->GetCurrentFrameData())
522 return "";
523 std::stringstream
524 out;
525 // for (int iFrame = 0; iFrame < document->GetNumFrames(); ++ iFrame)
526 if (document->GetNumFrames() == 1)
527 out << boost::format("app.load_file(\"%s\");\n") % q2s(document->GetCurrentInputFileName());
528 else {
529 out << "app.load_files([";
530 for (int iFrame = 0; iFrame < document->GetNumFrames(); ++ iFrame) {
531 if (iFrame != 0)
532 out << ", ";
533 out << boost::format("\"%s\"") % q2s(document->GetFrame(iFrame)->GetFullInputFileName());
534 }
535 out << "]);\n";
536 }
537 out << boost::format("\n%s\n") % q2s(view3d->GetViewDesc());
538 FDataSetList
539 &DataList = *document->GetCurrentFrameData();
540 bool
541 First = true;
542 for (uint iDataSet = 0; iDataSet < DataList.size(); ++ iDataSet)
543 {
544 FDataSetPtr
545 pActiveData = DataList[iDataSet];
546 if (!pActiveData->Active)
547 continue;
548 FOrbital
549 *pOrbital = dynamic_cast<FOrbital*>(pActiveData.get());
550 if ( pOrbital != 0 ) {
551 if (First)
552 out << "\n";
553 out << boost::format("app.show_mo(%i,0x%8x,0x%8x);\n") % iDataSet % pOrbital->pVisConfig->cIsoPlus % pOrbital->pVisConfig->cIsoMinus;
554 First = false;
555 }
556 }
557 // char const *pCropEnabledStr = (view3d->HasFlag(VIEWFLAG_CropImages)? "true" : "false");
558 if (document->GetNumFrames() == 1) {
559 out << boost::format("\n// view.save_png(\"%s.png\");\n") % q2s(ReplaceExt(document->GetCurrentInputFileName(),".png")); // % pCropEnabledStr;
560 } else {
561 out << "\n"
562 "for (var iFrame = 0; iFrame < doc.num_frames(); ++iFrame) {\n"
563 " app.set_frame(iFrame);\n"
564 " var frame = app.frame();\n"
565 " // view.save_png(replace_ext(frame.name, \".png\"));\n"
566 "}\n";
567 }
568
569 out << "\n// kate: syntax javascript;\n";
570 return s2q(out.str());
571 }
572
WriteStateScript(QString FileName)573 void FMainWindow::WriteStateScript(QString FileName)
574 {
575 if (FileName != ":/!clipboard!") {
576 if (FileName.isEmpty())
577 FileName = ReplaceExt(document->GetCurrentInputFileName(), ".js");
578 QFile
579 Out(FileName);
580 Out.open(QIODevice::WriteOnly | QIODevice::Text);
581 Out.write(MakeStateScript().toUtf8());
582 IvEmit("* wrote state script to '%1'", FileName);
583 } else {
584 QApplication::clipboard()->setText(MakeStateScript());
585 std::cout << "* copied state script to clipboard." << std::endl;
586 }
587 }
588
dummySignalTest(double o)589 void FMainWindow::dummySignalTest(double o)
590 {
591 IvEmit("!!Received signal: New value is %1", o);
592 }
593
594
LinkPropertyWidgets(QObject * pTarget,QWidget * pWidgetContainer,char const * pPropertyKeyName)595 void LinkPropertyWidgets(QObject *pTarget, QWidget *pWidgetContainer, char const *pPropertyKeyName)
596 {
597 QPropertyDataWidgetMapper
598 *viewMapper = QPropertyModel::newMapper(pTarget, pWidgetContainer);
599 // IvEmit("viewMapper addr: %1",(size_t)viewMapper);
600 // following are some hacks around the data model: QDataWidgetMapper has two "submit changes"
601 // policies: (1) submit when the widget loses focus (they call this "auto"),
602 // (2) submit manually.
603 // The idea that some people might want to submit changes as soon as they happen
604 // (i.e., on "valueChanges") has apparently not crossed the developer's minds.
605 // So what we do is the following:
606 // - change the policy to ManualSubmit
607 // - connect all kinds of onChange events we can think of to the mapper's "manual"
608 // submit mechanism.
609 // - This has of course the unfortunate side effect of updating *ALL* entries on
610 // *every* change of *any* widget...
611 viewMapper->setSubmitPolicy(QPropertyDataWidgetMapper::ManualSubmit);
612 QList<QWidget*> uiWidgets = pWidgetContainer->findChildren<QWidget*>();
613 foreach(QWidget *w, uiWidgets) {
614 QVariant
615 vaViewOptionName = w->property(pPropertyKeyName);
616 if (vaViewOptionName.isValid()) {
617 viewMapper->addMapping(w, vaViewOptionName.toString());
618 QAbstractButton
619 *pButton = qobject_cast<QAbstractButton*>(w);
620 if (pButton) {
621 QObject::connect(pButton, SIGNAL(toggled(bool)), viewMapper, SLOT(submit()));
622 }
623 QDoubleSpinBox
624 *pDoubleSpinBox = qobject_cast<QDoubleSpinBox*>(w);
625 if (pDoubleSpinBox) {
626 // IvEmit("...linking %1 'valueChanged' to 'submit'", pDoubleSpinBox->objectName());
627 QObject::connect(pDoubleSpinBox, SIGNAL(valueChanged(double)), viewMapper, SLOT(submit()));
628 }
629 QSpinBox
630 *pSpinBox = qobject_cast<QSpinBox*>(w);
631 if (pSpinBox)
632 QObject::connect(pSpinBox, SIGNAL(valueChanged(int)), viewMapper, SLOT(submit()));
633
634 QDial
635 *pDial = qobject_cast<QDial*>(w);
636 if (pDial)
637 QObject::connect(pDial, SIGNAL(valueChanged(int)), viewMapper, SLOT(submit()));
638
639 QComboBox
640 *pComboBox = qobject_cast<QComboBox*>(w);
641 if (pComboBox)
642 QObject::connect(pComboBox, SIGNAL(currentIndexChanged(int)), viewMapper, SLOT(submit()));
643 // ^- hm.. the QString variant doesn't work. Not that it matters.
644 }
645 }
646 viewMapper->toFirst();
647 }
648
649
650
651
652
653
654 QMainWindow *g_pMainWindow = 0; // for usage as dialog parent.
655
FMainWindow(QWidget * parent,Qt::WindowFlags flags)656 FMainWindow::FMainWindow(QWidget *parent, Qt::WindowFlags flags)
657 : FBase(parent, flags),
658 ui(new Ui::MainWindow),
659 document(new FDocument(this))
660 {
661 g_pMainWindow = this;
662 iUpdateLocked = 0;
663
664 ui->setupUi(this);
665 ui->tab_Frames->setEnabled(false); // shouldn't have any frames here. will be updated after OnDataChanged().
666 ui->groupBox_ShadingControls->setVisible(false); // that's the secret stuff 8).
667
668 // we have lots of layouts inside other layouts. And, unfortunately,
669 // *each layer* of this adds another set of internal margins. Depending
670 // on the UI style the result looks very stupid. So we hack this up here.
671 // I am not sure how bad this works with high resolution displays (i.e.,
672 // if QT resizes the pixels automagically or not for high DPIs)...must
673 // be tried... unfortunatelly, I don't have one available atm.
674 // Note: These things cannot be controlled via style sheet.
675 // docs say that the settings are inherited from the parent layout or
676 // parent control... but this doesn't really seem to work for me.
677 if (1) {
678 QList<QWidget*> uiWidgets = this->findChildren<QWidget*>();
679 foreach(QWidget *w, uiWidgets) {
680 if (qobject_cast<FStatusBar*>(w) != 0)
681 continue; // handles it's own layout.
682 //QLayout
683 // *pLayout = qobject_cast<QLayout*>(w);
684 QLayout
685 *pLayout = w->layout();
686 if (pLayout) {
687 QGroupBox *pGroupBox = qobject_cast<QGroupBox*>(w);
688 if (pGroupBox) {
689 QMargins margins = pLayout->contentsMargins();
690 margins.setLeft(3);
691 margins.setRight(3);
692 //pLayout->setContentsMargins(3, 8, 3, 6);
693 pLayout->setContentsMargins(margins);
694 } else
695 pLayout->setContentsMargins(3, 3, 3, 3);
696 //qDebug("set spacing of ", pLayout->objectName());
697 }
698 }
699 }
700 ui->tab_Orbitals->layout()->setContentsMargins(0, 0, 0, 0); // left, top, right, bottom
701 // ui->frame_Toolbar->layout()->setContentsMargins(3, 0, 0, 0);
702 // ^- this one leads to a strange OpenGL crash on Johannes' computer.
703 // 3d view keeps on resizing itself and goes into recursive repaints, which blow up the GL stack eventually
704
705 // ui->statusBar->setMaximumHeight(ui->toolButton_Dummy->height());
706
707 // ui->centralwidget->layout()->setContentsMargins(3, 3, 3, 0);
708 ui->centralwidget->layout()->setContentsMargins(3, 3, 3, 3);
709
710 LoadApplicationSettings();
711
712 dataViewFilter = new FShowOnlyCurrentColumnFilter(0, this);
713 dataViewFilter->setSourceModel(document);
714 // dataViewFilter->setDynamicSortFilter(true);
715 // ui->datasetView->setModel(document);
716 ui->datasetView->setModel(dataViewFilter); // show only data rows of currently selected frame.
717
718 //connect(document, SIGNAL(dataChanged(const QModelIndex &,const QModelIndex &)),this, SLOT(onDataChanged()));
719 //connect(document, SIGNAL(modelReset()), this, SLOT(onDataChanged()));
720 connect(document, SIGNAL(ActiveColChanged(int)), this, SLOT(onDataChanged()));
721 // ^- to change UI ranges (e.g., number of frames)
722
723 show();
724 QApplication::processEvents();
725 // ^- try to force building and rendering the widgets... idea is to
726 // make sure that ui->frame3d is correctly set up before view3d
727 // is constructed. Otherwise there were cases where QGLWidget produced
728 // an invalid gl context.
729 // std::cout << boost::format("!frame3d rect: %i %i visible? %s") % ui->frame3d->width() % ui->frame3d->height() % (ui->frame3d->isVisible()? "yes" : "no") << std::endl;
730 // view3d = new FView3d(ui->frame3d, document);
731 view3d = new IView3d(ui->frame3d, document);
732 view3d->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
733 ui->frame3d->layout()->addWidget(view3d);
734 // view3d->adjustSize();
735 // ui->frame3d->addWidget(view3d,0,0);
736 // ui->gridLayout_3dFrame->addWidget(view3d,0,0);
737 // view3d->updateGeometry(); // maybe required on MacOS X?
738
739
740 QIcon
741 // :/ is the resource name prefix. Note that I could also use it for shader
742 // code if I adjust stuff to use QT's file loading functions.
743 AppIcon(":/resources/d-lobe2.png");
744 this->setWindowIcon(AppIcon);
745
746
747 // link up events of data set table
748 if (1) {
749 ui->datasetView->verticalHeader()->hide();
750 ui->datasetView->horizontalHeader()->hide();
751 // ui->datasetView->resizeColumnsToContents();
752
753 connect(ui->datasetView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(onDataRowDoubleClicked(const QModelIndex &)));
754 connect(ui->datasetView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(onDataRowClicked(const QModelIndex &)));
755 // connect(document, SIGNAL(dataChanged(const QModelIndex &,const QModelIndex &)), view3d, SLOT(updateData(const QModelIndex &,const QModelIndex &)));
756 // connect(document, SIGNAL(layoutChanged()), view3d, SLOT(update()));
757 // connect(document, SIGNAL(SelectionChanged()), view3d, SLOT(update()));
758 // ^- done in FView3d now (where it probably should have been put right in the beginning).
759 connect(document, SIGNAL(ActiveColChanged(int)), this, SLOT(onTitleChanged()));
760 connect(dataViewFilter, SIGNAL(layoutChanged()), ui->datasetView, SLOT(resizeColumnsToContents()));
761 // connect(document, SIGNAL(layoutChanged()),
762 // ui->datasetView, SLOT(resizeColumnsToContents()));
763 // ^- doesn't work anymore?
764 connect(document, SIGNAL(layoutChanged()), this, SLOT(onActiveIboCurveChanged()));
765
766 ui->datasetView->setShowGrid(false);
767 QHeaderView *verticalHeader = ui->datasetView->verticalHeader();
768 // verticalHeader->setDefaultSectionSize(verticalHeader->fontMetrics().height()+4);
769 verticalHeader->setDefaultSectionSize(int(verticalHeader->fontMetrics().lineSpacing()*1.5));
770 }
771
772 connect(ui->pushButton_ShowCurveEnergy, SIGNAL(clicked()), this, SLOT(onActiveIboCurveChanged()));
773 connect(ui->pushButton_ShowCurveGradient, SIGNAL(clicked()), this, SLOT(onActiveIboCurveChanged()));
774
775 // connect toolbar/menu actions defined in QtDesigner to local functions.
776 connect(ui->actionOpen, SIGNAL(triggered()), this, SLOT(onOpenFile()));
777 connect(ui->actionCloseFiles, SIGNAL(triggered()), document, SLOT(Clear()));
778
779 connect(ui->actionSaveState, SIGNAL(triggered()), this, SLOT(onSaveState()));
780 connect(ui->actionSaveStateAs, SIGNAL(triggered()), this, SLOT(onSaveStateAs()));
781 connect(ui->actionCopyState, SIGNAL(triggered()), this, SLOT(onCopyState()));
782 connect(ui->actionExecScriptFromClipboard, SIGNAL(triggered()), this, SLOT(onExecStateFromClipboard()));
783 connect(ui->toolButton_ExportCurveData, SIGNAL(clicked()), this, SLOT(onExportCurves()));
784
785 connect(ui->actionSavePicture, SIGNAL(triggered()), this, SLOT(onSavePicture()));
786 connect(ui->actionSavePictureAs, SIGNAL(triggered()), this, SLOT(onSavePictureAs()));
787 connect(ui->actionCopyPicture, SIGNAL(triggered()), this, SLOT(onCopyPicture()));
788 connect(ui->actionQuit, SIGNAL(triggered()), this, SLOT(close()));
789 connect(ui->actionOpenPreferences, SIGNAL(triggered()), this, SLOT(onOpenPreferences()));
790
791 connect(ui->actionFlipSelectedOrbital, SIGNAL(triggered()), this, SLOT(onFlipOrbitalClicked()));
792
793 connect(ui->actionShowTablesAndCurves, SIGNAL(triggered()), this, SLOT(ShowTablesAndCurves()));
794 ui->actionShowTablesAndCurves->setEnabled(false);
795 // ^- FIXME: not really in working condition now... need to clean up and merge first. But
796 // other issues are more important for the beginning.
797
798 connect(ui->toolButton_EditFrames, SIGNAL(clicked()), ui->actionEditFrames, SLOT(trigger()));
799 connect(ui->actionEditFrames, SIGNAL(triggered()), this, SLOT(ShowEditFramesForm()));
800
801 connect(ui->actionAlignView_Xy, SIGNAL(triggered()), this, SLOT(onDiscreeteTrafoTriggered()));
802 connect(ui->actionAlignView_Xz, SIGNAL(triggered()), this, SLOT(onDiscreeteTrafoTriggered()));
803 connect(ui->actionAlignView_Yz, SIGNAL(triggered()), this, SLOT(onDiscreeteTrafoTriggered()));
804 connect(ui->actionRotate_90_CW, SIGNAL(triggered()), this, SLOT(onDiscreeteTrafoTriggered()));
805 connect(ui->actionRotate_90_CCW, SIGNAL(triggered()), this, SLOT(onDiscreeteTrafoTriggered()));
806 connect(ui->actionHideSelectedAtoms, SIGNAL(triggered()), document, SLOT(HideSelectedAtoms()));
807 connect(ui->actionResetAtomModes, SIGNAL(triggered()), document, SLOT(ClearAtomFlags()));
808 connect(ui->actionResetBondLines, SIGNAL(triggered()), document, SLOT(ResetBondLines()));
809 connect(ui->actionLabelElementsOther, SIGNAL(triggered()), ui->checkBox_ElementLabelsOther, SLOT(click()));
810 connect(ui->actionLabelElementsC, SIGNAL(triggered()), ui->checkBox_ElementLabelsCarbon, SLOT(click()));
811 connect(ui->actionLabelAtomNumbers, SIGNAL(triggered()), ui->checkBox_AtomNumbers, SLOT(click()));
812 connect(ui->actionShowShadingControls, SIGNAL(triggered()), this, SLOT(toggleShadingControls()));
813 QShortcut *sh = new QShortcut(QKeySequence("Ctrl+/"), this); // menu shortcuts don't work for hidden objects :(
814 connect(sh, SIGNAL(activated()), this, SLOT(toggleShadingControls()));
815
816 connect(ui->spinBox_OrbitalColorIndex, SIGNAL(valueChanged(int)), document, SLOT(SetNextOrbitalColorIndex(int)));
817 connect(document, SIGNAL(NextOrbitalColorIndexChanged(int)), ui->spinBox_OrbitalColorIndex, SLOT(setValue(int)));
818 connect(ui->comboBox_OrbitalColorScheme, SIGNAL(currentIndexChanged(int)), document, SLOT(SetNextOrbitalColorScheme(int)));
819
820 connect(ui->actionTraceActiveIsoSurfaces, SIGNAL(triggered()), this, SLOT(onTraceIsoSurfacesClicked()));
821 connect(ui->actionTraceAllIsoSurfaces, SIGNAL(triggered()), this, SLOT(onTraceIsoSurfacesClicked()));
822 connect(ui->actionComputeWaveFunction, SIGNAL(triggered()), this, SLOT(onComputeWaveFunctionTriggered()));
823 connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(onAboutClicked()));
824 connect(ui->toolButton_ComputeWf, SIGNAL(clicked()), ui->actionComputeWaveFunction, SLOT(trigger()));
825
826 // connect action buttons lying around on the UI.
827 connect(ui->toolButton_ImportFiles, SIGNAL(clicked()), this, SLOT(onOpenFile()));
828 connect(ui->toolButton_SaveState, SIGNAL(clicked()), this, SLOT(onSaveState()));
829 connect(ui->toolButton_SaveStateAs, SIGNAL(clicked()), this, SLOT(onSaveStateAs()));
830 connect(ui->toolButton_CopyState, SIGNAL(clicked()), this, SLOT(onCopyState()));
831 // connect(ui->toolButton_ExecScriptFromClipboard, SIGNAL(clicked()), this, SLOT(onExecStateFromClipboard()));
832 connect(ui->toolButton_SavePicture, SIGNAL(clicked()), this, SLOT(onSavePicture()));
833 connect(ui->toolButton_SavePictureAs, SIGNAL(clicked()), this, SLOT(onSavePictureAs()));
834 connect(ui->toolButton_CopyPicture, SIGNAL(clicked()), this, SLOT(onCopyPicture()));
835
836 connect(ui->toolButton_AlignViewXy, SIGNAL(clicked()), ui->actionAlignView_Xy, SLOT(trigger()));
837 connect(ui->toolButton_AlignViewXz, SIGNAL(clicked()), ui->actionAlignView_Xz, SLOT(trigger()));
838 connect(ui->toolButton_AlignViewYz, SIGNAL(clicked()), ui->actionAlignView_Yz, SLOT(trigger()));
839 connect(ui->toolButton_RotateCcw, SIGNAL(clicked()), ui->actionRotate_90_CCW, SLOT(trigger()));
840 connect(ui->toolButton_RotateCw, SIGNAL(clicked()), ui->actionRotate_90_CW, SLOT(trigger()));
841 connect(ui->toolButton_FlipOrbital, SIGNAL(clicked()), ui->actionFlipSelectedOrbital, SLOT(trigger()));
842 connect(ui->toolButton_TraceAllIsoSurfaces, SIGNAL(clicked()), ui->actionTraceAllIsoSurfaces, SLOT(trigger()));
843 connect(ui->toolButton_TraceSelectedIsoSurface, SIGNAL(clicked()), ui->actionTraceActiveIsoSurfaces, SLOT(trigger()));
844 connect(ui->toolButton_ReloadShaders, SIGNAL(clicked()), view3d, SLOT(RehashShaders()));
845 connect(ui->toolButton_ReloadShaders, SIGNAL(clicked()), view3d, SLOT(update()));
846 connect(ui->toolButton_SetShaderPath, SIGNAL(clicked()), this, SLOT(setShaderPath()));
847
848
849 // controls for iso surface changes
850 connect(ui->toolButton_RebuildSurface, SIGNAL(clicked()), this, SLOT(onRebuildIsoSurfacesClicked()));
851 // note: other controls are auto-synchronized over property model (everything linked via FView3d
852 // property model is)
853
854 connect(ui->toolButton_FrameLog, SIGNAL(clicked()), this, SLOT(showFrameLog()));
855
856 // link up controls for changing the bond & atom settings
857 connect(ui->pushButton_ToggleTrackedOrbital, SIGNAL(clicked()), this, SLOT(onToggleTrackedOrbitalClicked()));
858
859 // link up controls for changing orbital rendering settings.
860 connect(ui->dialCentralHue, SIGNAL(valueChanged(int)), this, SLOT(onOrbitalColorChanged(int)));
861 connect(ui->spinBox_AlphaCenter, SIGNAL(valueChanged(int)), this, SLOT(onOrbitalColorChanged(int)));
862 connect(ui->spinBox_ValCenter, SIGNAL(valueChanged(int)), this, SLOT(onOrbitalColorChanged(int)));
863 connect(ui->spinBox_SatCenter, SIGNAL(valueChanged(int)), this, SLOT(onOrbitalColorChanged(int)));
864 connect(ui->spinBox_HuePlus, SIGNAL(valueChanged(int)), this, SLOT(onOrbitalColorChanged(int)));
865 connect(ui->spinBox_HueMinus, SIGNAL(valueChanged(int)), this, SLOT(onOrbitalColorChanged(int)));
866 connect(ui->spinBox_AlphaPlus, SIGNAL(valueChanged(int)), this, SLOT(onOrbitalColorChanged(int)));
867 connect(ui->spinBox_AlphaMinus, SIGNAL(valueChanged(int)), this, SLOT(onOrbitalColorChanged(int)));
868 connect(ui->spinBox_SatPlus, SIGNAL(valueChanged(int)), this, SLOT(onOrbitalColorChanged(int)));
869 connect(ui->spinBox_SatMinus, SIGNAL(valueChanged(int)), this, SLOT(onOrbitalColorChanged(int)));
870 connect(ui->spinBox_ValPlus, SIGNAL(valueChanged(int)), this, SLOT(onOrbitalColorChanged(int)));
871 connect(ui->spinBox_ValMinus, SIGNAL(valueChanged(int)), this, SLOT(onOrbitalColorChanged(int)));
872
873 // set up a notification for *this if the active data set is changed.
874 connect(document, SIGNAL(ActiveDatasetChanged()), this, SLOT(onActiveDatasetChanged()));
875 connect(document, SIGNAL(ActiveDatasetChanged()), this, SLOT(onActiveIboCurveChanged()));
876
877 // link up controls for changing the current frame/tracking orbital
878 connect(document, SIGNAL(ActiveColChanged(int)), this, SLOT(onFrameIdChanged(int)));
879
880 connect(ui->spinBox_FrameId, SIGNAL(valueChanged(int)), document, SLOT(SetActiveCol(int)));
881 connect(ui->dial_FrameId, SIGNAL(valueChanged(int)), document, SLOT(SetActiveCol(int)));
882 connect(ui->dial_OrbitalId, SIGNAL(valueChanged(int)), document, SLOT(SetActiveRow(int)));
883
884 ui->checkBox_SkipVirtualOrbitals->setChecked(document->GetSkipVirtualOrbitals());
885 connect(ui->checkBox_SkipVirtualOrbitals, SIGNAL(toggled(bool)), document, SLOT(SetSkipVirtualOrbitals(bool)));
886 connect(document, SIGNAL(SkipVirtualOrbitalsChanged(bool)), ui->checkBox_SkipVirtualOrbitals, SLOT(setChecked(bool)));
887
888
889 ui->menuView->addSeparator();
890 AddPresetScript(ui->menuView, ":/resources/preset_not_very_shiny.js");
891 AddPresetScript(ui->menuView, ":/resources/preset_medium_shiny.js");
892 AddPresetScript(ui->menuView, ":/resources/preset_extra_shiny.js");
893 AddPresetScript(ui->menuView, ":/resources/preset_sooooo_shiny.js");
894
895 ui->tab_OrbitalColor->setEnabled(false);
896 ui->comboBox_IsoType->addItem("density");
897 ui->comboBox_IsoType->addItem("absolute");
898 ui->comboBox_IsoType->setCurrentIndex(0);
899
900 ui->toolBox_Main->adjustSize();
901
902 LinkPropertyWidgets(view3d, this, "view_option_name");
903
904 connect(ui->dialBondScale, SIGNAL(valueChanged(int)), this, SLOT(UpdateAtomAndBondScalesText(void)));
905 connect(ui->dialAtomScale, SIGNAL(valueChanged(int)), this, SLOT(UpdateAtomAndBondScalesText(void)));
906
907 if (g_WorkAroundAlphaCompositing) {
908 // it's a work-around for Mac. See comments on g_WorkAroundAlphaCompositing.
909 view3d->SetSaveAlpha(false);
910 ui->pushButton_SaveAlphaChannel->setEnabled(false);
911 }
912
913 // something in the document changed which just requires a new 3d-rendering, but not
914 // UI control updates.
915 connect(document, SIGNAL(VisualRepresentationChanged()), view3d, SLOT(update()));
916
917 setAcceptDrops(true);
918 onDataChanged();
919 }
920
AddPresetScript(QMenu * pMenu,QString FileName)921 void FMainWindow::AddPresetScript(QMenu *pMenu, QString FileName)
922 {
923 QString
924 ScriptText = LoadTextFromFile(FileName),
925 MenuTitle = FileName;
926 // IvEmit("Read script '%1': Contents:\n---%2\n---", FileName, ScriptText);
927 QStringList
928 Lines = ScriptText.split('\n');
929 if (!Lines.isEmpty() && Lines[0].startsWith("// "))
930 MenuTitle = Lines[0].midRef(3).toString();
931 QAction
932 *pAction = new QAction(MenuTitle, this);
933 connect(pAction, SIGNAL(triggered()), this, SLOT(ExecPresetScript()));
934 pAction->setData(QVariant(ScriptText));
935 pMenu->addAction(pAction);
936 }
937
ExecPresetScript()938 void FMainWindow::ExecPresetScript()
939 {
940 QAction
941 *pAction = qobject_cast<QAction*>(sender());
942 if (pAction) {
943 QString ScriptText = pAction->data().toString();
944 if (ScriptText == "")
945 QMessageBox::warning(this, "Script Execution Failed", "Attempted to execute preset script via menu action, but menu action contained no data.");
946 else
947 ExecScript(ThisAsIApp(), this->view3d, ScriptText, pAction->text());
948 }
949 }
950
showFrameLog()951 void FMainWindow::showFrameLog()
952 {
953 FFrame
954 *pFrame = document->GetCurrentFrame();
955 if (pFrame) {
956 FShowTextForm
957 ShowTextForm(
958 IvFmt("<html><pre>%1</pre></html>", pFrame->Log().GetText()),
959 IvFmt("Frame Log for '%1'",RemovePath(pFrame->GetFullInputFileName())));
960 ShowTextForm.exec();
961 }
962 }
963
onAboutClicked()964 void FMainWindow::onAboutClicked()
965 {
966 FAboutDialog
967 about(this);
968 about.exec();
969 }
970
onOpenPreferences()971 void FMainWindow::onOpenPreferences()
972 {
973 FPreferencesForm
974 PreferencesForm(document, this);
975 PreferencesForm.exec();
976 }
977
~FMainWindow()978 FMainWindow::~FMainWindow()
979 {
980 g_pMainWindow = 0;
981 delete document; // I guess this should go down before the 3d view... GL explodes if one destroys objects after the context is gone.
982 delete view3d;
983 delete dataViewFilter;
984 delete ui;
985 }
986
987
988
filterAcceptsColumn(int sourceColumn,const QModelIndex &) const989 bool FShowOnlyCurrentColumnFilter::filterAcceptsColumn(int sourceColumn, const QModelIndex &/*sourceParent*/) const
990 {
991 // std::cout << boost::format("invoked filter model for row %i.") % sourceColumn << std::endl;
992 return sourceColumn == m_ShownColumn;
993 }
994
FShowOnlyCurrentColumnFilter(int ShownColumn,QObject * parent)995 FShowOnlyCurrentColumnFilter::FShowOnlyCurrentColumnFilter(int ShownColumn, QObject *parent)
996 : FBase(parent)
997 {
998 m_ShownColumn = ShownColumn;
999 }
1000
setShownColumn(int NewColumn)1001 void FShowOnlyCurrentColumnFilter::setShownColumn(int NewColumn)
1002 {
1003 if (NewColumn != m_ShownColumn) {
1004 m_ShownColumn = NewColumn;
1005 invalidateFilter();
1006 }
1007 }
1008
1009
1010 float SvBoxScale = 100.f;
1011
clamp(float f,float min,float max)1012 float clamp(float f, float min, float max) {
1013 if (f < min) return min;
1014 if (f > max) return max;
1015 return f;
1016 }
1017
AlphaFromCol(uint32_t c)1018 float AlphaFromCol(uint32_t c) {
1019 return ((c >> 24) & 0xff)/255.f;
1020 }
1021
ColFromAlpha(float a)1022 uint32_t ColFromAlpha(float a) {
1023 int ia = clamp(::roundf(255 * a), 0., 255.);
1024 return ia << 24;
1025 }
1026
onActiveDatasetChanged()1027 void FMainWindow::onActiveDatasetChanged()
1028 {
1029 // std::cout << boost::format("ActiveDatasetChanged: current row is %i.") % document->GetActiveRowIndex() << std::endl;
1030 // we'll be updating the control values. this will emit valueChanged signals,
1031 // which will trigger orbital updates before we are finished. stop that.
1032 iUpdateLocked += 1;
1033
1034 FDataSetPtr
1035 pDataSet = document->GetActiveDataSet();
1036 FOrbital
1037 *pOrb = dynamic_cast<FOrbital*>(pDataSet.get());
1038
1039 if (document->GetCurrentFrame()) {
1040 int nOrbs = int(document->GetCurrentFrame()->m_Data.size() - 1);
1041 ui->dial_OrbitalId->setEnabled(nOrbs > 0);
1042 if (nOrbs != 0) { // <- without this we may get an infinite loop between the slider's valueChanged and the document's ActiveColChanged...
1043 ui->dial_OrbitalId->setMinimum(1);
1044 ui->dial_OrbitalId->setMaximum(std::max(nOrbs, 1));
1045 if (pOrb)
1046 ui->dial_OrbitalId->setValue(document->GetActiveRowIndex());
1047 }
1048 }
1049
1050 ui->tab_OrbitalColor->setEnabled(pOrb && pOrb->Active);
1051 if (pOrb && pOrb->Active) {
1052 // update the color values.
1053 // IvEmit("!Set Active Orbital: %1", pOrb->GetDesc().toStdString());
1054 FOrbitalVisualConfig
1055 *pVis = pOrb->pVisConfig.get();
1056 if (!pVis->bColorSet)
1057 view3d->updateGL(); // <- to force rendering the orbital such that we have a color set in the curve view...
1058 FColor
1059 cPlus(pVis->cIsoPlus),
1060 cMinus(pVis->cIsoMinus),
1061 cAvg;
1062 cAvg = .5f * cPlus + .5f * cMinus;
1063 float
1064 hp,sp,vp,ap,
1065 hm,sm,vm,am,
1066 hc,sc,vc,ac;
1067 ac = 1.0;
1068 cPlus.ToHsv(hp,sp,vp);
1069 cMinus.ToHsv(hm,sm,vm);
1070 cAvg.ToHsv(hc,sc,vc);
1071 ap = AlphaFromCol(pVis->cIsoPlus);
1072 am = AlphaFromCol(pVis->cIsoMinus);
1073 ac = .5f*(ap + am);
1074 if (::fabs(hp-hm) > 180.) {
1075 if (hp > hm)
1076 hp -= 360.;
1077 else
1078 hm -= 360.;
1079 }
1080
1081 hc = .5f * (hp + hm); // that is not right, is it?
1082 sc = .5f * (sp + sm);
1083 vc = .5f * (vp + vm);
1084
1085 // std::cout << boost::format("hc = %6.2f sc = %6.2f vc = %6.2f ac = %6.2f") % hc % (SvBoxScale*sc) % (SvBoxScale*vc) % (SvBoxScale*ac) << std::endl;
1086 ui->dialCentralHue->setValue((int)hc);
1087 ui->label_Hue->setText(QString("Hue (%1)").arg((int)hc));
1088 ui->spinBox_SatCenter->setValue(SvBoxScale * sc);
1089 ui->spinBox_ValCenter->setValue(SvBoxScale * vc);
1090 ui->spinBox_AlphaCenter->setValue(SvBoxScale * ac); // not actually changed atm.
1091
1092 ui->spinBox_HuePlus->setValue(hp - hc);
1093 ui->spinBox_SatPlus->setValue(SvBoxScale * (sp - sc));
1094 ui->spinBox_ValPlus->setValue(SvBoxScale * (vp - vc));
1095 ui->spinBox_HueMinus->setValue(hm - hc);
1096 ui->spinBox_SatMinus->setValue(SvBoxScale * (sm - sc));
1097 ui->spinBox_ValMinus->setValue(SvBoxScale * (vm - vc));
1098 }
1099 iUpdateLocked -= 1;
1100 }
1101
onOrbitalColorChanged(int)1102 void FMainWindow::onOrbitalColorChanged(int /*iNewValue*/)
1103 {
1104 // ^- can't use iNewValue... we don't actually know where it is coming from.
1105 if (iUpdateLocked != 0)
1106 // currently in the process of updating controls; that
1107 // is why the values are changing. don't do anything until
1108 // this is finished.
1109 return;
1110 FDataSetPtr
1111 pDataSet = document->GetActiveDataSet();
1112 if (pDataSet.get() && pDataSet->Active) {
1113 FOrbital
1114 *pOrb = dynamic_cast<FOrbital*>(pDataSet.get());
1115 if (pOrb) {
1116 // assemble new colors from data in controls.
1117 float
1118 hc = ui->dialCentralHue->value(),
1119 vc = ui->spinBox_ValCenter->value()/SvBoxScale,
1120 sc = ui->spinBox_SatCenter->value()/SvBoxScale,
1121 ac = ui->spinBox_AlphaCenter->value()/SvBoxScale;
1122 float
1123 hp = hc + ui->spinBox_HuePlus->value(),
1124 sp = clamp(sc + ui->spinBox_SatPlus->value()/SvBoxScale, 0.f, 1.f),
1125 vp = clamp(vc + ui->spinBox_ValPlus->value()/SvBoxScale, 0.f, 1.f),
1126 ap = clamp(ac + ui->spinBox_AlphaPlus->value()/SvBoxScale, 0.f, 1.f),
1127 hm = hc + ui->spinBox_HueMinus->value(),
1128 sm = clamp(sc + ui->spinBox_SatMinus->value()/SvBoxScale, 0.f, 1.f),
1129 vm = clamp(vc + ui->spinBox_ValMinus->value()/SvBoxScale, 0.f, 1.f),
1130 am = clamp(ac + ui->spinBox_AlphaMinus->value()/SvBoxScale, 0.f, 1.f);
1131
1132 ui->label_Hue->setText(QString("Hue (%1)").arg((int)hc));
1133
1134 // uint32_t
1135 // cPlus = ct::Hsv(hp,sp,vp).uint32() | 0xff000000,
1136 // cMinus = ct::Hsv(hm,sm,vm).uint32() | 0xff000000;
1137 uint32_t
1138 cPlus = ct::Hsv(hp,sp,vp).uint32() | ColFromAlpha(ap),
1139 cMinus = ct::Hsv(hm,sm,vm).uint32() | ColFromAlpha(am);
1140 FOrbitalVisualConfig
1141 *pVis = pOrb->pVisConfig.get();
1142 pVis->cIsoMinus = cMinus;
1143 pVis->cIsoPlus = cPlus;
1144 // update the color also in the mesh, if one already exists.
1145 pVis->UpdateLinkedRepresentations(FOrbitalVisualConfig::UPDATE_InvalidateColors, this->view3d);
1146 view3d->update();
1147 }
1148 }
1149 }
1150
1151
toggleShadingControls()1152 void FMainWindow::toggleShadingControls()
1153 {
1154 ui->groupBox_ShadingControls->setVisible(!ui->groupBox_ShadingControls->isVisible());
1155 // ui->groupBox_ShadingControls->setVisible(ui->actionShowShadingControls->isChecked());
1156 }
1157
setShaderPath()1158 void FMainWindow::setShaderPath()
1159 {
1160 QString
1161 NewPath = QFileDialog::getExistingDirectory(this, "Open Path");
1162 if (NewPath != "") {
1163 view3d->SetShaderPath(NewPath);
1164 view3d->RehashShaders();
1165 view3d->update();
1166 }
1167 }
1168
1169
onRebuildIsoSurfacesClicked()1170 void FMainWindow::onRebuildIsoSurfacesClicked()
1171 {
1172 for (int iFrame = 0; iFrame < document->GetNumFrames(); ++ iFrame){
1173 FDataSetList
1174 *pData = document->GetFrameData(iFrame);
1175 if (pData) {
1176 for (int iRow = 0; size_t(iRow) < pData->size(); ++ iRow) {
1177 (*pData)[iRow]->InvalidateRenderCache();
1178 }
1179 }
1180 }
1181 view3d->update();
1182 }
1183
onTraceIsoSurfacesClicked()1184 void FMainWindow::onTraceIsoSurfacesClicked()
1185 {
1186 int
1187 nTotalSets = 0;
1188 bool
1189 TraceAll = false;
1190 if (sender() == ui->actionTraceAllIsoSurfaces)
1191 TraceAll = true;
1192
1193 if (!TraceAll) {
1194 // count number of active data sets, to get an estimation of the required time.
1195 int
1196 nActiveSets = 0; // note: one of them is the geometry...
1197 for (int iRow = 0; size_t(iRow) < document->GetCurrentFrameData()->size(); ++ iRow)
1198 if ((*document->GetCurrentFrameData())[iRow]->Active)
1199 nActiveSets += 1;
1200 nTotalSets = nActiveSets * document->GetNumFrames();
1201 } else {
1202 nTotalSets = document->GetCurrentFrameData()->size() * document->GetNumFrames();
1203 }
1204
1205 QProgressDialog progress("Tracing iso-surfaces...", "Cancel", 0, nTotalSets, this);
1206 progress.setWindowModality(Qt::WindowModal);
1207 progress.setValue(0);
1208
1209 int
1210 nDoneSets = 0;
1211 bool
1212 Cancelled = false;
1213 for (int iFrame = 0; iFrame < document->GetNumFrames(); ++ iFrame) {
1214 FDataSetList
1215 *pData = document->GetFrameData(iFrame);
1216 if (pData) {
1217 for (int iRow = 0; size_t(iRow) < pData->size(); ++ iRow) {
1218 if ((*pData)[iRow]->Active || TraceAll) {
1219 for (int iDummy = 0; iDummy < 100; ++ iDummy)
1220 QApplication::processEvents();
1221 // ^- this shouldn't be here, but otherwise the "Cancel" button does not work...
1222 // update... well, it also doesn't work *with* it behing here..
1223 Cancelled = Cancelled || bool(progress.wasCanceled());
1224 if (Cancelled)
1225 break;
1226 (*pData)[iRow]->BuildRenderCache(view3d);
1227 nDoneSets += 1;
1228 progress.setValue(nDoneSets);
1229 IvEmit("* progess: %1 of %2 iso-surfaces done.", nDoneSets, nTotalSets);
1230 }
1231 }
1232 }
1233 }
1234 if (Cancelled)
1235 std::cout << " Iso Tracing cancelled." << std::endl;
1236 progress.setValue(nTotalSets);
1237 view3d->update();
1238 }
1239
onFlipOrbitalClicked()1240 void FMainWindow::onFlipOrbitalClicked()
1241 {
1242 if (iUpdateLocked != 0)
1243 // currently in the process of updating controls; that
1244 // is why the values are changing. don't do anything until
1245 // this is finished.
1246 return;
1247 FDataSetPtr
1248 pDataSet = document->GetActiveDataSet();
1249 if (pDataSet.get() && pDataSet->Active) {
1250 FOrbital
1251 *pOrb = dynamic_cast<FOrbital*>(pDataSet.get());
1252 if (pOrb) {
1253 // pOrb->FlipPhase();
1254 FOrbitalVisualConfig
1255 *pVis = pOrb->pVisConfig.get();
1256 std::swap(pVis->cIsoMinus, pVis->cIsoPlus);
1257 pVis->UpdateLinkedRepresentations(FOrbitalVisualConfig::UPDATE_InvalidateColors, this->view3d);
1258
1259 onActiveDatasetChanged();
1260 // ^- to update the values in the color controls.
1261 view3d->update();
1262 }
1263 }
1264 }
1265
onTitleChanged()1266 void FMainWindow::onTitleChanged()
1267 {
1268 QString
1269 FileName = RemovePath(document->GetCurrentInputFileName());
1270 if (!FileName.isEmpty())
1271 setWindowTitle(FileName + QString(" -- IboView "));
1272 else
1273 setWindowTitle("IboView");
1274 }
1275
1276
onFrameIdChanged(int iNewFrame)1277 void FMainWindow::onFrameIdChanged(int iNewFrame)
1278 {
1279 // document->SetActiveCol(iNewFrame);
1280 dataViewFilter->setShownColumn(iNewFrame);
1281 ui->datasetView->resizeColumnsToContents();
1282 if (document->GetCurrentFrame() != 0) {
1283 QString const
1284 &FileName = document->GetCurrentFrame()->GetBaseInputFileName(),
1285 LabelText = IvFmt("#%1: %2", iNewFrame, FileName);
1286 ui->label_FrameFileName->setText(LabelText);
1287 }
1288 }
1289
onDataRowClicked(QModelIndex const & Index)1290 void FMainWindow::onDataRowClicked(QModelIndex const &Index)
1291 {
1292 document->SetActiveRow(Index.row());
1293 // ^- note that these are indices from 'dataViewFilter', not from 'document' itself.
1294 }
1295
onDataRowDoubleClicked(QModelIndex const & Index)1296 void FMainWindow::onDataRowDoubleClicked(QModelIndex const &Index)
1297 {
1298 document->ToggleDataRow(Index.row());
1299 }
1300
onDataChanged()1301 void FMainWindow::onDataChanged()
1302 {
1303 int
1304 iFrame = document->GetActiveColIndex(),
1305 nFrames = document->GetNumFrames();
1306 //if (iFrame == -1)
1307 // iFrame = 0;
1308 //if (nFrames == 0)
1309 // nFrames = 1;
1310 // ^- FIXME: is that safe? can't this make an infinite loop?
1311 if (nFrames > 0) {
1312 ui->spinBox_FrameId->setMinimum(0);
1313 ui->dial_FrameId->setMinimum(0);
1314
1315 ui->spinBox_FrameId->setMaximum(nFrames - 1);
1316 ui->dial_FrameId->setMaximum(nFrames - 1);
1317
1318 if (iFrame != -1) {
1319 ui->spinBox_FrameId->setValue(iFrame);
1320 ui->dial_FrameId->setValue(iFrame);
1321 }
1322 }
1323
1324 ui->tab_Frames->setEnabled(nFrames >= 2);
1325 ui->spinBox_FrameId->setEnabled(nFrames >= 2);
1326
1327 // todo: update file names etc.
1328 }
1329
1330
onSaveState()1331 void FMainWindow::onSaveState(/*const QAction &action*/)
1332 {
1333 // std::cout << "onSaveState triggered!!" << std::endl;
1334 WriteStateScript();
1335 }
1336
onCopyState()1337 void FMainWindow::onCopyState(/*const QAction &action*/)
1338 {
1339 WriteStateScript(":/!clipboard!");
1340 }
1341
1342 // given a text, check if it kinda looks like an xyz file (admittedly, a better
1343 // method might be to simply try to load it as an .xyz file, but this might get
1344 // more messy).
DoesThisLookLikeAnXyzFile(QString Text)1345 bool DoesThisLookLikeAnXyzFile(QString Text)
1346 {
1347 // IvEmit("Got into 'DoesThisLookLikeAnXyzFile(..).'");
1348 // check if there are at least three lines (#Atoms, Comment, First Element-X-Y-Z-line)
1349 QStringList
1350 Lines = Text.split("\n");
1351 if (Lines.size() < 3)
1352 return false;
1353 // IvEmit("...line number check passed.'");
1354
1355 // Does the first line consists of a single integer?
1356 QString Line0 = Lines[0];
1357 bool
1358 ok[4] = {false,false,false,false};
1359 int
1360 nAtoms = Line0.toInt(&ok[0],10);
1361 // ^- 10: conversion base. sorry, no octal number of atoms for you 8).
1362 // (might actually lead to problems for people who prepend their ints with 0s)
1363 IR_SUPPRESS_UNUSED_WARNING(nAtoms);
1364
1365 // if (!ok[0] || Lines.size() < 2 + nAtoms)
1366 // ^- actual load attempt would give more meaningfull error messages than checking consistency here.
1367 if (!ok[0])
1368 return false;
1369 // IvEmit("...first-line-integer check passed.'");
1370
1371 // now see if there are three doubles in line 2.
1372 QString
1373 Line2 = Lines[2];
1374 QStringList
1375 ls = Line2.trimmed().split(QRegExp("\\s+")); // <- split at whitespace
1376 if (ls.size() >= 4) { // element x y z
1377 // IvEmit("...3rd line count check check passed.'");
1378 // IvEmit("...ls[1] = '%1'", ls[1]);
1379 // IvEmit("...ls[2] = '%1'", ls[2]);
1380 // IvEmit("...ls[3] = '%1'", ls[3]);
1381 ls[1].toDouble(&ok[1]);
1382 ls[2].toDouble(&ok[2]);
1383 ls[3].toDouble(&ok[3]);
1384 }
1385 // IvEmit("...stabs: %1 %2 %3 %4'", int(ok[0]), int(ok[1]), int(ok[2]), int(ok[3]));
1386 return ok[0] && ok[1] && ok[2] && ok[3];
1387 }
1388
1389
1390
onExecStateFromClipboard()1391 void FMainWindow::onExecStateFromClipboard()
1392 {
1393 QClipboard
1394 *clipboard = QApplication::clipboard();
1395 QString
1396 ScriptText = clipboard->text();
1397 if (ScriptText == "")
1398 QMessageBox::warning(this, "Script Execution Failed", "Attempted to execute script from clipboard. but clipboard does not contain text.");
1399 else {
1400 if (DoesThisLookLikeAnXyzFile(ScriptText)) {
1401 // try to load it as an .xyz file. Unload other stuff first.
1402 document->Clear();
1403 ThisAsIApp()->load_file(":/!clipboard!");
1404 } else
1405 // run it as a script.
1406 ExecScript(ThisAsIApp(), this->view3d, ScriptText, "clipboard");
1407 }
1408 }
1409
onSavePicture()1410 void FMainWindow::onSavePicture(/*const QAction &action*/)
1411 {
1412 // view3d->HasFlag(VIEWFLAG_CropImages)
1413 view3d->save_png(ReplaceExt(document->GetCurrentInputFileName(), ".png"));
1414 }
1415
onCopyPicture()1416 void FMainWindow::onCopyPicture(/*const QAction &action*/)
1417 {
1418 view3d->save_png(":/!clipboard!");
1419 }
1420
1421
1422 // FSaveFileDirChangeProxy::FSaveFileDirChangeProxy(QFileDialog *pDialog)
1423 // : m_pDialog(pDialog)
1424 // {
1425 // connect(pDialog, SIGNAL(fileSelected(QString const&)), this, SLOT(selectFile(QString const&)));
1426 // }
1427 //
1428 // FSaveFileDirChangeProxy::~FSaveFileDirChangeProxy()
1429 // {}
1430 //
1431 // void FSaveFileDirChangeProxy::selectFile(QString const &File)
1432 // {
1433 // std::cout << "PROXY: Selected '" << q2s(File) << std::endl;
1434 // }
1435
1436
onSavePictureAs()1437 void FMainWindow::onSavePictureAs(/*const QAction &action*/)
1438 {
1439 QString
1440 FileNameSuggest = ReplaceExt(document->GetCurrentInputFileName(), ".png"),
1441 FileName;
1442 FileName = IvGetSaveFileName("History/SaveFiles", this, "Save Picture",
1443 (FileNameSuggest), "Bitmap files (*.png *.jpg);;All Files (*.*)");
1444 // FileName = QFileDialog::getSaveFileName(this, "Save Picture",
1445 // ("./"+FileNameSuggest), "Bitmap files (*.png *.jpg);;All Files (*.*)");
1446
1447 if (FileName != "")
1448 view3d->save_png(FileName);
1449 }
1450
onSaveStateAs()1451 void FMainWindow::onSaveStateAs(/*const QAction &action*/)
1452 {
1453 QString
1454 RefFileName = document->GetCommonInputFileName(),
1455 FileNameSuggest = ReplaceExt(RefFileName, ".js"),
1456 FileName;
1457
1458 // FileName = QFileDialog::getSaveFileName(this, "Save State Script",
1459 // ("./"+FileNameSuggest), "Script files (*.js *.chai);;All Files (*.*)");
1460 FileName = IvGetSaveFileName("History/SaveFiles", this, "Save State Script",
1461 FileNameSuggest, "Script files (*.js *.chai);;All Files (*.*)");
1462
1463
1464 if (!FileName.isEmpty())
1465 WriteStateScript(FileName);
1466 }
1467
onOpenFile()1468 void FMainWindow::onOpenFile()
1469 {
1470 // QStringList FileNames = QFileDialog::getOpenFileNames(this, "Open File",
1471 // "", "Molpro XMLs, Xyz Files, Molden files, IboView Scripts (*.xml *.xyz *.molden *.js *.chai);;Xyz Files (*.xyz);;All Files (*.*)");
1472 QStringList FileNames = IvGetOpenFileNames("History/OpenFiles", this, "Open File",
1473 "", "Molpro XMLs, Xyz Files, Molden files, IboView Scripts (*.xml *.xyz *.molden *.molden.input *.js *.chai);;Xyz Files (*.xyz);;All Files (*.*)");
1474
1475 load_files_(FileNames);
1476 }
1477
dragEnterEvent(QDragEnterEvent * event)1478 void FMainWindow::dragEnterEvent(QDragEnterEvent *event)
1479 {
1480 // FBase::dragEnterEvent(event);
1481 // if (event->mimeData()->hasFormat("text/plain"))
1482 // maybe I should check if these are the correct file types?
1483 if (event->mimeData()->hasUrls())
1484 event->acceptProposedAction();
1485 }
1486
dropEvent(QDropEvent * event)1487 void FMainWindow::dropEvent(QDropEvent *event)
1488 {
1489 const QMimeData *mimeData = event->mimeData();
1490
1491 // is it a list of files?
1492 if (mimeData->hasUrls()) {
1493 QStringList
1494 pathList;
1495 QList<QUrl>
1496 urlList = mimeData->urls();
1497
1498 // extract the local paths of the files
1499 for (int i = 0; i < urlList.size(); ++i) {
1500 pathList.append(urlList.at(i).toLocalFile());
1501 }
1502
1503 // call a function to open the files
1504 // openFiles(pathList);
1505 // for (int i = 0; i < pathList.size(); ++i)
1506 // IvEmit("!! OPEN: '%1'", pathList[i]);
1507 close_files_();
1508 load_files_(pathList);
1509 }
1510 // FBase::dropEvent(event);
1511 }
1512
1513
closeEvent(QCloseEvent * event)1514 void FMainWindow::closeEvent(QCloseEvent *event)
1515 {
1516 StoreApplicationSettings();
1517 return QMainWindow::closeEvent(event);
1518 }
1519
1520
StoreApplicationSettings()1521 void FMainWindow::StoreApplicationSettings()
1522 {
1523 IvSaveWindowSize("MainWindow/Size", this);
1524 IvSaveSplitterState("MainWindow/SplitterH", ui->splitter_ViewVsTools);
1525 }
1526
LoadApplicationSettings()1527 void FMainWindow::LoadApplicationSettings()
1528 {
1529 IvRestoreWindowSize("MainWindow/Size", this);
1530 IvRestoreSplitterState("MainWindow/SplitterH", ui->splitter_ViewVsTools);
1531 }
1532
1533
ShowTablesAndCurves()1534 void FMainWindow::ShowTablesAndCurves()
1535 {
1536 // check if we already have one of those dialogs.
1537 FTablesForm*
1538 tablesForm = findChild<FTablesForm*>("TablesForm");
1539 if (!tablesForm) {
1540 // seems not. Make a new one.
1541 tablesForm = new FTablesForm(document, this);
1542 }
1543 tablesForm->show();
1544 }
1545
ShowEditFramesForm()1546 void FMainWindow::ShowEditFramesForm()
1547 {
1548 FEditFramesForm
1549 *framesForm = findChild<FEditFramesForm*>("EditFramesForm");
1550 if (!framesForm)
1551 framesForm = new FEditFramesForm(document, this);
1552 framesForm->exec();
1553 framesForm->DoPostProcessingOnClose();
1554 // framesForm->done(0);
1555 // // should I call done() here to invoke the close command? Otherwise it's just hidden, I guess..
1556 framesForm->close();
1557 }
1558
1559
onDiscreeteTrafoTriggered()1560 void FMainWindow::onDiscreeteTrafoTriggered()
1561 {
1562 QAction
1563 *pAction = qobject_cast<QAction*>(sender());
1564 // note: could also use pAction->setData() to discern what is what.
1565 if (pAction == ui->actionAlignView_Xy) {
1566 view3d->modify("align xy", 0.);
1567 } else if (pAction == ui->actionAlignView_Xz) {
1568 view3d->modify("align xz", 0.);
1569 } else if (pAction == ui->actionAlignView_Yz) {
1570 view3d->modify("align yz", 0.);
1571 } else if (pAction == ui->actionRotate_90_CW) {
1572 view3d->modify("roll", +90.);
1573 } else if (pAction == ui->actionRotate_90_CCW) {
1574 view3d->modify("roll", -90.);
1575 } else {
1576 IvNotify(NOTIFY_Warning, IvFmt("WARNTING: Action '%1' not recognized in dtraf. Ignored!", pAction->objectName()));
1577 }
1578 }
1579
1580
1581
1582 // FIXME: this stuff should all be merged into IvTables.
onExportCurves()1583 void FMainWindow::onExportCurves()
1584 {
1585 QString
1586 RefFileName = document->GetCommonInputFileName(),
1587 FileNameSuggest = ReplaceExt(RefFileName, ".csv"),
1588 FileName;
1589 // FileName = QFileDialog::getSaveFileName(this, "Export Curve Data",
1590 // ("./"+FileNameSuggest), "Comma Separated Values (*.csv);;All Files (*.*)");
1591 FileName = IvGetSaveFileName("History/SaveCurves", this, "Export Curve Data",
1592 FileNameSuggest, "Comma Separated Values (*.csv);;All Files (*.*)");
1593 if (FileName.isEmpty())
1594 return;
1595 TArray<float>
1596 ArcLengths;
1597 uint
1598 ArcLengthFlags = 0; // do something about mass weighting?
1599 MakeIrcArcLengths(ArcLengths, document, ArcLengthFlags);
1600
1601 uint
1602 nFrames = document->GetNumFrames();
1603 QList<TArray<float> >
1604 CurveData;
1605 QString
1606 CaptionLine;
1607 if ((ArcLengthFlags & ARCLENGTH_NoMassWeighting) != 0)
1608 CaptionLine = "\"Arc Length (bohr)\"";
1609 else
1610 CaptionLine = "\"Reaction Coordinate (bohr$\\cdot$amu$^{1/2}$)\"";
1611
1612 for (int iCurve = 0; iCurve < ui->curveView->getNumCurves(); ++ iCurve) {
1613 TArray<float>
1614 Data;
1615 QString Title;
1616 QColor Color;
1617 uint32_t Flags;
1618 ui->curveView->getCurve(Data, Title, Flags, Color, iCurve);
1619 if (Title == "" || Title == "(current)")
1620 continue;
1621 CurveData.append(Data);
1622 uint32_t dwColor = ((Color.red() << 16) + (Color.green() << 8) + Color.blue());
1623 QString Caption = QString::fromStdString(boost::str(boost::format("%s [#%06x]") % Title.toStdString() % dwColor));
1624 if (!CaptionLine.isEmpty())
1625 CaptionLine += ",";
1626 CaptionLine.append(IvFmt("\"%1\"", Caption));
1627 }
1628 QFile
1629 OutFile(FileName);
1630 if (OutFile.open(QFile::WriteOnly | QFile::Truncate)) {
1631 QTextStream Out(&OutFile);
1632
1633 Out << CaptionLine << "\n";
1634 Out.setRealNumberNotation(QTextStream::FixedNotation);
1635 Out.setRealNumberPrecision(6);
1636 for (uint iFrame = 0; iFrame < nFrames; ++ iFrame) {
1637 Out << ArcLengths[iFrame];
1638 for (int iCurve = 0; iCurve < CurveData.size(); ++ iCurve) {
1639 // if (iCurve != 0)
1640 // Out << ",";
1641 Out << ",";
1642 float f = CurveData.at(iCurve)[iFrame];
1643 Out << f;
1644 }
1645 Out << "\n";
1646 }
1647 IvEmit("* wrote curve data to '%1'", FileName);
1648 } else {
1649 IvNotify(NOTIFY_Error, IvFmt("Failed to open file '%1' for writing.", FileName));
1650 }
1651 }
1652
1653
1654 template<class FFloat>
PrintChargeArray(std::ostream & xout,std::string const & Desc,TArray<FFloat> & CurveData)1655 static void PrintChargeArray(std::ostream &xout, std::string const &Desc, TArray<FFloat> &CurveData)
1656 {
1657 xout << boost::format("!chgs %s npts = %i [") % Desc % CurveData.size();
1658 for (uint i = 0; i < CurveData.size(); ++ i) {
1659 if (i != 0)
1660 xout << ", ";
1661 xout << boost::format("%5.2f") % CurveData[i];
1662 }
1663 xout << "]\n";
1664 }
1665
NormalizeCurve(TArray<float> & CurveData,float fMin,float fMax,bool LowerTo0=false)1666 static void NormalizeCurve(TArray<float> &CurveData, float fMin, float fMax, bool LowerTo0=false)
1667 {
1668 if (CurveData.empty())
1669 return;
1670 float
1671 fDataMin = CurveData[0],
1672 fDataMax = CurveData[0];
1673 for (size_t i = 1; i < CurveData.size(); ++ i) {
1674 fDataMax = std::max(float(fDataMax), float(CurveData[i]));
1675 fDataMin = std::min(float(fDataMin), float(CurveData[i]));
1676 }
1677 if (LowerTo0)
1678 fDataMin = 0.;
1679 if (fDataMax - fDataMin <= 0) {
1680 for (size_t i = 0; i < CurveData.size(); ++ i)
1681 CurveData[i] = (fMax + fMin) / 2;
1682 } else {
1683 for (size_t i = 0; i < CurveData.size(); ++ i)
1684 CurveData[i] = ((CurveData[i] - fDataMin)/(fDataMax - fDataMin)) * (fMax - fMin) + fMin;
1685 }
1686 }
1687
1688
1689 // FIXME: this stuff should all be merged into IvTables.
onActiveIboCurveChanged()1690 void FMainWindow::onActiveIboCurveChanged()
1691 {
1692 if (document->GetActiveRowIndex() == 0)
1693 return;
1694 // std::cout << "onActiveIboCurveChanged [iUpd = " << iUpdateLocked << "]" << std::endl;
1695 if (iUpdateLocked != 0)
1696 return;
1697 if (!document->GetFrameData(0) || !document->GetCurrentFrameData())
1698 return;
1699 iUpdateLocked += 1;
1700 FDataSetList
1701 &DataList = *document->GetFrameData(0);
1702 // ui->dial_OrbitalId->setMinimum(1);
1703 // ui->dial_OrbitalId->setMaximum(int(DataList.size())-1);
1704
1705 // uint32_t
1706 // dwEnabledColor = 0xffa0a0a0, // color of data sets which are enabled but not current (or use active color?)
1707 // dwActiveColor = 0xffffffff; // color of current data set
1708 TArray<float>
1709 CurveData;
1710 uint
1711 nCurves = 0,
1712 iFrame = document->GetActiveColIndex();
1713 ui->curveView->setBackgroundBrush(QBrush(QColor(0xff404040)));
1714 ui->curveView->clearCurves();
1715 // Qt::DotLine
1716 QPen AxisPen = QPen(QBrush(QColor(0xff171717)), 3.41, Qt::SolidLine);
1717 ui->curveView->addHline(float(0.f), AxisPen, HVCURVE_EndArrow);
1718 ui->curveView->addVline(float(0.f), AxisPen, HVCURVE_EndArrow);
1719 uint32_t
1720 dwFrameIndicatorColor = 0xff70e070;
1721 // dwFrameIndicatorColor = 0xffff6060;
1722 ui->curveView->addVline(float(iFrame), QPen(QBrush(QColor(dwFrameIndicatorColor)), 2.41, Qt::DotLine));
1723 if (document->HaveEnergies() && ui->pushButton_ShowCurveEnergy->isChecked()) {
1724 CurveData.resize(document->GetNumFrames());
1725 for (int iFrame = 0; iFrame < document->GetNumFrames(); ++ iFrame)
1726 CurveData[iFrame] = float(document->GetFrame(iFrame)->GetEnergy());
1727 NormalizeCurve(CurveData, 0., 1.);
1728 uint iCurveId = nCurves + 1;
1729 uint32_t
1730 dwColor = 0xffc0c0,
1731 dwFlags = CURVE_Outline | CURVE_Ellipses;
1732 QPen pen = QPen(QBrush(QColor(dwColor)), 1.4, Qt::SolidLine); // Qt::DashLine
1733 ui->curveView->setCurve(iCurveId, CurveData, "Energy", dwFlags, -1, pen, QBrush(dwColor));
1734 nCurves += 1;
1735 }
1736 if (document->HaveGradients() && ui->pushButton_ShowCurveGradient->isChecked()) {
1737 CurveData.resize(document->GetNumFrames());
1738 for (int iFrame = 0; iFrame < document->GetNumFrames(); ++ iFrame)
1739 CurveData[iFrame] = float(document->GetFrame(iFrame)->GetGradient());
1740 for (size_t i = 0; i < CurveData.size(); ++ i) {
1741 if (CurveData[i] < 1e-10)
1742 CurveData[i] = 1e-10;
1743 CurveData[i] = std::log(CurveData[i]);
1744 }
1745 NormalizeCurve(CurveData, 0., 1.);
1746 uint iCurveId = nCurves + 1;
1747 uint32_t
1748 dwColor = 0xc0c0ff,
1749 dwFlags = CURVE_Outline | CURVE_Ellipses;
1750 QPen pen = QPen(QBrush(QColor(dwColor)), 1.4, Qt::SolidLine); // Qt::DashLine
1751 ui->curveView->setCurve(iCurveId, CurveData, "Gradient", dwFlags, -1, pen, QBrush(dwColor));
1752 nCurves += 1;
1753 }
1754
1755 for (uint iDataSet = 0; iDataSet < DataList.size(); ++ iDataSet)
1756 {
1757 FDataSetPtr
1758 pActiveData = DataList[iDataSet];
1759 bool
1760 IsCurrent_ = int(iDataSet) == document->GetActiveRowIndex();
1761 // IsCurrent_ = int(iDataSet) == ui->dial_OrbitalId->value();
1762 if (!pActiveData->Active && !IsCurrent_)
1763 continue;
1764 FOrbital
1765 *pOrbital = dynamic_cast<FOrbital*>(pActiveData.get());
1766 if (!pOrbital)
1767 continue;
1768 if (IsCurrent_) {
1769 ui->pushButton_ToggleTrackedOrbital->setChecked(pActiveData->Active);
1770 ui->pushButton_ToggleTrackedOrbital->setText(IvFmt("Show #%1", iDataSet));
1771 }
1772 for (uint IsCurrent = 1 - uint(pActiveData->Active); IsCurrent != 1 + uint(IsCurrent_); ++ IsCurrent) {
1773 QPen
1774 pen;
1775 uint32_t
1776 dwColor,
1777 dwFlags = CURVE_Outline | CURVE_Ellipses;
1778 // dwFlags |= CURVE_Bold;
1779 int
1780 zOrder = 0;
1781 uint
1782 iCurveId = nCurves + 1;
1783 if (IsCurrent == 1) {
1784 dwColor = 0xffffffff;
1785 // pen = QPen(QBrush(QColor(dwColor)), 3.41, Qt::DotLine); // Qt::DashLine
1786 pen = QPen(QBrush(QColor(dwColor)), 2.41, Qt::DotLine); // Qt::DashLine
1787 zOrder = 1; // paint on top.
1788 // dwFlags = CURVE_Fill;
1789 // dwFlags |= CURVE_Bold;
1790 dwFlags &= ~CURVE_Ellipses;
1791 iCurveId = 0;
1792 } else {
1793 // if (pOrbital->pVisConfig->bColorSet)
1794 // // dwColor = (FColor(0.5f*(FColor(pOrbital->pVisConfig->cIsoPlus) + FColor(pOrbital->pVisConfig->cIsoMinus))).uint32());
1795 // dwColor = ct::irgb(FColor(0.5f*(FColor(pOrbital->pVisConfig->cIsoPlus) + FColor(pOrbital->pVisConfig->cIsoMinus))).uint32());
1796 // else
1797 // dwColor = 0xffffff;
1798 // // ^-- not set before rendering is finished...
1799 // dwColor |= 0xff000000;
1800 dwColor = 0xff000000 | pOrbital->GetBaseColor();
1801 pen = QPen(QBrush(QColor(dwColor)), 2.01, Qt::SolidLine);
1802 }
1803
1804 if (!MakeIboChangeCurve(CurveData, iDataSet, document))
1805 // failed to make the delta-frame data set.
1806 continue;
1807
1808 // PrintChargeArray(std::cout, "set curve", CurveData);
1809 QString
1810 Title = IvFmt("Orb. %1", iDataSet);
1811 if (IsCurrent)
1812 Title = "(current)";
1813 ui->curveView->setCurve(iCurveId, CurveData, Title, dwFlags, zOrder, pen, QBrush(dwColor));
1814 if (IsCurrent != 1) {
1815 nCurves += 1;
1816 }
1817 }
1818 }
1819 ui->curveView->update();
1820 iUpdateLocked -= 1;
1821 }
1822
onToggleTrackedOrbitalClicked()1823 void FMainWindow::onToggleTrackedOrbitalClicked()
1824 {
1825 if (iUpdateLocked != 0)
1826 return;
1827 if (!document->GetFrameData(0) || !document->GetCurrentFrameData())
1828 return;
1829 iUpdateLocked += 1;
1830 FDataSetList
1831 *pDataList = document->GetCurrentFrameData();
1832 // ui->pushButton_ToggleTrackedOrbital->setChecked(pActiveData->Active);
1833 // ui->pushButton_ToggleTrackedOrbital->isChecked();
1834 int
1835 iRow = ui->dial_OrbitalId->value();
1836 if (pDataList && size_t(iRow) < pDataList->size()) {
1837 FDataSetPtr
1838 pActiveData = (*pDataList)[iRow];
1839 document->ToggleDataRow(iRow);
1840 view3d->updateGL(); // <- to force rendering the orbital such that we have a color set in the curve view...
1841 ui->pushButton_ToggleTrackedOrbital->setChecked(pActiveData->Active);
1842 }
1843 // document->GetCurrentFrame()->document->GetFrame(0)
1844 iUpdateLocked -= 1;
1845 onActiveIboCurveChanged();
1846 onActiveDatasetChanged(); // to enable/disable the orbital color controls.
1847 }
1848
UpdateAtomAndBondScalesText()1849 void FMainWindow::UpdateAtomAndBondScalesText()
1850 {
1851 ui->groupBox_AtomAndBondScales->setTitle(QString("Atom && Bond Size (%1, %2)").arg(ui->dialAtomScale->value()).arg(ui->dialBondScale->value()));
1852 }
1853
1854
1855 class QMakeWfThread : public QRunnable
1856 {
1857 public:
QMakeWfThread(FLogQt & Log_,FDocument * pDocument_,QAbstractButton * pAbortButton_)1858 QMakeWfThread(FLogQt &Log_, FDocument *pDocument_, QAbstractButton *pAbortButton_)
1859 : m_Log(Log_), m_pDocument(pDocument_), m_pAbortButton(pAbortButton_) {}
1860 void run();
1861 protected:
1862 // ct::FLog &m_Log;
1863 FLogQt &m_Log;
1864 FDocument *m_pDocument;
1865 QAbstractButton *m_pAbortButton;
1866 };
1867
HtmlHightlight(QString s)1868 QString HtmlHightlight(QString s) { return "<pre style=\"color:white;\">" + s + "</pre>"; }
1869
run()1870 void QMakeWfThread::run()
1871 {
1872 m_Log.Write(q2s(HtmlHightlight("*** WAVE FUNCTION COMPUTATION STARTING")));
1873 m_pDocument->RebuildWf(m_Log);
1874 m_Log.Write(q2s(HtmlHightlight("*** WAVE FUNCTION COMPUTATION FINISHED.")));
1875 m_Log.endReport();
1876 //if (m_pAbortButton)
1877 // m_pAbortButton->setEnabled(false);
1878 // ^- hmpf... it's in a different thread. Can't do that.
1879 }
1880
1881
onComputeWaveFunctionTriggered()1882 void FMainWindow::onComputeWaveFunctionTriggered()
1883 {
1884 FComputeWfForm
1885 WfDialog(document);
1886 if (bool(WfDialog.exec()) && (WfDialog.GetRunScf() || WfDialog.GetRunIbba())) {
1887 if (!WfDialog.IsMemoryOkay()) {
1888 QMessageBox::StandardButton
1889 btn = QMessageBox::question(this, "Potential Memory Overload",
1890 "This computation may need more memory "
1891 "than installed on this computer. We strongly recommend to perform it "
1892 "with a standard quantum chemistry package instead of IboView itself, and to just load "
1893 "the exported result for analysis."
1894 "\n\n"
1895 "Do you really wish to run this calculation now?",
1896 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
1897 if (btn != QMessageBox::Yes)
1898 return;
1899 }
1900
1901 FDataSetList
1902 ObjectLock;
1903 document->AcquireFrameObjectLock(ObjectLock);
1904 // ^- what is this? it is a list of *all* data set objects within
1905 // frames... because some of them have GL bindings, and GL cannot be
1906 // accessed from anything but the main thread. This is not restricted to
1907 // rendering. Trying to delete the GL resource bindings from another
1908 // thread crashes the application. I am not kidding. So we here keep smart
1909 // pointer refs to them to defer their deletion until the lock object goes
1910 // out of scope (which it does in the main thread).
1911
1912 // IvNotify(NOTIFY_Warning, IvFmt("Compute WF triggered! Basis = %1", document->GetWfOptions()->GetOrbBasis()));
1913 // document->RebuildWf();
1914 FShowTextForm
1915 *pShowTextForm = new FShowTextForm(QString(), "SCF Progress", "Abort", this);
1916 pShowTextForm->setAttribute( Qt::WA_DeleteOnClose, true );
1917 // ct::FLogStdStream xxLog(std::cout);
1918 // ct::FLog *pLog = &xxLog;
1919 // FLogQt *pLog = new FLogQt(pShowTextForm);
1920 FMemoryLogQt *pLog = new FMemoryLogQt(pShowTextForm);
1921 connect(pLog, SIGNAL(textEmitted(QString)), pShowTextForm, SLOT(AppendText(QString const&)), Qt::QueuedConnection);
1922 connect(pLog, SIGNAL(sectionEnded()), pShowTextForm, SLOT(clear()), Qt::QueuedConnection);
1923 connect(pLog, SIGNAL(reportEnded(QString)), pShowTextForm, SLOT(setText(QString)), Qt::QueuedConnection);
1924 connect(pShowTextForm, SIGNAL(redButtonPressed()), pLog, SLOT(emitAbortSignal()), Qt::QueuedConnection);
1925 // connect(document, SIGNAL(layoutChanged()), pShowTextForm, SLOT(close()), Qt::QueuedConnection);
1926 QThreadPool::globalInstance()->start(new QMakeWfThread(*pLog, document, pShowTextForm->GetRedButton()));
1927 // QMakeWfThread test(*pLog, document);
1928 // test.run();
1929 // ^- should this take ownership?
1930 pShowTextForm->exec();
1931 // FIXME: this will soooo blow up if the form is closed before the thread is done...
1932
1933 // invalidate cached rendering data
1934 onRebuildIsoSurfacesClicked();
1935 }
1936 }
1937
1938
1939
1940
1941 struct FAboutDialogImpl
1942 {
1943 };
1944
FAboutDialog(QWidget * parent)1945 FAboutDialog::FAboutDialog(QWidget *parent)
1946 : QDialog(parent),
1947 ui(new Ui::AboutDialog),
1948 p(new FAboutDialogImpl)
1949 {
1950 ui->setupUi(this);
1951 ui->svgWidget->sizePolicy().setHeightForWidth(true);
1952 ui->svgWidget->load(QString(":/resources/iboview_logo.svg"));
1953 ui->textBrowser->setHtml(LoadTextFromFile(":/resources/credits.htm"));
1954 if (!IvRestoreWindowSize("AboutDialog/Size", this))
1955 IvGuessSubDialogSize(this);
1956 }
1957
~FAboutDialog()1958 FAboutDialog::~FAboutDialog()
1959 {
1960 IvSaveWindowSize("AboutDialog/Size", this);
1961 delete ui;
1962 delete p;
1963 }
1964
1965 // void FAboutDialog::closeEvent(QCloseEvent *)
1966 // {
1967 // return QDialog::closeEvent(event);
1968 // }
1969
1970
1971 // #include <QScriptEngine>
1972
1973 class FMyApplication;
1974 static FMyApplication *g_papp = 0;
1975
1976
1977 class FMyApplication : public QApplication
1978 {
1979 public:
FMyApplication(int & argc,char ** argv)1980 FMyApplication(int &argc, char **argv) : QApplication(argc, argv)
1981 {
1982 g_papp = this;
1983 // MainWindow.show();
1984 #if QT_VERSION >= 0x050000
1985 // must be done after QApplication object is constructed. Otherwise undeployable
1986 // on windows ('could not load or find the Qt platform plugin "windows"')
1987 if (s_UseStyleFiles) {
1988 QApplication::setDesktopSettingsAware(false);
1989 QApplication::setStyle(QStyleFactory::create("Fusion"));
1990 }
1991 #endif
1992 if (s_UseStyleFiles) {
1993 // load and apply our super cool style sheet 8).
1994 // (I could not resist)
1995 QFile StyleFile(":/resources/style.qss");
1996 StyleFile.open(QFile::ReadOnly);
1997 QString Text(StyleFile.readAll());
1998 this->setStyleSheet(Text);
1999 }
2000
2001 pMainWindow = new IApplication();
2002
2003 // check if we have a startup script to execute. If yes, execut it.
2004 if (1) {
2005 QSettings
2006 settings;
2007 QString
2008 StartupScriptText,
2009 StartupScriptFile = settings.value("IboView/StartupScriptFile").toString();
2010 if (!StartupScriptFile.isEmpty()) {
2011 StartupScriptText = LoadTextFileViaQt(StartupScriptFile);
2012
2013 if (!StartupScriptText.isEmpty()) {
2014 ExecScript(pMainWindow, pMainWindow->view3d, StartupScriptText, QString("Startup Script: %1").arg(StartupScriptFile));
2015 }
2016 }
2017 }
2018 }
2019 // IApplication MainWindow;
2020 IApplication
2021 *pMainWindow;
2022
~FMyApplication()2023 ~FMyApplication() {
2024 g_papp = 0;
2025 delete pMainWindow; // not in this->children.
2026 pMainWindow = 0;
2027 }
2028 };
2029
2030 static QStringList
2031 s_CommandLineArgs;
2032
processInputs()2033 void FMainWindow::processInputs()
2034 {
2035 // I guess at this point only file names are left?
2036 load_files_(s_CommandLineArgs);
2037
2038 IvNotify(NOTIFY_FinishWork, "");
2039 }
2040
2041
2042
IvWarn(std::string const & Text)2043 void IvWarn(std::string const &Text)
2044 {
2045 IvNotify(NOTIFY_Warning, s2q(Text));
2046 }
IvWarn(QString const & Text)2047 void IvWarn(QString const &Text)
2048 {
2049 IvNotify(NOTIFY_Warning, Text);
2050 }
2051
IvEmit(QString const & Text)2052 void IvEmit(QString const &Text)
2053 {
2054 // if (Text.startsWith("!") || Text.startsWith("*")) {
2055 // FMainWindow
2056 // *pWin = qobject_cast<FMainWindow*>(g_pMainWindow);
2057 // std::cout << "^^^ STR: " << pWin << std::endl;
2058 // if (pWin) {
2059 // pWin->ui->statusBar->SetStatus(STATUS_Working, Text);
2060 // }
2061 // }
2062
2063 std::cout << Text.toStdString() << std::endl;
2064 }
2065
2066 void IvEmit(QString const &Text);
2067
IvEmit(QString Text,FEmitArg const & a0)2068 void IvEmit(QString Text, FEmitArg const &a0) { return IvEmit(Text.arg(a0.m)); }
IvEmit(QString Text,FEmitArg const & a0,FEmitArg const & a1)2069 void IvEmit(QString Text, FEmitArg const &a0, FEmitArg const &a1) { return IvEmit( Text.arg(a0.m, a1.m)); }
IvEmit(QString Text,FEmitArg const & a0,FEmitArg const & a1,FEmitArg const & a2)2070 void IvEmit(QString Text, FEmitArg const &a0, FEmitArg const &a1, FEmitArg const &a2) { return IvEmit( Text.arg(a0.m, a1.m, a2.m)); }
IvEmit(QString Text,FEmitArg const & a0,FEmitArg const & a1,FEmitArg const & a2,FEmitArg const & a3)2071 void IvEmit(QString Text, FEmitArg const &a0, FEmitArg const &a1, FEmitArg const &a2, FEmitArg const &a3) { return IvEmit( Text.arg(a0.m, a1.m, a2.m, a3.m)); }
IvEmit(QString Text,FEmitArg const & a0,FEmitArg const & a1,FEmitArg const & a2,FEmitArg const & a3,FEmitArg const & a4)2072 void IvEmit(QString Text, FEmitArg const &a0, FEmitArg const &a1, FEmitArg const &a2, FEmitArg const &a3, FEmitArg const &a4) { return IvEmit( Text.arg(a0.m, a1.m, a2.m, a3.m, a4.m)); }
IvEmit(QString Text,FEmitArg const & a0,FEmitArg const & a1,FEmitArg const & a2,FEmitArg const & a3,FEmitArg const & a4,FEmitArg const & a5)2073 void IvEmit(QString Text, FEmitArg const &a0, FEmitArg const &a1, FEmitArg const &a2, FEmitArg const &a3, FEmitArg const &a4, FEmitArg const &a5) { return IvEmit( Text.arg(a0.m, a1.m, a2.m, a3.m, a4.m, a5.m)); }
IvEmit(QString Text,FEmitArg const & a0,FEmitArg const & a1,FEmitArg const & a2,FEmitArg const & a3,FEmitArg const & a4,FEmitArg const & a5,FEmitArg const & a6)2074 void IvEmit(QString Text, FEmitArg const &a0, FEmitArg const &a1, FEmitArg const &a2, FEmitArg const &a3, FEmitArg const &a4, FEmitArg const &a5, FEmitArg const &a6) { return IvEmit( Text.arg(a0.m, a1.m, a2.m, a3.m, a4.m, a5.m, a6.m)); }
IvEmit(QString Text,FEmitArg const & a0,FEmitArg const & a1,FEmitArg const & a2,FEmitArg const & a3,FEmitArg const & a4,FEmitArg const & a5,FEmitArg const & a6,FEmitArg const & a7)2075 void IvEmit(QString Text, FEmitArg const &a0, FEmitArg const &a1, FEmitArg const &a2, FEmitArg const &a3, FEmitArg const &a4, FEmitArg const &a5, FEmitArg const &a6, FEmitArg const &a7) { return IvEmit( Text.arg(a0.m, a1.m, a2.m, a3.m, a4.m, a5.m, a6.m, a7.m)); }
2076
IvFmt(QString Text,FEmitArg const & a0)2077 QString IvFmt(QString Text, FEmitArg const &a0) { return Text.arg(a0.m); }
IvFmt(QString Text,FEmitArg const & a0,FEmitArg const & a1)2078 QString IvFmt(QString Text, FEmitArg const &a0, FEmitArg const &a1) { return Text.arg(a0.m, a1.m); }
IvFmt(QString Text,FEmitArg const & a0,FEmitArg const & a1,FEmitArg const & a2)2079 QString IvFmt(QString Text, FEmitArg const &a0, FEmitArg const &a1, FEmitArg const &a2) { return Text.arg(a0.m, a1.m, a2.m); }
IvFmt(QString Text,FEmitArg const & a0,FEmitArg const & a1,FEmitArg const & a2,FEmitArg const & a3)2080 QString IvFmt(QString Text, FEmitArg const &a0, FEmitArg const &a1, FEmitArg const &a2, FEmitArg const &a3) { return Text.arg(a0.m, a1.m, a2.m, a3.m); }
IvFmt(QString Text,FEmitArg const & a0,FEmitArg const & a1,FEmitArg const & a2,FEmitArg const & a3,FEmitArg const & a4)2081 QString IvFmt(QString Text, FEmitArg const &a0, FEmitArg const &a1, FEmitArg const &a2, FEmitArg const &a3, FEmitArg const &a4) { return Text.arg(a0.m, a1.m, a2.m, a3.m, a4.m); }
IvFmt(QString Text,FEmitArg const & a0,FEmitArg const & a1,FEmitArg const & a2,FEmitArg const & a3,FEmitArg const & a4,FEmitArg const & a5)2082 QString IvFmt(QString Text, FEmitArg const &a0, FEmitArg const &a1, FEmitArg const &a2, FEmitArg const &a3, FEmitArg const &a4, FEmitArg const &a5) { return Text.arg(a0.m, a1.m, a2.m, a3.m, a4.m, a5.m); }
IvFmt(QString Text,FEmitArg const & a0,FEmitArg const & a1,FEmitArg const & a2,FEmitArg const & a3,FEmitArg const & a4,FEmitArg const & a5,FEmitArg const & a6)2083 QString IvFmt(QString Text, FEmitArg const &a0, FEmitArg const &a1, FEmitArg const &a2, FEmitArg const &a3, FEmitArg const &a4, FEmitArg const &a5, FEmitArg const &a6) { return Text.arg(a0.m, a1.m, a2.m, a3.m, a4.m, a5.m, a6.m); }
IvFmt(QString Text,FEmitArg const & a0,FEmitArg const & a1,FEmitArg const & a2,FEmitArg const & a3,FEmitArg const & a4,FEmitArg const & a5,FEmitArg const & a6,FEmitArg const & a7)2084 QString IvFmt(QString Text, FEmitArg const &a0, FEmitArg const &a1, FEmitArg const &a2, FEmitArg const &a3, FEmitArg const &a4, FEmitArg const &a5, FEmitArg const &a6, FEmitArg const &a7) { return Text.arg(a0.m, a1.m, a2.m, a3.m, a4.m, a5.m, a6.m, a7.m); }
2085
2086 namespace fmt {
2087 /*
2088 #define MAKE_FMT_X_FN(FmtName, FType) \
2089 QString FmtName(QString const &fmt, FType arg) { \
2090 return QString::fromStdString(boost::str(boost::format(fmt.toStdString()) % arg)); \
2091 }
2092 // ^- that must be the most expensive string formatting routine ever.
2093 MAKE_FMT_X_FN(fmti, long)
2094 MAKE_FMT_X_FN(fmti, unsigned long)
2095 MAKE_FMT_X_FN(fmti, int)
2096 MAKE_FMT_X_FN(fmti, unsigned int)
2097 MAKE_FMT_X_FN(fmtf, float)
2098 MAKE_FMT_X_FN(fmtf, double)
2099 #undef MAKE_FMT_X_FN
2100
2101 QString fmts(QString const &fmt, QString s) {
2102 return QString::fromStdString(boost::str(boost::format(fmt.toStdString()) % s.toStdString()));
2103 }
2104 */
fmt_width(QString const & s,int width)2105 inline QString fmt_width(QString const &s, int width) {
2106 if (width == 0)
2107 return s;
2108 else
2109 return QString("%1").arg(s, width);
2110 }
2111 // these ones use explicit width/prec/base specifications (note: still
2112 // options missing... e.g., field alignment, leading zeros, 'always use sign',
2113 // etc..).
fmti(int i,int width,int base)2114 QString fmti(int i, int width, int base) { return fmt_width(QString::number(i, (base==0? 10:base)), width); }
fmti(unsigned int i,int width,int base)2115 QString fmti(unsigned int i, int width, int base) { return fmt_width(QString::number(i, (base==0? 10:base)), width); }
fmti(long i,int width,int base)2116 QString fmti(long i, int width, int base) { return fmt_width(QString::number(i, (base==0? 10:base)), width); }
fmti(unsigned long i,int width,int base)2117 QString fmti(unsigned long i, int width, int base) { return fmt_width(QString::number(i, (base==0? 10:base)), width); }
2118 #ifdef NEED_EXTRA_SIZE_T_TYPES
fmti(ptrdiff_t i,int width,int base)2119 QString fmti(ptrdiff_t i, int width, int base) { return fmt_width(QString::number(i, (base==0? 10:base)), width); }
fmti(size_t i,int width,int base)2120 QString fmti(size_t i, int width, int base) { return fmt_width(QString::number(i, (base==0? 10:base)), width); }
2121 #endif
2122
fmtf(double f,int width,int prec,char format)2123 QString fmtf(double f, int width, int prec, char format) { return fmt_width(QString::number(f, format, prec), width); }
fmtf(float f,int width,int prec,char format)2124 QString fmtf(float f, int width, int prec, char format) { return fmt_width(QString::number(f, format, prec), width); }
2125
fmts(QString const & s,int width)2126 QString fmts(QString const &s, int width) {
2127 return fmt_width(s, width);
2128 // ^- note: QString uses negative field widths to indicate left alignment.
2129 // For strings that is usually what we want.
2130 }
fmts(char const * p,int width)2131 QString fmts(char const *p, int width) { return fmts(QString(p), width); }
fmtp(void * p)2132 QString fmtp(void *p) { return "0x"+fmti(reinterpret_cast<size_t>(p),8,16); }
2133 // ^- should really be uintptr_t, but that's not standard C++.
2134 }
2135
2136
NotifyClassToStatusClass(FNotificationClass NotifyClass)2137 FStatusClass NotifyClassToStatusClass(FNotificationClass NotifyClass) {
2138 switch (NotifyClass) {
2139 case NOTIFY_Information: return STATUS_Idle;
2140 case NOTIFY_StartWork: return STATUS_Working;
2141 case NOTIFY_FinishWork: return STATUS_Idle;
2142 case NOTIFY_Warning: return STATUS_Warning;
2143 case NOTIFY_Error: return STATUS_Error;
2144 default: return STATUS_Unknown;
2145 }
2146 }
2147
2148
2149 // FIXME: make a log class etc. This is just a hack for now.
UpdateStatus(FNotificationClass NotifyClass,QString const & Text)2150 static void UpdateStatus(FNotificationClass NotifyClass, QString const &Text)
2151 {
2152 FMainWindow
2153 *pWin = qobject_cast<FMainWindow*>(g_pMainWindow);
2154 if (pWin) {
2155 pWin->ui->statusBar->SetStatus(NotifyClassToStatusClass(NotifyClass), Text);
2156 }
2157 }
2158
2159 // if (document->columnCount() == 0)
2160 // ui->statusBar->SetStatus(STATUS_Unknown, "Ready");
2161 // else
2162 // ui->statusBar->SetStatus(STATUS_Idle, "Ready");
2163
2164
IvNotify(FNotificationClass NotifyClass,QString const & Text)2165 void IvNotify(FNotificationClass NotifyClass, QString const &Text)
2166 {
2167 bool
2168 IsError = NotifyClass == NOTIFY_Error;
2169 std::ostream
2170 *pout = IsError? &std::cerr : &std::cout;
2171
2172 if (NotifyClass == NOTIFY_StartWork || NotifyClass == NOTIFY_Information || NotifyClass == NOTIFY_FinishWork) {
2173 if (!(NotifyClass == NOTIFY_FinishWork || NotifyClass == NOTIFY_Information))
2174 *pout << q2s(Text) << std::endl;
2175 else {
2176 if (Text.isEmpty())
2177 return UpdateStatus(NotifyClass, "Ready");
2178 }
2179 return UpdateStatus(NotifyClass, Text);
2180 }
2181
2182 QString
2183 Title = "IboView Message",
2184 Message = Text;
2185 int iContextStart = Text.indexOf(":\t");
2186 if (iContextStart != -1) {
2187 Title = Text.mid(0, iContextStart);
2188 Message = Text.mid(iContextStart+2);
2189 }
2190
2191 if (NotifyClass == NOTIFY_Error) {
2192 *pout << "\n!ERROR: ";
2193 Title = "Error in " + Title;
2194 }
2195 else if (NotifyClass == NOTIFY_Warning) {
2196 *pout << "!WARNING: ";
2197 Title = "Warning from " + Title;
2198 }
2199 *pout << q2s(Text) << std::endl;
2200 // if (LongText == "")
2201 // *pout << q2s(Text) << std::endl;
2202 // else
2203 // *pout << q2s(Text) << "\n" << q2s(LongText) << std::endl;
2204 UpdateStatus(NotifyClass, Text);
2205
2206 QWidget
2207 *parent = g_papp? g_papp->pMainWindow : 0;
2208
2209 if (NotifyClass == NOTIFY_Error)
2210 QMessageBox::critical(parent, Title, Message);
2211 // else if (NotifyClass == NOTIFY_Warning)
2212 // QMessageBox::warning(parent, Title, Message);
2213 // else
2214 // QMessageBox::information(parent, Title, Message);
2215 // ^- don't make message boxes for those by default.
2216 }
2217
2218 // void IvNotify(FNotificationClass NotifyClass, QString const &Text, QString const &LongText)
2219 // {
2220 // QString
2221 // Title = Text,
2222 // Message = LongText;
2223 // if (LongText == "") {
2224 // Title = "IboView";
2225 // Message = Text;
2226 // }
2227 //
2228 // bool
2229 // IsError = NotifyClass == NOTIFY_Error;
2230 // std::ostream
2231 // *pout = IsError? &std::cerr : &std::cout;
2232 // if (NotifyClass == NOTIFY_Error)
2233 // *pout << "\n!ERROR: ";
2234 // else if (NotifyClass == NOTIFY_Warning)
2235 // *pout << "!WARNING: ";
2236 // if (LongText == "")
2237 // *pout << q2s(Text) << std::endl;
2238 // else
2239 // *pout << q2s(Text) << "\n" << q2s(LongText) << std::endl;
2240 //
2241 // QWidget
2242 // *parent = g_papp? g_papp->pMainWindow : 0;
2243 //
2244 // if (NotifyClass == NOTIFY_Error)
2245 // QMessageBox::critical(parent, Title, Message);
2246 // // else if (NotifyClass == NOTIFY_Warning)
2247 // // QMessageBox::warning(parent, Title, Message);
2248 // // else
2249 // // QMessageBox::information(parent, Title, Message);
2250 // // ^- don't make message boxes for those by default.
2251 // }
2252
2253 // QString GetUserName() {
2254 // // recycled from http://qt-project.org/forums/viewthread/11951.
2255 // QString
2256 // name = qgetenv("USER"); // get the user name in Linux
2257 // if (name.isEmpty())
2258 // name = qgetenv("USERNAME"); // get the name in Windows
2259 // return name;
2260 // }
2261
2262
2263 struct FCmdLineArg: public option::Arg
2264 {
printErrorFCmdLineArg2265 static void printError(const char* msg1, const option::Option& opt, const char* msg2)
2266 {
2267 std::fprintf(stderr, "ERROR: %s", msg1);
2268 std::fwrite(opt.name, opt.namelen, 1, stderr);
2269 std::fprintf(stderr, "%s", msg2);
2270 }
2271
RequiredFCmdLineArg2272 static option::ArgStatus Required(const option::Option& option, bool msg)
2273 {
2274 if (option.arg != 0)
2275 return option::ARG_OK;
2276
2277 if (msg) printError("Option '", option, "' requires an argument\n");
2278 return option::ARG_ILLEGAL;
2279 }
2280
NumericFCmdLineArg2281 static option::ArgStatus Numeric(const option::Option& option, bool msg)
2282 {
2283 char* endptr = 0;
2284 if (option.arg != 0 && strtol(option.arg, &endptr, 10)){};
2285 if (endptr != option.arg && *endptr == 0)
2286 return option::ARG_OK;
2287
2288 if (msg) printError("Option '", option, "' requires a numeric argument\n");
2289 return option::ARG_ILLEGAL;
2290 }
2291 };
2292
2293
2294 enum FOptionIndex { OPTION_Unknown, OPTION_Help, OPTION_ShowVirtuals, OPTION_SkipVirtuals, OPTION_NumThreads, OPTION_ViewAlpha, OPTION_AllowBackfaces, OPTION_UseStyleFile };
2295 enum FOptionType { OPTTYPE_Other = 0, OPTTYPE_Enable = 1, OPTTYPE_Disable = 2};
2296 static const option::Descriptor s_OptionDesc[] =
2297 {
2298 {OPTION_Unknown, 0, "", "", FCmdLineArg::None, "USAGE: example [options]\n\n"
2299 "Options:" },
2300 {OPTION_Help, 0,"", "help", FCmdLineArg::None, " --help \tPrint usage and exit." },
2301 // {OPTION_ShowVirtuals, 0,"", "virt-orb", FCmdLineArg::None, " --virt-orb \tIf set, also load virtual orbitals from files, not only occupied orbitals." },
2302 {OPTION_SkipVirtuals, 0,"", "skip-virt", FCmdLineArg::None, " --skip-virt \tIf set, virtual orbitals will not be loaded from files." },
2303 {OPTION_NumThreads, 0, "t", "num-threads", FCmdLineArg::Numeric, " -t <n>, --num-threads=<n> \tSet the number of threads to use for computations (requires OpenMP)." },
2304 {OPTION_UseStyleFile, OPTTYPE_Disable, "", "disable-style", FCmdLineArg::None, " --disable-style \tIf set, disable custom style sheet for widgets. Some colors may look off." },
2305 {OPTION_ViewAlpha, OPTTYPE_Disable, "", "disable-alpha", FCmdLineArg::None, " --disable-alpha \tIf set, disable destination alpha in 3d window (workaround for MacOS window compositing)." },
2306 {OPTION_AllowBackfaces, OPTTYPE_Disable, "", "disable-backfaces", FCmdLineArg::None, " --disable-backfaces \tIf set, disable option to turn on orbital back faces (workaround for MacOS OpenGL driver bugs)." },
2307 {0,0,0,0,0,0}
2308 };
2309
2310
wrapped_main(int & argc,char * argv[])2311 int wrapped_main(int &argc, char *argv[])
2312 {
2313 // argc/argv without program name.
2314 int argc1 = (argc > 0)? (argc - 1) : 0;
2315 char **argv1 = (argc > 0)? (argv + 1) : 0;
2316
2317 // parse command line
2318 option::Stats
2319 stats(s_OptionDesc, argc1 , argv1);
2320 std::vector<option::Option>
2321 options(stats.options_max);
2322 std::vector<option::Option>
2323 buffer(stats.buffer_max);
2324 option::Parser
2325 parse(s_OptionDesc, argc1 , argv1, &options[0], &buffer[0]);
2326
2327 if (parse.error())
2328 return 1;
2329
2330 if (options[OPTION_Help]) {
2331 option::printUsage(std::cout, s_OptionDesc);
2332 return 0;
2333 }
2334
2335 if (options[OPTION_ViewAlpha].last()->type() == OPTTYPE_Disable)
2336 g_WorkAroundAlphaCompositing = true;
2337 if (options[OPTION_AllowBackfaces].last()->type() == OPTTYPE_Disable)
2338 g_WorkAroundGlFrontFacingBug = true;
2339
2340 for (int i = 0; i < parse.nonOptionsCount(); ++ i)
2341 s_CommandLineArgs.append(QString(parse.nonOption(i)));
2342
2343 if (options[OPTION_NumThreads]) {
2344 g_nMaxOmpThreads = QString(options[OPTION_NumThreads].arg).toInt();
2345 } else {
2346 // take everything there is.
2347 g_nMaxOmpThreads = omp_get_num_procs();
2348 // ^- note that this ignores OMP_NUM_THREADS (unlike omp_get_max_threads)
2349 }
2350 omp_set_num_threads(g_nMaxOmpThreads);
2351
2352 // g_ShowVirtualOrbitals = options[OPTION_ShowVirtuals];
2353 g_ShowVirtualOrbitals = !options[OPTION_SkipVirtuals];
2354
2355 //#if QT_VERSION >= 0x050000
2356 // QApplication::setDesktopSettingsAware(false);
2357 // QApplication::setStyle(QStyleFactory::create("Fusion"));
2358 //#endif
2359
2360 if (0) {
2361 // set a standard scheme in order to check if the style sheet is complete.
2362 QApplication::setDesktopSettingsAware(false);
2363 QApplication::setStyle(QStyleFactory::create("plastique"));
2364 //QApplication::setStyle(QStyleFactory::create("Fusion"));
2365 QStringList keys = QStyleFactory::keys();
2366 //QMessageBox::information(0, "known styles", keys.join("\n"));
2367 //qDebug("known styles:\n" + keys.join("\n"));
2368 for (int i = 0; i < keys.size(); ++i)
2369 std::cout << keys.at(i).toLocal8Bit().constData() << std::endl;
2370 }
2371 if (!options[OPTION_UseStyleFile] || options[OPTION_UseStyleFile].last()->type() != OPTTYPE_Disable) {
2372 s_UseStyleFiles = true;
2373 }
2374
2375 ct::g_BasisSetLibrary.ImportMolproLib(":/bases/minao.libmol");
2376 ct::g_BasisSetLibrary.ImportMolproLib(":/bases/def2-nzvpp-orb.libmol");
2377 ct::g_BasisSetLibrary.ImportMolproLib(":/bases/def2-nzvpp-jfit.libmol");
2378 ct::g_BasisSetLibrary.ImportMolproLib(":/bases/def2-nzvpp-jkfit.libmol");
2379
2380 // these ones are for instanciating QSettings objects for persistent storage of application settings
2381 // (in particular, window size, splitter size, and position).
2382 QCoreApplication::setOrganizationName("Knizia-Research-Group");
2383 QCoreApplication::setOrganizationDomain("iboview.org");
2384 QCoreApplication::setApplicationName("IboView");
2385 QCoreApplication::setApplicationVersion("v20150427");
2386
2387 FMyApplication
2388 app(argc, argv); // <- that's the main QApplication object.
2389 app.setWheelScrollLines(1); // mainly for IBO tracking. others work more or less fine with default value of 3.
2390
2391 // process command line arguments, but not before the event loop
2392 // is started (otherwise we cannot quit the application via script!)
2393 QTimer::singleShot(0, app.pMainWindow, SLOT(processInputs()));
2394 // QTimer::singleShot(1000, app.pMainWindow, SLOT(processInputs()));
2395 return app.exec();
2396 }
2397
2398
2399
2400
main(int argc,char * argv[])2401 int main(int argc, char *argv[])
2402 {
2403 if (1) {
2404 try {
2405 return wrapped_main(argc, argv);
2406 } catch (std::exception &e) {
2407 // std::cerr << boost::format("\n# Application crashed. Received Unhandled Exception:\n%s") % e.what() << std::endl;
2408 IvNotify(NOTIFY_Error, "Application crashed:\t" + QString("Received Unhandled Exception:\n%1").arg(QString(e.what())));
2409 }
2410 } else {
2411 return wrapped_main(argc, argv);
2412 }
2413 return -1;
2414 }
2415