Core: Remember export information on a per document basis (#22067)

* Use a new struct ExportInfo to hold export information on a per document basis
This commit is contained in:
theo-vt
2025-07-15 22:43:57 -04:00
committed by GitHub
parent 7e03fc6620
commit f2d0a69e01
5 changed files with 87 additions and 47 deletions

View File

@@ -1189,6 +1189,14 @@ Document::ExportStatus Document::isExporting(const DocumentObject* obj) const
}
return Document::NotExporting;
}
ExportInfo Document::exportInfo() const
{
return d->exportInfo;
}
void Document::setExportInfo(const ExportInfo& info)
{
d->exportInfo = info;
}
void Document::exportObjects(const std::vector<DocumentObject*>& obj, std::ostream& out)
{

View File

@@ -33,6 +33,7 @@
#include "PropertyContainer.h"
#include "PropertyLinks.h"
#include "PropertyStandard.h"
#include "ExportInfo.h"
#include <map>
#include <vector>
@@ -246,6 +247,8 @@ public:
Exporting,
};
ExportStatus isExporting(const DocumentObject* obj) const;
ExportInfo exportInfo() const;
void setExportInfo(const ExportInfo& info);
void exportObjects(const std::vector<DocumentObject*>&, std::ostream&);
void exportGraphviz(std::ostream&) const;
std::vector<DocumentObject*> importObjects(Base::XMLReader& reader);

26
src/App/ExportInfo.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef SRC_APP_EXPORTINFO_H_
#define SRC_APP_EXPORTINFO_H_
#include "DocumentObject.h"
#include <string>
namespace App
{
/// Struct that holds information about the last
/// export so that the user does not have to reenter
/// them on each export. The export command either uses
/// the target document's export info
struct ExportInfo {
std::string location {};
std::string filename {};
std::string filter {};
bool generatedName {false};
App::DocumentObject* object {nullptr};
};
}
#endif

View File

@@ -42,6 +42,7 @@
#include <App/DocumentObject.h>
#include <App/DocumentObserver.h>
#include <App/StringHasher.h>
#include <App/ExportInfo.h>
#include <Base/UniqueNameManager.h>
// using VertexProperty = boost::property<boost::vertex_root_t, DocumentObject* >;
@@ -95,6 +96,7 @@ struct DocumentP
mutable HasherMap hashers;
std::multimap<const App::DocumentObject*, std::unique_ptr<App::DocumentObjectExecReturn>>
_RecomputeLog;
ExportInfo exportInfo;
StringHasherRef Hasher {new StringHasher};

View File

