diff --git a/.github/workflows/codacy.yml b/.github/workflows/codacy.yml index 0b061851..0730af9b 100644 --- a/.github/workflows/codacy.yml +++ b/.github/workflows/codacy.yml @@ -53,13 +53,17 @@ jobs: # This will handover control about PR rejection to the GitHub side max-allowed-issues: 2147483647 - - name: Filter out MISRA + # Filter out MISRA and potentially reduce the number of runs in the SARIF output + - name: Filter and Reduce SARIF run: | pip install globber - python3 scripts/filter_sarif.py --input results.sarif --output filtered.sarif --split-lines -- "-**/*.*:cppcheck_misra*" + python3 scripts/filter_and_reduce_sarif.py --input results.sarif --output filtered_and_reduced.sarif + env: + PYTHONUNBUFFERED: 1 - # Upload the SARIF file generated in the previous step + + # Upload the SARIF file generated in the previous step - name: Upload SARIF results file uses: github/codeql-action/upload-sarif@v3 with: - sarif_file: filtered.sarif + sarif_file: filtered_and_reduced.sarif \ No newline at end of file diff --git a/avogadro/mainwindow.cpp b/avogadro/mainwindow.cpp index e520f007..68b7f270 100644 --- a/avogadro/mainwindow.cpp +++ b/avogadro/mainwindow.cpp @@ -38,6 +38,8 @@ #include #include +#include +#include #include #include #include @@ -50,8 +52,6 @@ #include #include #include - -#include #include #include #include @@ -228,6 +228,8 @@ using VTK::vtkGLWidget; #endif MainWindow::MainWindow(const QStringList& fileNames, bool disableSettings) + : QMainWindow(/* parent */) + , dragStartPosition() : m_molecule(nullptr) , m_rwMolecule(nullptr) , m_moleculeModel(nullptr) @@ -357,6 +359,50 @@ MainWindow::~MainWindow() delete m_viewFactory; } +void MainWindow::mousePressEvent(QMouseEvent* event) +{ + if (event->button() == Qt::LeftButton) + dragStartPosition = event->pos(); +} + +void MainWindow::mouseMoveEvent(QMouseEvent* event) +{ + if (!(event->buttons() & Qt::LeftButton)) + return; + if ((event->pos() - dragStartPosition).manhattanLength() < + QApplication::startDragDistance()) + return; + + performDrag(); +} + +void MainWindow::performDrag() +{ + // Assuming you have a method to get the currently selected molecule as a + // string in a specific format + QString moleculeData = /* method to get molecule data */; + if (moleculeData.isEmpty()) + return; + + auto* mimeData = new QMimeData; + mimeData->setText(moleculeData); // For demonstration, using plain text. + // Adjust based on actual data format. + + // Create a drag object + auto* drag = new QDrag(this); + drag->setMimeData(mimeData); + + // You can set an appropriate pixmap for the drag object if you like + // QPixmap pixmap(iconSize); + // QPainter painter(&pixmap); + // painter.drawPixmap(QPoint(), /* your pixmap here */); + // painter.end(); + // drag->setPixmap(pixmap); + + // Execute the drag operation + drag->exec(Qt::CopyAction | Qt::MoveAction); +} + void MainWindow::setupInterface() { // We take care of setting up the main interface here, along with any custom @@ -522,26 +568,28 @@ void MainWindow::dragEnterEvent(QDragEnterEvent* event) else event->ignore(); } - void MainWindow::dropEvent(QDropEvent* event) { if (event->mimeData()->hasUrls()) { - // TODO: check for ZIP, TAR, PY scripts (plugins) - foreach (const QUrl& url, event->mimeData()->urls()) { + QList urls = event->mimeData()->urls(); + for (const QUrl& url : urls) { if (url.isLocalFile()) { QString fileName = url.toLocalFile(); QFileInfo info(fileName); - QString extension = info.completeSuffix(); // e.g. .tar.gz or .pdb.gz - if (extension == "py") + // Distinguish Python scripts for special handling + if (info.suffix().compare("py", Qt::CaseInsensitive) == 0) { addScript(fileName); - else + } else { + openFile(fileName); + } } } event->acceptProposedAction(); - } else + } else { event->ignore(); + } } void MainWindow::moleculeReady(int) @@ -577,7 +625,7 @@ void setDefaultViews(MultiViewWidget* viewWidget) bool anyPluginTrue = false; // load plugins normally, if all non-ignore are false. // restore the default behavior - for (auto plugin : sceneModel->scenePlugins()) { + for (ScenePlugin* plugin : sceneModel->scenePlugins()) { QString settingsKey("MainWindow/" + plugin->objectName()); bool enabled = settings.value(settingsKey, plugin->isEnabled()).toBool(); if (plugin->defaultBehavior() != ScenePlugin::DefaultBehavior::Ignore && @@ -1497,6 +1545,9 @@ bool MainWindow::saveFileAs(bool async) QFileDialog saveDialog(this, tr("Save chemical file"), dir, filter); saveDialog.setAcceptMode(QFileDialog::AcceptSave); saveDialog.exec(); + if (saveDialog.selectedFiles().isEmpty()) // user cancel + return false; + QString fileName = saveDialog.selectedFiles().first(); if (fileName.isEmpty()) // user cancel @@ -1976,6 +2027,46 @@ void MainWindow::buildMenu() m_menuBuilder->addAction(path, action, 960); m_fileToolBar->addAction(action); connect(action, SIGNAL(triggered()), SLOT(saveFileAs())); + // Initialize autosave feature + m_autosaveInterval = 5; // Autosave interval in minutes + m_autosaveTimer = new QTimer(this); + connect(m_autosaveTimer, &QTimer::timeout, this, + &MainWindow::autosaveDocument); + m_autosaveTimer->start(m_autosaveInterval * + 60000); // Convert minutes to milliseconds + + void MainWindow::autosaveDocument() + { + if (!m_molecule || !m_moleculeDirty) { + return; // No molecule loaded or no changes made since the last save. + } + + QString autosaveDirPath = + QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + + "/autosave"; + QDir autosaveDir(autosaveDirPath); + if (!autosaveDir.exists()) { + autosaveDir.mkpath("."); + } + + // Construct autosave file name + QString autosaveFilename; + if (m_molecule->hasData("fileName")) { + QFileInfo fileInfo(m_molecule->data("fileName").toString().c_str()); + autosaveFilename = fileInfo.baseName() + "_autosave.cjson"; + } else { + autosaveFilename = "unsaved_autosave.cjson"; + } + QString autosaveFilePath = autosaveDirPath + "/" + autosaveFilename; + + // Use CJSON format for autosaving + Io::CjsonFormat writer; + if (!writer.writeFile(autosaveFilePath, *m_molecule)) { + qWarning() << "Failed to autosave the document to" << autosaveFilePath; + } else { + qDebug() << "Document autosaved to" << autosaveFilePath; + } + } // Export action for menu QStringList exportPath = path; @@ -2632,4 +2723,4 @@ bool MainWindow::handleCommand(const QString& command, return false; } -} // End of Avogadro namespace +} // End of Avogadro namespace \ No newline at end of file diff --git a/avogadro/mainwindow.h b/avogadro/mainwindow.h index 3dadf613..26d633ba 100644 --- a/avogadro/mainwindow.h +++ b/avogadro/mainwindow.h @@ -6,6 +6,8 @@ #ifndef AVOGADRO_MAINWINDOW_H #define AVOGADRO_MAINWINDOW_H +#include +#include #include #include #include @@ -69,7 +71,7 @@ class MainWindow : public QMainWindow public slots: void setMolecule(Avogadro::QtGui::Molecule* molecule); - + void autosaveDocument(); // line to declare the autosave slot /** * Update internal state to reflect that the molecule has been modified. */ @@ -161,6 +163,9 @@ public slots: void moleculeChanged(QtGui::Molecule* molecue); protected: + void mousePressEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void closeEvent(QCloseEvent* event); // Handle drag and drop -- accept files dragged on the window @@ -391,6 +396,7 @@ private slots: void setProjectionPerspective(); private: + QPoint dragStartPosition; // To store the start position of a drag operation QtGui::Molecule* m_molecule; QtGui::RWMolecule* m_rwMolecule; QtGui::MoleculeModel* m_moleculeModel; @@ -398,7 +404,8 @@ private slots: QtGui::ScenePlugin* m_activeScenePlugin; bool m_queuedFilesStarted; QStringList m_queuedFiles; - + QTimer* m_autosaveTimer; // for the autosave timer + int m_autosaveInterval; // for autosave interval in minutes QStringList m_recentFiles; QList m_actionRecentFiles; @@ -478,7 +485,7 @@ private slots: * Initialize the tool plugins. */ void buildTools(); - + void performDrag(); /** * Convenience function that converts a file extension to a wildcard * expression, e.g. "out" to "*.out". This method also checks for "extensions" @@ -502,4 +509,4 @@ private slots: } // End Avogadro namespace -#endif +#endif \ No newline at end of file diff --git a/i18n/de.po b/i18n/de.po index df10d813..1d561798 100644 --- a/i18n/de.po +++ b/i18n/de.po @@ -14,7 +14,7 @@ msgstr "" "Project-Id-Version: _avogadro-de\n" "Report-Msgid-Bugs-To: avogadro-devel@lists.sourceforge.net\n" "POT-Creation-Date: 2023-12-11 21:00+0000\n" -"PO-Revision-Date: 2024-01-23 16:01+0000\n" +"PO-Revision-Date: 2024-02-19 19:02+0000\n" "Last-Translator: Norwid Behrnd \n" "Language-Team: German \n" @@ -23,7 +23,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.4-dev\n" +"X-Generator: Weblate 5.5-dev\n" "X-Launchpad-Export-Date: 2018-04-13 16:02+0000\n" #. i18n: file: aboutdialog.ui:62 @@ -591,8 +591,9 @@ msgstr "Dateien können nicht geöffnet werden" #. i18n: file: mainwindow.ui:49 #. i18n: ectx: property (title), widget (QMenu, menuBuild) #: menubuilder.cpp:79:45 +#, fuzzy msgid "&Build" -msgstr "&Erstellen" +msgstr "S&truktur" #. i18n: file: mainwindow.ui:71 #. i18n: ectx: property (title), widget (QMenu, menuSelect)