diff --git a/avogadro/qtplugins/customelements/customelements.cpp b/avogadro/qtplugins/customelements/customelements.cpp index 6a561056fb..2d3342b227 100644 --- a/avogadro/qtplugins/customelements/customelements.cpp +++ b/avogadro/qtplugins/customelements/customelements.cpp @@ -16,11 +16,18 @@ #include "customelements.h" +#include #include +#include #include +#include +#include #include +#include +#include +using Avogadro::QtGui::BackgroundFileFormat; using Avogadro::QtGui::Molecule; namespace Avogadro { @@ -28,16 +35,20 @@ namespace QtPlugins { CustomElements::CustomElements(QObject* parent_) : Avogadro::QtGui::ExtensionPlugin(parent_), m_molecule(nullptr), - m_reassignAction(new QAction(tr("Reassign &Custom Elements..."), this)) + m_reassignUsingTool(nullptr), m_reassignFromFile(nullptr), + m_fileReadThread(nullptr), m_threadedReader(nullptr), + m_fileReadMolecule(nullptr), m_progressDialog(nullptr) { - connect(m_reassignAction, SIGNAL(triggered()), SLOT(reassign())); + m_reassignUsingTool = new QAction(tr("Reassign &Custom Elements..."), this); + m_reassignFromFile = + new QAction(tr("&Import Coordinate/Topology File..."), this); + connect(m_reassignUsingTool, SIGNAL(triggered()), SLOT(reassign())); + connect(m_reassignFromFile, SIGNAL(triggered()), SLOT(importMapFile())); updateReassignAction(); } -CustomElements::~CustomElements() -{ -} +CustomElements::~CustomElements() {} QString CustomElements::description() const { @@ -46,7 +57,7 @@ QString CustomElements::description() const QList CustomElements::actions() const { - return QList() << m_reassignAction; + return QList() << m_reassignUsingTool << m_reassignFromFile; } QStringList CustomElements::menuPath(QAction*) const @@ -86,9 +97,129 @@ void CustomElements::reassign() } } +bool CustomElements::openFile(const QString& fileName, Io::FileFormat* reader) +{ + if (fileName.isEmpty() || reader == nullptr) { + delete reader; + return false; + } + + QString ident = QString::fromStdString(reader->identifier()); + + // Prepare the background thread to read in the selected file. + if (!m_fileReadThread) + m_fileReadThread = new QThread(qobject_cast(parent())); + if (m_threadedReader) + m_threadedReader->deleteLater(); + m_threadedReader = new BackgroundFileFormat(reader); + if (m_fileReadMolecule) + m_fileReadMolecule->deleteLater(); + m_fileReadMolecule = new Molecule(qobject_cast(parent())); + m_fileReadMolecule->setData("fileName", fileName.toLocal8Bit().data()); + m_threadedReader->moveToThread(m_fileReadThread); + m_threadedReader->setMolecule(m_fileReadMolecule); + m_threadedReader->setFileName(fileName); + + // Setup a progress dialog in case file loading is slow + m_progressDialog = new QProgressDialog(qobject_cast(parent())); + m_progressDialog->setRange(0, 0); + m_progressDialog->setValue(0); + m_progressDialog->setMinimumDuration(750); + m_progressDialog->setWindowTitle(tr("Reading File")); + m_progressDialog->setLabelText( + tr("Opening file '%1'\nwith '%2'").arg(fileName).arg(ident)); + m_progressDialog->setCancelButton(nullptr); + connect(m_progressDialog, SIGNAL(canceled()), m_fileReadThread, SLOT(quit())); + connect(m_fileReadThread, SIGNAL(started()), m_threadedReader, SLOT(read())); + connect(m_threadedReader, SIGNAL(finished()), m_fileReadThread, SLOT(quit())); + connect(m_threadedReader, SIGNAL(finished()), + SLOT(backgroundReaderFinished())); + + // Start the file operation + m_fileReadThread->start(); + m_progressDialog->show(); + + return true; +} + +void CustomElements::backgroundReaderFinished() +{ + QString fileName = m_threadedReader->fileName(); + if (m_progressDialog->wasCanceled()) { + delete m_fileReadMolecule; + } else if (m_threadedReader->success()) { + if (!fileName.isEmpty()) { + m_fileReadMolecule->setData("fileName", fileName.toLocal8Bit().data()); + } else { + m_fileReadMolecule->setData("fileName", Core::Variant()); + } + setMapFromMolecule(m_fileReadMolecule); + } else { + QMessageBox::critical(qobject_cast(parent()), tr("File error"), + tr("Error while reading file '%1':\n%2") + .arg(fileName) + .arg(m_threadedReader->error())); + delete m_fileReadMolecule; + } + m_fileReadThread->deleteLater(); + m_fileReadThread = nullptr; + m_threadedReader->deleteLater(); + m_threadedReader = nullptr; + m_fileReadMolecule = nullptr; + m_progressDialog->hide(); + m_progressDialog->deleteLater(); + m_progressDialog = nullptr; +} + +void CustomElements::setMapFromMolecule(QtGui::Molecule* mol) +{ + if (mol->atomCount() != m_molecule->atomCount()) { + QMessageBox::critical( + qobject_cast(parent()), tr("Error"), + tr("Atom count mismatch.\nExpected %1 atoms, found %2.") + .arg(m_molecule->atomCount()) + .arg(mol->atomCount())); + } else { + size_t n = m_molecule->atomCount(), i; + for (i = 0; i < n; ++i) { + m_molecule->atom(i).setAtomicNumber(mol->atom(i).atomicNumber()); + } + n = m_molecule->bondCount(); + for (i = 0; i < n; ++i) { + m_molecule->addBond(mol->bond(i).atom1(), mol->bond(i).atom2(), + mol->bond(i).order()); + } + m_molecule->emitChanged(Molecule::Atoms | Molecule::Modified); + } +} + +void CustomElements::importMapFile() +{ + QSettings settings; + QString dir = settings.value("MainWindow/lastOpenDir").toString(); + + QtGui::FileFormatDialog::FormatFilePair reply = + QtGui::FileFormatDialog::fileToRead(qobject_cast(parent()), + tr("Open Molecule"), dir); + + if (reply.first == NULL) // user cancel + return; + + dir = QFileInfo(reply.second).absoluteDir().absolutePath(); + settings.setValue("MainWindow/lastOpenDir", dir); + + if (!openFile(reply.second, reply.first->newInstance())) { + QMessageBox::information( + qobject_cast(parent()), tr("Cannot open file"), + tr("Can't open supplied file %1").arg(reply.second)); + } +} + void CustomElements::updateReassignAction() { - m_reassignAction->setEnabled(m_molecule && m_molecule->hasCustomElements()); + m_reassignUsingTool->setEnabled(m_molecule && + m_molecule->hasCustomElements()); + m_reassignFromFile->setEnabled(m_molecule && m_molecule->atomCount()); } } // namespace QtPlugins diff --git a/avogadro/qtplugins/customelements/customelements.h b/avogadro/qtplugins/customelements/customelements.h index 00c215e12d..27cd029528 100644 --- a/avogadro/qtplugins/customelements/customelements.h +++ b/avogadro/qtplugins/customelements/customelements.h @@ -19,7 +19,15 @@ #include +class QProgressDialog; +class QThread; + namespace Avogadro { + +namespace QtGui { +class BackgroundFileFormat; +} + namespace QtPlugins { /** @@ -43,12 +51,21 @@ public slots: private slots: void moleculeChanged(unsigned int changes); void reassign(); + void importMapFile(); + void backgroundReaderFinished(); private: QtGui::Molecule* m_molecule; - QAction* m_reassignAction; + QAction* m_reassignUsingTool; + QAction* m_reassignFromFile; + QThread* m_fileReadThread; + QtGui::BackgroundFileFormat* m_threadedReader; + QtGui::Molecule* m_fileReadMolecule; + QProgressDialog* m_progressDialog; void updateReassignAction(); + bool openFile(const QString& fileName, Io::FileFormat* reader); + void setMapFromMolecule(QtGui::Molecule* mol); }; } // namespace QtPlugins