@@ -400,12 +400,6 @@ void StdCmdExport::activated(int iMsg)
{
Q_UNUSED(iMsg);
static QString lastExportFullPath = QString();
static App::DocumentObject* lastExportedObject = nullptr;
static bool lastExportUsedGeneratedFilename = true;
static QString lastExportFilterUsed = QString();
static Document* lastActiveDocument;
auto selection = Gui::Selection().getObjectsOfType(App::DocumentObject::getClassTypeId());
if (selection.empty()) {
QMessageBox::warning(Gui::getMainWindow(),
@@ -414,77 +408,82 @@ void StdCmdExport::activated(int iMsg)
return;
}
App::DocumentObject* toExport = selection.front();
App::Document* doc = toExport->getDocument();
App::ExportInfo exportInfo = doc->exportInfo();
bool filenameWasGenerated = false;
// fill the list of registered suffixes
QStringList filterList;
std::map<std::string, std::string> filterMap = App::GetApplication().getExportFilters();
for (const auto &filter : filterMap) {
// ignore the project file format
if (filter.first.find("(*.FCStd)") == std::string::npos)
if (filter.first.find("(*.FCStd)") == std::string::npos) {
filterList << QString::fromStdString(filter.first);
}
}
QString formatList = filterList.join(QLatin1String(";;"));
Base::Reference<ParameterGrp> hPath =
App::GetApplication().GetUserParameter().GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("General");
QString selectedFilter = QString::fromStdString(hPath->GetASCII("FileExportFilter"));
if (!lastExportFilterUsed.isEmpty())
selectedFilter = lastExportFilterUsed;
QString selectedFilter;
if (!exportInfo.filter.empty()) {
selectedFilter = QString::fromStdString(exportInfo.filter);
} else {
selectedFilter = QString::fromStdString(hPath->GetASCII("FileExportFilter"));
}
// Create a default filename for the export
// * If this is the first export this session default, generate a new default.
// * If this is a repeated export during the same session and file:
// * If this is the first export (the ExportInfo object' fields are empty)
// * If this is a repeated export using a filled ExportInfo object:
// * If the user accepted the default filename last time, regenerate a new
// default, potentially updating the object label.
// * If not, default to their previously-set export filename.
// * If this is an export of a different object than last time
QString defaultFilename = lastExportFullPath;
bool filenameWasGenerated = false;
bool didActiveDocumentChange = lastActiveDocument != getActiveGuiDocument();
bool didExportedObjectChange = lastExportedObject != selection.front();
// We want to generate a new default name in four cases:
if (defaultFilename.isEmpty() || lastExportUsedGeneratedFilename || didActiveDocumentChange || didExportedObjectChange) {
QString defaultFilename;
if (exportInfo.filename.empty() || exportInfo.generatedName || exportInfo.object != toExport) {
// First, get the name and path of the current .FCStd file, if there is one:
QString docFilename = QString::fromUtf8(
App::GetApplication().getActiveDocument()->getFileName());
QString docFilename = QString::fromStdString(doc->getFileName());
// Find the default location for our exported file. Three possibilities:
QString defaultExportPath;
if (!lastExportFullPath.isEmpty()) {
QFileInfo fi(lastExportFullPath);
defaultExportPath = fi.path();
QString exportPath;
if (!exportInfo.filename.empty()) {
QFileInfo fi(QString::fromStdString(exportInfo.filename));
exportPath = fi.path();
}
else if (!docFilename.isEmpty()) {
QFileInfo fi(docFilename);
defaultExportPath = fi.path();
exportPath = fi.path();
}
else {
defaultExportPath = Gui::FileDialog::getWorkingDirectory();
exportPath = Gui::FileDialog::getWorkingDirectory();
}
if (lastExportUsedGeneratedFilename || didActiveDocumentChange || didExportedObjectChange) { /*<- static, true on first call*/
defaultFilename = defaultExportPath + QLatin1Char('/') + createDefaultExportBasename();
if (exportInfo.generatedName || exportInfo.object != toExport) { /*<- static, true on first call*/
defaultFilename = exportPath + QLatin1Char('/') + createDefaultExportBasename();
// Append the last extension used, if there is one.
if (!lastExportFullPath.isEmpty()) {
QFileInfo lastExportFile(lastExportFullPath);
if (!exportInfo.filename.empty()) {
QFileInfo lastExportFile(QString::fromStdString(exportInfo.filename));
if (!lastExportFile.suffix().isEmpty())
defaultFilename += QLatin1String(".") + lastExportFile.suffix();
}
filenameWasGenerated = true;
}
} else {
defaultFilename = QString::fromStdString(exportInfo.filename);
}
// Launch the file selection modal dialog
QString fileName = FileDialog::getSaveFileName(getMainWindow(),
// Launch the file selection modal dialog
QString filename = FileDialog::getSaveFileName(getMainWindow(),
QObject::tr("Export file"), defaultFilename, formatList, &selectedFilter);
if (!fileName.isEmpty()) {
if (!filename.isEmpty()) {
hPath->SetASCII("FileExportFilter", selectedFilter.toLatin1().constData());
lastExportFilterUsed = selectedFilter; // So we can select the same one next time
SelectModule::Dict dict = SelectModule::exportHandler(fileName, selectedFilter);
SelectModule::Dict dict = SelectModule::exportHandler(filename, selectedFilter);
// export the files with the associated modules
for (SelectModule::Dict::iterator it = dict.begin(); it != dict.end(); ++it) {
getGuiApplication()->exportTo(it.key().toUtf8(),
getActiveGuiDocument()->getDocument()->getName(),
doc->getName(),
it.value().toLatin1());
}
@@ -492,16 +491,18 @@ void StdCmdExport::activated(int iMsg)
// did, next time we can recreate it, which will update the object label if
// there is one.
QFileInfo defaultExportFI(defaultFilename);
QFileInfo thisExportFI(fileName);
if (filenameWasGenerated &&
thisExportFI.completeBaseName() == defaultExportFI.completeBaseName())
lastExportUsedGeneratedFilename = true;
else
lastExportUsedGeneratedFilename = false;
QFileInfo thisExportFI(filename);
if (filenameWasGenerated && thisExportFI.completeBaseName() != defaultExportFI.completeBaseName()) {
filenameWasGenerated = false;
}
lastExportFullPath = fileName;
lastActiveDocument = getActiveGuiDocument();
lastExportedObject = selection.front();
exportInfo.filename = filename.toStdString();
exportInfo.object = toExport;
exportInfo.filter = selectedFilter.toStdString();
exportInfo.generatedName = filenameWasGenerated;
doc->setExportInfo(exportInfo);
}
}