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