From cef0aac4c47489103fe196ec335110c840364b8a Mon Sep 17 00:00:00 2001 From: Magne Sjaastad Date: Wed, 21 Oct 2026 08:48:47 +0200 Subject: [PATCH 1/2] #13360: Integrate project version restore tool into GUI Add a GUI feature for restoring previous versions of a project file from its backup database, replacing the need for the external standalone tool. - RiaProjectBackupTools: add extractVersionsToFolder() to read all stored revisions from a project's .rspdb backup database and write each one to a timestamped .rsp file in a destination folder. - RicRestoreProjectVersionsUi: dialog for selecting the project file and export folder, defaulting to the currently open project. Adds a Recent Project Files dropdown (backed by RiuRecentFileActionProvider) that quickly populates the project file and export folder fields. - RicRestoreProjectVersionsFeature: new command feature wired into the File menu as 'Restore Previous Project Versions...', using the OpenFolder icon and placed after a separator below Save Project / Save Project As. - RiuRecentFileActionProvider: add recentProjectFiles() to expose the list of recently used project files. - Added '...' suffix to Save Project As, consistent with other dialog-launching actions. --- .../Tools/RiaProjectBackupTools.cpp | 96 ++++++++++++ .../Application/Tools/RiaProjectBackupTools.h | 5 + .../CMakeLists_files.cmake | 2 + .../RicRestoreProjectVersionsFeature.cpp | 104 +++++++++++++ .../RicRestoreProjectVersionsFeature.h | 33 +++++ .../RicRestoreProjectVersionsUi.cpp | 140 ++++++++++++++++++ .../RicRestoreProjectVersionsUi.h | 50 +++++++ .../RicSaveProjectAsFeature.cpp | 2 +- .../UserInterface/RiuMenuBarBuildTools.cpp | 2 + .../RiuRecentFileActionProvider.cpp | 18 +++ .../RiuRecentFileActionProvider.h | 4 +- 11 files changed, 454 insertions(+), 2 deletions(-) create mode 100644 ApplicationLibCode/Commands/ApplicationCommands/RicRestoreProjectVersionsFeature.cpp create mode 100644 ApplicationLibCode/Commands/ApplicationCommands/RicRestoreProjectVersionsFeature.h create mode 100644 ApplicationLibCode/Commands/ApplicationCommands/RicRestoreProjectVersionsUi.cpp create mode 100644 ApplicationLibCode/Commands/ApplicationCommands/RicRestoreProjectVersionsUi.h diff --git a/ApplicationLibCode/Application/Tools/RiaProjectBackupTools.cpp b/ApplicationLibCode/Application/Tools/RiaProjectBackupTools.cpp index 747bb099520..47b740bed8c 100644 --- a/ApplicationLibCode/Application/Tools/RiaProjectBackupTools.cpp +++ b/ApplicationLibCode/Application/Tools/RiaProjectBackupTools.cpp @@ -20,6 +20,10 @@ #include "RiaLogging.h" #include +#include +#include +#include +#include #include #include #include @@ -137,4 +141,96 @@ bool appendTextToDatabase( const QString& databaseFilePath, int maximumRecordCou return true; } +//-------------------------------------------------------------------------------------------------- +/// Restore all previous revisions of a project file stored in the backup database to the destination folder. +/// Each revision is written to a separate file named by its timestamp. +//-------------------------------------------------------------------------------------------------- +bool extractVersionsToFolder( const QString& databaseFilePath, const QString& destinationFolder, int* numberOfFilesWritten, QString* errorMessage ) +{ + if ( numberOfFilesWritten ) *numberOfFilesWritten = 0; + + auto setError = [errorMessage]( const QString& message ) + { + if ( errorMessage ) *errorMessage = message; + RiaLogging::error( message.toStdString() ); + }; + + if ( !QFile::exists( databaseFilePath ) ) + { + setError( "Backup database file does not exist: " + databaseFilePath ); + return false; + } + + QDir dir; + if ( !dir.mkpath( destinationFolder ) ) + { + setError( "Not able to create destination folder: " + destinationFolder ); + return false; + } + + const QString databaseType = "QSQLITE"; + if ( !QSqlDatabase::isDriverAvailable( databaseType ) ) + { + setError( "sqlite database driver is not available." ); + return false; + } + + // Use a dedicated named connection to avoid interfering with the default connection used when writing backups. + const QString connectionName = "RiaProjectBackupToolsRestoreConnection"; + + bool success = true; + { + QSqlDatabase db = QSqlDatabase::addDatabase( databaseType, connectionName ); + db.setDatabaseName( databaseFilePath ); + + if ( !db.open() ) + { + setError( "Error opening backup database: " + db.lastError().text() ); + QSqlDatabase::removeDatabase( connectionName ); + return false; + } + + QSqlQuery query( db ); + query.prepare( "SELECT timestamp, content FROM file_versions ORDER BY timestamp ASC" ); + if ( !query.exec() ) + { + setError( "Error retrieving content from backup database: " + query.lastError().text() ); + success = false; + } + else + { + const QString dateFormat = "yyyy-MM-dd_hh-mm-ss"; + int fileCount = 0; + + while ( query.next() ) + { + QDateTime timestamp = query.value( 0 ).toDateTime(); + QString projectContent = query.value( 1 ).toString(); + + QString filename = destinationFolder + "/" + timestamp.toString( dateFormat ) + ".rsp"; + QFile file( filename ); + if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) + { + setError( "Error opening file for writing: " + filename ); + success = false; + break; + } + + QTextStream out( &file ); + out << projectContent; + file.close(); + + fileCount++; + } + + if ( numberOfFilesWritten ) *numberOfFilesWritten = fileCount; + } + + db.close(); + } + QSqlDatabase::removeDatabase( connectionName ); + + return success; +} + } // namespace RiaProjectBackupTools diff --git a/ApplicationLibCode/Application/Tools/RiaProjectBackupTools.h b/ApplicationLibCode/Application/Tools/RiaProjectBackupTools.h index 658bb6b06b2..8b43ee91554 100644 --- a/ApplicationLibCode/Application/Tools/RiaProjectBackupTools.h +++ b/ApplicationLibCode/Application/Tools/RiaProjectBackupTools.h @@ -26,4 +26,9 @@ namespace RiaProjectBackupTools { bool appendTextToDatabase( const QString& databaseFilePath, int maximumRecordCount, const QString& content ); + +bool extractVersionsToFolder( const QString& databaseFilePath, + const QString& destinationFolder, + int* numberOfFilesWritten = nullptr, + QString* errorMessage = nullptr ); } // namespace RiaProjectBackupTools diff --git a/ApplicationLibCode/Commands/ApplicationCommands/CMakeLists_files.cmake b/ApplicationLibCode/Commands/ApplicationCommands/CMakeLists_files.cmake index 492c3823c42..308dd3dec17 100644 --- a/ApplicationLibCode/Commands/ApplicationCommands/CMakeLists_files.cmake +++ b/ApplicationLibCode/Commands/ApplicationCommands/CMakeLists_files.cmake @@ -19,6 +19,8 @@ set(SOURCE_GROUP_SOURCE_FILES ${CMAKE_CURRENT_LIST_DIR}/RicOpenInTextEditorFeature.cpp ${CMAKE_CURRENT_LIST_DIR}/RicShowMemoryReportFeature.cpp ${CMAKE_CURRENT_LIST_DIR}/RicSumoDataFeature.cpp + ${CMAKE_CURRENT_LIST_DIR}/RicRestoreProjectVersionsUi.cpp + ${CMAKE_CURRENT_LIST_DIR}/RicRestoreProjectVersionsFeature.cpp ) list(APPEND COMMAND_CODE_SOURCE_FILES ${SOURCE_GROUP_SOURCE_FILES}) diff --git a/ApplicationLibCode/Commands/ApplicationCommands/RicRestoreProjectVersionsFeature.cpp b/ApplicationLibCode/Commands/ApplicationCommands/RicRestoreProjectVersionsFeature.cpp new file mode 100644 index 00000000000..b9e5c9da071 --- /dev/null +++ b/ApplicationLibCode/Commands/ApplicationCommands/RicRestoreProjectVersionsFeature.cpp @@ -0,0 +1,104 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2026- Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RicRestoreProjectVersionsFeature.h" +#include "RicRestoreProjectVersionsUi.h" + +#include "RiaApplication.h" +#include "RiaProjectBackupTools.h" + +#include "RimProject.h" + +#include "RiuMainWindow.h" +#include "RiuMessageDialog.h" +#include "RiuRecentFileActionProvider.h" + +#include "cafPdmUiPropertyViewDialog.h" + +#include +#include +#include +#include +#include + +CAF_CMD_SOURCE_INIT( RicRestoreProjectVersionsFeature, "RicRestoreProjectVersionsFeature" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicRestoreProjectVersionsFeature::onActionTriggered( bool isChecked ) +{ + RicRestoreProjectVersionsUi featureUi; + + RimProject* project = RiaApplication::instance()->project(); + + QString projectFileName = ( project ) ? project->fileName() : QString(); + if ( projectFileName.isEmpty() ) + { + projectFileName = RiuRecentFileActionProvider::lastUsedProjectFileName(); + } + + if ( !projectFileName.isEmpty() ) + { + featureUi.setProjectFile( projectFileName ); + featureUi.setExportFolder( QFileInfo( projectFileName ).absolutePath() ); + } + + caf::PdmUiPropertyViewDialog propertyDialog( RiuMainWindow::instance(), + &featureUi, + "Restore Previous Project Versions", + "", + QDialogButtonBox::Ok | QDialogButtonBox::Cancel ); + propertyDialog.resize( QSize( 560, 260 ) ); + + if ( propertyDialog.exec() != QDialog::Accepted ) return; + + QString projectFile = featureUi.projectFile(); + QString exportFolder = featureUi.exportFolder(); + if ( projectFile.isEmpty() || exportFolder.isEmpty() ) return; + + QString databaseFilePath = projectFile + "db"; + if ( !QFile::exists( databaseFilePath ) ) + { + RiuMessageDialog::showError( RiuMainWindow::instance(), + "Restore Previous Project Versions", + "No backup database was found for the selected project file.\n\n" + databaseFilePath ); + return; + } + + int numberOfFilesWritten = 0; + QString errorMessage; + if ( !RiaProjectBackupTools::extractVersionsToFolder( databaseFilePath, exportFolder, &numberOfFilesWritten, &errorMessage ) ) + { + RiuMessageDialog::showError( RiuMainWindow::instance(), "Restore Previous Project Versions", errorMessage ); + return; + } + + QMessageBox::information( RiuMainWindow::instance(), + "Restore Previous Project Versions", + QString( "Restored %1 project version(s) to:\n%2" ).arg( numberOfFilesWritten ).arg( exportFolder ) ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicRestoreProjectVersionsFeature::setupActionLook( QAction* actionToSetup ) +{ + actionToSetup->setText( "Restore Previous Project Versions..." ); + actionToSetup->setIcon( QIcon( ":/OpenFolder.svg" ) ); +} diff --git a/ApplicationLibCode/Commands/ApplicationCommands/RicRestoreProjectVersionsFeature.h b/ApplicationLibCode/Commands/ApplicationCommands/RicRestoreProjectVersionsFeature.h new file mode 100644 index 00000000000..41be9501008 --- /dev/null +++ b/ApplicationLibCode/Commands/ApplicationCommands/RicRestoreProjectVersionsFeature.h @@ -0,0 +1,33 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2026- Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "cafCmdFeature.h" + +//================================================================================================== +/// +//================================================================================================== +class RicRestoreProjectVersionsFeature : public caf::CmdFeature +{ + CAF_CMD_HEADER_INIT; + +protected: + void onActionTriggered( bool isChecked ) override; + void setupActionLook( QAction* actionToSetup ) override; +}; diff --git a/ApplicationLibCode/Commands/ApplicationCommands/RicRestoreProjectVersionsUi.cpp b/ApplicationLibCode/Commands/ApplicationCommands/RicRestoreProjectVersionsUi.cpp new file mode 100644 index 00000000000..98ef4b3fcba --- /dev/null +++ b/ApplicationLibCode/Commands/ApplicationCommands/RicRestoreProjectVersionsUi.cpp @@ -0,0 +1,140 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2026- Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RicRestoreProjectVersionsUi.h" + +#include "RiuRecentFileActionProvider.h" + +#include "cafPdmUiComboBoxEditor.h" +#include "cafPdmUiFilePathEditor.h" +#include "cafPdmUiOrdering.h" + +#include + +CAF_PDM_SOURCE_INIT( RicRestoreProjectVersionsUi, "RicRestoreProjectVersionsUi" ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RicRestoreProjectVersionsUi::RicRestoreProjectVersionsUi() +{ + CAF_PDM_InitObject( "Restore Project Versions" ); + + CAF_PDM_InitFieldNoDefault( &m_recentProjectFiles, "RecentProjectFiles", "Recent Project Files" ); + m_recentProjectFiles.uiCapability()->setUiEditorTypeName( caf::PdmUiComboBoxEditor::uiEditorTypeName() ); + + CAF_PDM_InitFieldNoDefault( &m_projectFile, "ProjectFile", "Project File" ); + m_projectFile.uiCapability()->setUiEditorTypeName( caf::PdmUiFilePathEditor::uiEditorTypeName() ); + m_projectFile.uiCapability()->setAttribute( caf::PdmUiFilePathEditor::Keys::FILE_SELECTION_FILTER, + QString( "ResInsight project (*.rsp *.rip);;All files (*.*)" ) ); + + CAF_PDM_InitFieldNoDefault( &m_exportFolder, "ExportFolder", "Export Folder" ); + m_exportFolder.uiCapability()->setUiEditorTypeName( caf::PdmUiFilePathEditor::uiEditorTypeName() ); + m_exportFolder.uiCapability()->setAttribute( caf::PdmUiFilePathEditor::Keys::SELECT_DIRECTORY, true ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RicRestoreProjectVersionsUi::projectFile() const +{ + return m_projectFile().path(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QString RicRestoreProjectVersionsUi::exportFolder() const +{ + return m_exportFolder().path(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicRestoreProjectVersionsUi::setProjectFile( const QString& filePath ) +{ + m_projectFile = caf::FilePath( filePath ); + + // Keep the recent files combo box in sync so it reflects the currently selected project file + // instead of appearing empty when a default/current project file is set programmatically. + QStringList recentFiles = RiuRecentFileActionProvider::recentProjectFiles(); + m_recentProjectFiles = recentFiles.contains( filePath ) ? filePath : QString(); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicRestoreProjectVersionsUi::setExportFolder( const QString& folderPath ) +{ + m_exportFolder = caf::FilePath( folderPath ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +QList RicRestoreProjectVersionsUi::calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) +{ + QList options; + + if ( fieldNeedingOptions == &m_recentProjectFiles ) + { + for ( const QString& recentFile : RiuRecentFileActionProvider::recentProjectFiles() ) + { + QString uiText = QFileInfo( recentFile ).fileName() + " (" + recentFile + ")"; + options.push_back( caf::PdmOptionItemInfo( uiText, recentFile ) ); + } + } + + return options; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicRestoreProjectVersionsUi::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) +{ + if ( changedField == &m_recentProjectFiles ) + { + QString selectedFile = m_recentProjectFiles(); + if ( !selectedFile.isEmpty() ) + { + setProjectFile( selectedFile ); + setExportFolder( QFileInfo( selectedFile ).absolutePath() ); + } + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RicRestoreProjectVersionsUi::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) +{ + bool hasRecentFiles = !RiuRecentFileActionProvider::recentProjectFiles().isEmpty(); + m_recentProjectFiles.uiCapability()->setUiHidden( !hasRecentFiles ); + + auto infoGroup = uiOrdering.addNewGroup( "Information" ); + infoGroup->addNewLabel( "Restores previous revisions of a ResInsight project from its backup history. Select the " + "project file to restore from and an export folder, and each stored revision will be " + "written as a separate project file." ); + + auto selectionGroup = uiOrdering.addNewGroup( "Project Selection" ); + selectionGroup->add( &m_recentProjectFiles ); + selectionGroup->add( &m_projectFile ); + selectionGroup->add( &m_exportFolder ); +} diff --git a/ApplicationLibCode/Commands/ApplicationCommands/RicRestoreProjectVersionsUi.h b/ApplicationLibCode/Commands/ApplicationCommands/RicRestoreProjectVersionsUi.h new file mode 100644 index 00000000000..7b90eb64233 --- /dev/null +++ b/ApplicationLibCode/Commands/ApplicationCommands/RicRestoreProjectVersionsUi.h @@ -0,0 +1,50 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2026- Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "cafFilePath.h" +#include "cafPdmField.h" +#include "cafPdmObject.h" + +//================================================================================================== +/// +//================================================================================================== +class RicRestoreProjectVersionsUi : public caf::PdmObject +{ + CAF_PDM_HEADER_INIT; + +public: + RicRestoreProjectVersionsUi(); + + QString projectFile() const; + QString exportFolder() const; + + void setProjectFile( const QString& filePath ); + void setExportFolder( const QString& folderPath ); + +private: + QList calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override; + void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override; + void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override; + +private: + caf::PdmField m_recentProjectFiles; + caf::PdmField m_projectFile; + caf::PdmField m_exportFolder; +}; diff --git a/ApplicationLibCode/Commands/ApplicationCommands/RicSaveProjectAsFeature.cpp b/ApplicationLibCode/Commands/ApplicationCommands/RicSaveProjectAsFeature.cpp index 308356effef..0d1d9f044bd 100644 --- a/ApplicationLibCode/Commands/ApplicationCommands/RicSaveProjectAsFeature.cpp +++ b/ApplicationLibCode/Commands/ApplicationCommands/RicSaveProjectAsFeature.cpp @@ -80,7 +80,7 @@ void RicSaveProjectAsFeature::onActionTriggered( bool isChecked ) //-------------------------------------------------------------------------------------------------- void RicSaveProjectAsFeature::setupActionLook( QAction* actionToSetup ) { - actionToSetup->setText( "Save Project &As" ); + actionToSetup->setText( "Save Project &As..." ); actionToSetup->setIcon( QIcon( ":/SaveAs.svg" ) ); applyShortcutWithHintToAction( actionToSetup, QKeySequence::SaveAs ); diff --git a/ApplicationLibCode/UserInterface/RiuMenuBarBuildTools.cpp b/ApplicationLibCode/UserInterface/RiuMenuBarBuildTools.cpp index 8a804b82cb0..9eae53844e5 100644 --- a/ApplicationLibCode/UserInterface/RiuMenuBarBuildTools.cpp +++ b/ApplicationLibCode/UserInterface/RiuMenuBarBuildTools.cpp @@ -192,6 +192,8 @@ void RiuMenuBarBuildTools::addSaveProjectActions( QMenu* menu ) menu->addAction( cmdFeatureMgr->action( "RicSaveProjectFeature" ) ); menu->addAction( cmdFeatureMgr->action( "RicSaveProjectAsFeature" ) ); + menu->addSeparator(); + menu->addAction( cmdFeatureMgr->action( "RicRestoreProjectVersionsFeature" ) ); } //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/UserInterface/RiuRecentFileActionProvider.cpp b/ApplicationLibCode/UserInterface/RiuRecentFileActionProvider.cpp index db85014f916..bfec92f63b7 100644 --- a/ApplicationLibCode/UserInterface/RiuRecentFileActionProvider.cpp +++ b/ApplicationLibCode/UserInterface/RiuRecentFileActionProvider.cpp @@ -88,6 +88,24 @@ QString RiuRecentFileActionProvider::lastUsedProjectFileName() return {}; } +//-------------------------------------------------------------------------------------------------- +/// Returns all recently used project files, most recently used first. +//-------------------------------------------------------------------------------------------------- +QStringList RiuRecentFileActionProvider::recentProjectFiles() +{ + QStringList projectFiles; + + const QStringList files = RiaStringListSerializer( registryKey() ).textStrings(); + for ( const QString& file : files ) + { + if ( file.endsWith( ".rsp", Qt::CaseInsensitive ) || file.endsWith( ".rip", Qt::CaseInsensitive ) ) + { + projectFiles.push_back( file ); + } + } + return projectFiles; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/UserInterface/RiuRecentFileActionProvider.h b/ApplicationLibCode/UserInterface/RiuRecentFileActionProvider.h index 0506c8e4e62..49902e45d4c 100644 --- a/ApplicationLibCode/UserInterface/RiuRecentFileActionProvider.h +++ b/ApplicationLibCode/UserInterface/RiuRecentFileActionProvider.h @@ -20,6 +20,7 @@ #include #include +#include #include @@ -41,7 +42,8 @@ class RiuRecentFileActionProvider : public QObject void addFileName( const QString& fileName ); std::vector actions() const; - static QString lastUsedProjectFileName(); + static QString lastUsedProjectFileName(); + static QStringList recentProjectFiles(); private slots: void slotOpenRecentFile(); From 89b4c2d1eb2f67af15a6098df7d1b04cfce55ec5 Mon Sep 17 00:00:00 2001 From: Magne Sjaastad Date: Wed, 21 Oct 2026 14:51:14 +0200 Subject: [PATCH 2/2] Remove standalone extract-projectfile-versions tool, superseded by GUI integration --- CMakeLists.txt | 5 - .../.clang-format | 78 ---------- .../CMakeLists.txt | 37 ----- .../extract-projectfile-versions/main.cpp | 135 ------------------ docs/agents/build.md | 2 - 5 files changed, 257 deletions(-) delete mode 100644 ThirdParty/extract-projectfile-versions/.clang-format delete mode 100644 ThirdParty/extract-projectfile-versions/CMakeLists.txt delete mode 100644 ThirdParty/extract-projectfile-versions/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d6f71adfd49..5006e82eb60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1285,11 +1285,6 @@ else(RESINSIGHT_USE_EXTERNAL_OCTAVE_PLUGINS) endif(OCTAVE_MKOCTFILE) endif(RESINSIGHT_USE_EXTERNAL_OCTAVE_PLUGINS) -add_subdirectory(ThirdParty/extract-projectfile-versions) -install(TARGETS extract-projectfile-versions - DESTINATION ${RESINSIGHT_INSTALL_FOLDER} -) - # ############################################################################## # Visual Studio : Create the ruleset file to be used by Static Code Analysis # https://stackoverflow.com/questions/75031903/how-to-enable-static-analysis-with-custom-ruleset-in-msvc-via-cmakelists-txt diff --git a/ThirdParty/extract-projectfile-versions/.clang-format b/ThirdParty/extract-projectfile-versions/.clang-format deleted file mode 100644 index 241093fc3eb..00000000000 --- a/ThirdParty/extract-projectfile-versions/.clang-format +++ /dev/null @@ -1,78 +0,0 @@ ---- -Language: Cpp -# BasedOnStyle: LLVM -AccessModifierOffset: -4 -AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: true -AlignConsecutiveDeclarations: true -AlignEscapedNewlinesLeft: true -AlignOperands: true -AlignTrailingComments: false -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortBlocksOnASingleLine: false -AllowShortCaseLabelsOnASingleLine: false -AllowShortIfStatementsOnASingleLine: true -AllowShortFunctionsOnASingleLine: InlineOnly -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: true -BinPackArguments: false -BinPackParameters: false -BreakBeforeBinaryOperators: None -BreakBeforeBraces: Allman -BreakBeforeTernaryOperators: true -BreakConstructorInitializersBeforeComma: true -BreakAfterJavaFieldAnnotations: false -BreakStringLiterals: true -ColumnLimit: 140 -CommentPragmas: "^ IWYU pragma:" -ConstructorInitializerAllOnOneLineOrOnePerLine: false -ConstructorInitializerIndentWidth: 4 -ContinuationIndentWidth: 4 -Cpp11BracedListStyle: true -DerivePointerAlignment: false -DisableFormat: false -ForEachMacros: [foreach, Q_FOREACH, BOOST_FOREACH] -IncludeCategories: - - Regex: '^"(llvm|llvm-c|clang|clang-c)/' - Priority: 2 - - Regex: '^(<|"(gtest|isl|json)/)' - Priority: 3 - - Regex: ".*" - Priority: 1 -IncludeIsMainRegex: "$" -IndentCaseLabels: true -IndentWidth: 4 -IndentWrappedFunctionNames: true -JavaScriptQuotes: Leave -KeepEmptyLinesAtTheStartOfBlocks: false -MacroBlockBegin: "" -MacroBlockEnd: "" -MaxEmptyLinesToKeep: 1 -NamespaceIndentation: Inner -PenaltyBreakAssignment: 13 -PenaltyBreakBeforeFirstCallParameter: 10000 -PenaltyBreakComment: 20 -PenaltyBreakFirstLessLess: 12 -PenaltyBreakString: 100 -PenaltyExcessCharacter: 5 -PenaltyReturnTypeOnItsOwnLine: 30 -PointerAlignment: Left -ReflowComments: true -SortIncludes: true -SpaceAfterCStyleCast: false -SpaceAfterTemplateKeyword: true -SpaceBeforeAssignmentOperators: true -SpaceBeforeParens: ControlStatements -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 -SpacesInAngles: false -SpacesInContainerLiterals: false -SpacesInCStyleCastParentheses: false -SpacesInParentheses: true -SpacesInSquareBrackets: false -Standard: c++20 -TabWidth: 4 -UseTab: Never diff --git a/ThirdParty/extract-projectfile-versions/CMakeLists.txt b/ThirdParty/extract-projectfile-versions/CMakeLists.txt deleted file mode 100644 index 09eb3dd584d..00000000000 --- a/ThirdParty/extract-projectfile-versions/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -cmake_minimum_required(VERSION 3.15) -project(extract-projectfile-versions) - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_INCLUDE_CURRENT_DIR ON) -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) -set(CMAKE_AUTOUIC ON) - -find_package(Qt6 COMPONENTS Core Sql REQUIRED) - -add_executable(extract-projectfile-versions main.cpp) - -target_link_libraries(extract-projectfile-versions Qt6::Core Qt6::Sql) - -# This standalone developer tool must not share the output directory with the -# application. The POST_BUILD step below copies Qt6Core.dll and Qt6Sql.dll next -# to the executable, and the build root has ResInsight.exe running out of it -# during Python code generation with those same DLLs loaded. Overwriting a -# mapped DLL is a sharing violation on Windows, and nothing orders the two build -# edges relative to each other. -set_target_properties( - extract-projectfile-versions - PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} -) - -if(MSVC) - add_custom_command( - TARGET extract-projectfile-versions - POST_BUILD - COMMAND - ${CMAKE_COMMAND} -E copy_if_different - $ - $ - COMMAND_EXPAND_LISTS - ) -endif(MSVC) diff --git a/ThirdParty/extract-projectfile-versions/main.cpp b/ThirdParty/extract-projectfile-versions/main.cpp deleted file mode 100644 index 88d5b56ed56..00000000000 --- a/ThirdParty/extract-projectfile-versions/main.cpp +++ /dev/null @@ -1,135 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -std::vector> getAllContent() -{ - std::vector> content; - - QSqlQuery query; - - query.prepare( "SELECT timestamp, content FROM file_versions" ); - if ( !query.exec() ) - { - qDebug() << "Error retrieving content:" << query.lastError().text(); - return content; - } - - while ( query.next() ) - { - QDateTime timestamp = query.value( 0 ).toDateTime(); - QString projectContent = query.value( 1 ).toString(); - content.push_back( std::make_pair( timestamp, projectContent ) ); - } - - return content; -} - -//-------------------------------------------------------------------------------------------------- -/// -//-------------------------------------------------------------------------------------------------- -void extractVersionsToFolder( const QString& destinationFolder ) -{ - auto allContent = getAllContent(); - - for ( const auto& [timestamp, content] : allContent ) - { - const QString dateFormat = "yyyy-MM-dd_hh-mm-ss"; - QString filename = destinationFolder + "/" + timestamp.toString( dateFormat ) + ".rsp"; - QFile file( filename ); - if ( !file.open( QIODevice::WriteOnly | QIODevice::Text ) ) - { - qCritical() << "Error opening file for writing:" << filename; - return; - } - - QTextStream out( &file ); - out << content; - file.close(); - } -} - -int main( int argc, char* argv[] ) -{ - QCoreApplication app( argc, argv ); - QCoreApplication::setApplicationName( "extract-projectfile-versions" ); - QCoreApplication::setApplicationVersion( "1.0" ); - - QCommandLineParser parser; - parser.setApplicationDescription( - "extract-projectfile-versions is used to restore previous revisions of a ResInsight project from a project file database." ); - parser.addHelpOption(); - parser.addVersionOption(); - - parser.addPositionalArgument( "file", "ResInsight project file database (*.rspdb)" ); - parser.addPositionalArgument( "outputfolder", "Output folder all project files" ); - - parser.process( app ); - - const QStringList args = parser.positionalArguments(); - if ( args.size() != 2 ) - { - qCritical() << "Failed to detect two input arguments."; - parser.showHelp( 1 ); - return 1; - } - - QString databaseFilePath = args.front(); - if ( !QFile::exists( databaseFilePath ) ) - { - qCritical() << "Database file does not exist:" << databaseFilePath; - return 1; - } - - QString destinationDir = args[1]; - QDir dir; - if ( !dir.mkpath( destinationDir ) ) - { - qCritical() << "Not able to create destination folder : " << destinationDir; - return 1; - } - - const QString databaseType = "QSQLITE"; - - if ( !QSqlDatabase::isDriverAvailable( databaseType ) ) - { - qInfo() << "sqlite database is not available."; - return 1; - } - - // Try to open the SQLITE database - QSqlDatabase db = QSqlDatabase::database(); - if ( !db.isValid() || !db.open() ) - { - qInfo() << "Adding database"; - - // Add the SQLITE database, and it it required to do this once per session. The database will be available during the lifetime - // of the application, and can be accessed using QSqlDatabase::database() - db = QSqlDatabase::addDatabase( databaseType ); - } - - QFileInfo fileInfo( databaseFilePath ); - auto dbPath = fileInfo.absoluteFilePath(); - db.setDatabaseName( dbPath ); - - if ( !db.open() ) - { - QString txt = "Error opening database:" + db.lastError().text(); - qCritical() << txt; - return 1; - } - - extractVersionsToFolder( destinationDir ); - - return 1; -} diff --git a/docs/agents/build.md b/docs/agents/build.md index 0ed18c1ae58..1693330bb03 100644 --- a/docs/agents/build.md +++ b/docs/agents/build.md @@ -74,7 +74,6 @@ ninja -C build # Build specific targets ninja -C build ResInsight -ninja -C build extract-projectfile-versions ``` ### Windows (Visual Studio 2022) @@ -96,7 +95,6 @@ cmake --build --preset x64-relwithdebinfo # Build specific targets cmake --build --preset x64-relwithdebinfo --target ResInsight -cmake --build --preset x64-relwithdebinfo --target extract-projectfile-versions ``` **Important — use `cmake --build`, not `ninja` directly, when sharing `build/` with Visual Studio:**