diff --git a/app/config/config.cpp b/app/config/config.cpp index f3d69f13d..32a55322e 100644 --- a/app/config/config.cpp +++ b/app/config/config.cpp @@ -66,7 +66,9 @@ void Config::SetDefaults() SetEntryInternal(QStringLiteral("DefaultStillLength"), NodeValue::kRational, QVariant::fromValue(rational(2))); SetEntryInternal(QStringLiteral("HoverFocus"), NodeValue::kBoolean, false); SetEntryInternal(QStringLiteral("AudioScrubbing"), NodeValue::kBoolean, true); + SetEntryInternal(QStringLiteral("AutorecoveryEnabled"), NodeValue::kBoolean, true); SetEntryInternal(QStringLiteral("AutorecoveryInterval"), NodeValue::kInt, 1); + SetEntryInternal(QStringLiteral("AutorecoveryMaximum"), NodeValue::kInt, 20); SetEntryInternal(QStringLiteral("DiskCacheSaveInterval"), NodeValue::kInt, 10000); SetEntryInternal(QStringLiteral("Language"), NodeValue::kText, QString()); SetEntryInternal(QStringLiteral("ScrollZooms"), NodeValue::kBoolean, false); diff --git a/app/core.cpp b/app/core.cpp index 71dece504..fa19a60ad 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -39,6 +39,7 @@ #include "common/xmlutils.h" #include "config/config.h" #include "dialog/about/about.h" +#include "dialog/autorecovery/autorecoverydialog.h" #include "dialog/export/export.h" #include "dialog/footagerelink/footagerelinkdialog.h" #include "dialog/sequence/sequence.h" @@ -164,6 +165,9 @@ void Core::Start() void Core::Stop() { + // Assume all projects have closed gracefully and no auto-recovery is necessary + QFile::remove(GetAutoRecoveryIndexFilename()); + // Save Config Config::Save(); @@ -667,6 +671,7 @@ void Core::StartGUI(bool full_screen) // Start autorecovery timer using the config value as its interval SetAutorecoveryInterval(Config::Current()["AutorecoveryInterval"].toInt()); + connect(&autorecovery_timer_, &QTimer::timeout, this, &Core::SaveAutorecovery); autorecovery_timer_.start(); // Load recently opened projects list @@ -687,7 +692,7 @@ void Core::StartGUI(bool full_screen) } } -void Core::SaveProjectInternal(Project* project) +void Core::SaveProjectInternal(Project* project, const QString& override_filename) { // Create save manager Task* psm; @@ -704,11 +709,19 @@ void Core::SaveProjectInternal(Project* project) #endif } else { psm = new ProjectSaveTask(project); + + if (!override_filename.isEmpty()) { + // Set override filename if provided + static_cast(psm)->SetOverrideFilename(override_filename); + } } TaskDialog* task_dialog = new TaskDialog(psm, tr("Save Project"), main_window_); - connect(task_dialog, &TaskDialog::TaskSucceeded, this, &Core::ProjectSaveSucceeded); + if (override_filename.isEmpty()) { + // Default behavior: set as not modified and push to top of "Open Recent" dialog + connect(task_dialog, &TaskDialog::TaskSucceeded, this, &Core::ProjectSaveSucceeded); + } task_dialog->open(); } @@ -743,12 +756,85 @@ Sequence *Core::GetSequenceToExport() return nullptr; } +QString Core::GetAutoRecoveryIndexFilename() +{ + return QDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(QStringLiteral("unrecovered")); +} + +QString Core::GetAutoRecoveryRoot() +{ + return QDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath(QStringLiteral("autorecovery")); +} + void Core::SaveAutorecovery() { - foreach (Project* p, open_projects_) { - if (!p->has_autorecovery_been_saved()) { - // FIXME: SAVE AN AUTORECOVERY PROJECT - p->set_autorecovery_saved(true); + if (Config::Current()[QStringLiteral("AutorecoveryEnabled")].toBool()) { + QStringList autorecoveries_saved; + + foreach (Project* p, open_projects_) { + if (!p->has_autorecovery_been_saved()) { + QDir project_autorecovery_dir(QDir(GetAutoRecoveryRoot()).filePath(p->GetUuid().toString())); + if (project_autorecovery_dir.mkpath(QStringLiteral("."))) { + QString this_autorecovery_path = project_autorecovery_dir.filePath(QStringLiteral("%1.ove").arg(QString::number(QDateTime::currentSecsSinceEpoch()))); + + SaveProjectInternal(p, this_autorecovery_path); + + p->set_autorecovery_saved(true); + + autorecoveries_saved.append(project_autorecovery_dir.absolutePath()); + + qDebug() << "Saved auto-recovery to:" << this_autorecovery_path; + + // Write human-readable real name so it's not just a UUID + { + QFile realname_file(project_autorecovery_dir.filePath(QStringLiteral("realname.txt"))); + realname_file.open(QFile::WriteOnly); + realname_file.write(p->pretty_filename().toUtf8()); + realname_file.close(); + } + + int64_t max_recoveries_per_file = Config::Current()[QStringLiteral("AutorecoveryMaximum")].toLongLong(); + + // Since we write an extra file, increment total allowed files by 1 + max_recoveries_per_file++; + + // Delete old entries + QStringList recovery_files = project_autorecovery_dir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name); + while (recovery_files.size() > max_recoveries_per_file) { + bool deleted = false; + for (int i=0; istatusBar()->showMessage(s); } +void Core::OpenRecoveryProject(const QString &filename) +{ + OpenProjectInternal(filename); +} + +void Core::CheckForAutoRecoveries() +{ + QFile autorecovery_index(GetAutoRecoveryIndexFilename()); + if (autorecovery_index.exists()) { + // Uh-oh, we have auto-recoveries to prompt + if (autorecovery_index.open(QFile::ReadOnly)) { + QStringList recovery_filenames = QString::fromUtf8(autorecovery_index.readAll()).split('\n'); + + AutoRecoveryDialog ard(tr("The following projects had unsaved changes when Olive " + "forcefully quit. Would you like to load them?"), + recovery_filenames, true, main_window_); + ard.exec(); + + autorecovery_index.close(); + + // Delete recovery index since we don't need it anymore + QFile::remove(GetAutoRecoveryIndexFilename()); + } else { + QMessageBox::critical(main_window_, tr("Auto-Recovery Error"), + tr("Found auto-recoveries but failed to load the auto-recovery index. " + "Auto-recover projects will have to be opened manually.\n\n" + "Your recoverable projects are still available at: %1").arg(GetAutoRecoveryRoot())); + } + } +} + +void Core::BrowseAutoRecoveries() +{ + QDir autorecovery_root(GetAutoRecoveryRoot()); + + // List all auto-recovery entries + QStringList entries = autorecovery_root.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + for (int i=0; i. + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + dialog/autorecovery/autorecoverydialog.h + dialog/autorecovery/autorecoverydialog.cpp + PARENT_SCOPE +) diff --git a/app/dialog/autorecovery/autorecoverydialog.cpp b/app/dialog/autorecovery/autorecoverydialog.cpp new file mode 100644 index 000000000..4b8cc1467 --- /dev/null +++ b/app/dialog/autorecovery/autorecoverydialog.cpp @@ -0,0 +1,134 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 Olive Team + + This program 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. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "autorecoverydialog.h" + +#include +#include +#include +#include +#include +#include + +#include "core.h" + +namespace olive { + +#define super QDialog + +AutoRecoveryDialog::AutoRecoveryDialog(const QString &message, const QStringList &recoveries, bool autocheck_latest, QWidget* parent) : + QDialog(parent) +{ + Init(message); + + PopulateTree(recoveries, autocheck_latest); +} + +void AutoRecoveryDialog::accept() +{ + foreach (QTreeWidgetItem* checkable, checkable_items_) { + if (checkable->checkState(0) == Qt::Checked) { + QString filename = checkable->data(0, kFilenameRole).toString(); + Core::instance()->OpenRecoveryProject(filename); + } + } + + super::accept(); +} + +void AutoRecoveryDialog::Init(const QString& header_text) +{ + QVBoxLayout* layout = new QVBoxLayout(this); + + setWindowTitle(tr("Auto-Recovery")); + + layout->addWidget(new QLabel(header_text)); + + tree_widget_ = new QTreeWidget(); + tree_widget_->setHeaderHidden(true); + layout->addWidget(tree_widget_); + + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + buttons->button(QDialogButtonBox::Ok)->setText(tr("Load")); + connect(buttons, &QDialogButtonBox::accepted, this, &AutoRecoveryDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, this, &AutoRecoveryDialog::reject); + layout->addWidget(buttons); +} + +void AutoRecoveryDialog::PopulateTree(const QStringList& recoveries, bool autocheck_latest) +{ + // Each entry in `recoveries` is a directory with 1+ recovery projects in it + foreach (const QString& recovery_folder, recoveries) { + QDir recovery_dir(recovery_folder); + + QString pretty_name; + { + // Retrieve pretty name + QFile pretty_name_file(recovery_dir.filePath(QStringLiteral("realname.txt"))); + if (pretty_name_file.open(QFile::ReadOnly)) { + // Read pretty name that we should have written in the autorecovery process + pretty_name = QString::fromUtf8(pretty_name_file.readAll()); + pretty_name_file.close(); + } + + if (pretty_name.isEmpty()) { + // Fallback to just the UUID. While it won't mean much to the user, it's better than nothing. + pretty_name = recovery_dir.dirName(); + } + } + + QTreeWidgetItem* top_level = new QTreeWidgetItem(tree_widget_); + top_level->setText(0, pretty_name); + + { + // Populate with recoveries + QStringList entries = recovery_dir.entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name | QDir::Reversed); + for (int i=0; isetText(0, entry_name); + entry_item->setData(0, kFilenameRole, recovery_dir.filePath(entry)); + + // Allow to be checked, auto-checking the first entry + entry_item->setCheckState(0, (autocheck_latest && top_level->childCount() == 1) ? Qt::Checked : Qt::Unchecked); + + checkable_items_.append(entry_item); + } + } + } + } +} + +} diff --git a/app/dialog/autorecovery/autorecoverydialog.h b/app/dialog/autorecovery/autorecoverydialog.h new file mode 100644 index 000000000..d38119b74 --- /dev/null +++ b/app/dialog/autorecovery/autorecoverydialog.h @@ -0,0 +1,57 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 Olive Team + + This program 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. + + This program 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 for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef AUTORECOVERYDIALOG_H +#define AUTORECOVERYDIALOG_H + +#include +#include + +#include "common/define.h" + +namespace olive { + +class AutoRecoveryDialog : public QDialog +{ + Q_OBJECT +public: + AutoRecoveryDialog(const QString& message, const QStringList& recoveries, bool autocheck_latest, QWidget* parent); + +public slots: + virtual void accept() override; + +private: + void Init(const QString &header_text); + + void PopulateTree(const QStringList &recoveries, bool autocheck); + + QTreeWidget* tree_widget_; + + QVector checkable_items_; + + enum DataRole { + kFilenameRole = Qt::UserRole + }; + +}; + +} + +#endif // AUTORECOVERYDIALOG_H diff --git a/app/dialog/preferences/tabs/preferencesgeneraltab.cpp b/app/dialog/preferences/tabs/preferencesgeneraltab.cpp index c26fa94bb..e732cca1a 100644 --- a/app/dialog/preferences/tabs/preferencesgeneraltab.cpp +++ b/app/dialog/preferences/tabs/preferencesgeneraltab.cpp @@ -21,6 +21,7 @@ #include "preferencesgeneraltab.h" #include +#include #include #include @@ -36,66 +37,116 @@ PreferencesGeneralTab::PreferencesGeneralTab() QVBoxLayout* layout = new QVBoxLayout(this); layout->setMargin(0); - QGridLayout* general_layout = new QGridLayout(); - layout->addLayout(general_layout); + { + QGroupBox* global_groupbox = new QGroupBox(tr("Locale")); + QGridLayout* global_layout = new QGridLayout(global_groupbox); + layout->addWidget(global_groupbox); - int row = 0; + int row = 0; - // General -> Language - general_layout->addWidget(new QLabel(tr("Language:")), row, 0); + // General -> Language + global_layout->addWidget(new QLabel(tr("Language:")), row, 0); - language_combobox_ = new QComboBox(); + language_combobox_ = new QComboBox(); - // Add default language (en-US) - QDir language_dir(QStringLiteral(":/ts")); - QStringList languages = language_dir.entryList(); - foreach (const QString& l, languages) { - AddLanguage(l); - } - - QString current_language = Config::Current()[QStringLiteral("Language")].toString(); - if (current_language.isEmpty()) { - // No configured language, use system language - current_language = QLocale::system().name(); - - // If we don't have a language for this, default to en_US - if (!languages.contains(current_language)) { - current_language = QStringLiteral("en_US"); + // Add default language (en-US) + QDir language_dir(QStringLiteral(":/ts")); + QStringList languages = language_dir.entryList(); + foreach (const QString& l, languages) { + AddLanguage(l); } + + QString current_language = Config::Current()[QStringLiteral("Language")].toString(); + if (current_language.isEmpty()) { + // No configured language, use system language + current_language = QLocale::system().name(); + + // If we don't have a language for this, default to en_US + if (!languages.contains(current_language)) { + current_language = QStringLiteral("en_US"); + } + } + language_combobox_->setCurrentIndex(languages.indexOf(current_language)); + + global_layout->addWidget(language_combobox_, row, 1); } - language_combobox_->setCurrentIndex(languages.indexOf(current_language)); - general_layout->addWidget(language_combobox_, row, 1); + { + QGroupBox* timeline_groupbox = new QGroupBox(tr("Timeline")); + QGridLayout* timeline_layout = new QGridLayout(timeline_groupbox); + layout->addWidget(timeline_groupbox); - row++; + int row = 0; - general_layout->addWidget(new QLabel(tr("Auto-Scroll Method:")), row, 0); + timeline_layout->addWidget(new QLabel(tr("Auto-Scroll Method:")), row, 0); - // ComboBox indices match enum indices - autoscroll_method_ = new QComboBox(); - autoscroll_method_->addItem(tr("None"), AutoScroll::kNone); - autoscroll_method_->addItem(tr("Page Scrolling"), AutoScroll::kPage); - autoscroll_method_->addItem(tr("Smooth Scrolling"), AutoScroll::kSmooth); - autoscroll_method_->setCurrentIndex(Config::Current()["Autoscroll"].toInt()); - general_layout->addWidget(autoscroll_method_, row, 1); + // ComboBox indices match enum indices + autoscroll_method_ = new QComboBox(); + autoscroll_method_->addItem(tr("None"), AutoScroll::kNone); + autoscroll_method_->addItem(tr("Page Scrolling"), AutoScroll::kPage); + autoscroll_method_->addItem(tr("Smooth Scrolling"), AutoScroll::kSmooth); + autoscroll_method_->setCurrentIndex(Config::Current()["Autoscroll"].toInt()); + timeline_layout->addWidget(autoscroll_method_, row, 1); - row++; + row++; - general_layout->addWidget(new QLabel(tr("Rectified Waveforms:")), row, 0); + timeline_layout->addWidget(new QLabel(tr("Rectified Waveforms:")), row, 0); - rectified_waveforms_ = new QCheckBox(); - rectified_waveforms_->setChecked(Config::Current()["RectifiedWaveforms"].toBool()); - general_layout->addWidget(rectified_waveforms_, row, 1); + rectified_waveforms_ = new QCheckBox(); + rectified_waveforms_->setChecked(Config::Current()["RectifiedWaveforms"].toBool()); + timeline_layout->addWidget(rectified_waveforms_, row, 1); - row++; + row++; - general_layout->addWidget(new QLabel(tr("Default Still Image Length:")), row, 0); + timeline_layout->addWidget(new QLabel(tr("Default Still Image Length:")), row, 0); - default_still_length_ = new FloatSlider(); - default_still_length_->SetMinimum(0.1); - default_still_length_->SetFormat(tr("%1 second(s)")); - default_still_length_->SetValue(Config::Current()["DefaultStillLength"].value().toDouble()); - general_layout->addWidget(default_still_length_); + default_still_length_ = new FloatSlider(); + default_still_length_->SetMinimum(0.1); + default_still_length_->SetFormat(tr("%1 second(s)")); + default_still_length_->SetValue(Config::Current()["DefaultStillLength"].value().toDouble()); + timeline_layout->addWidget(default_still_length_); + } + + { + QGroupBox* autorecovery_groupbox = new QGroupBox(tr("Auto-Recovery")); + QGridLayout* autorecovery_layout = new QGridLayout(autorecovery_groupbox); + layout->addWidget(autorecovery_groupbox); + + int row = 0; + + autorecovery_layout->addWidget(new QLabel(tr("Enable Auto-Recovery:")), row, 0); + + autorecovery_enabled_ = new QCheckBox(); + autorecovery_enabled_->setChecked(Config::Current()[QStringLiteral("AutorecoveryEnabled")].toBool()); + autorecovery_layout->addWidget(autorecovery_enabled_, row, 1); + + row++; + + autorecovery_layout->addWidget(new QLabel(tr("Auto-Recovery Interval:")), row, 0); + + autorecovery_interval_ = new IntegerSlider(); + autorecovery_interval_->SetMinimum(1); + autorecovery_interval_->SetMaximum(60); + autorecovery_interval_->SetFormat(tr("%1 minute(s)")); + autorecovery_interval_->SetValue(Config::Current()[QStringLiteral("AutorecoveryInterval")].toLongLong()); + autorecovery_layout->addWidget(autorecovery_interval_, row, 1); + + row++; + + autorecovery_layout->addWidget(new QLabel(tr("Maximum Versions Per Project:")), row, 0); + + autorecovery_maximum_ = new IntegerSlider(); + autorecovery_maximum_->SetMinimum(1); + autorecovery_maximum_->SetMaximum(1000); + autorecovery_maximum_->SetValue(Config::Current()[QStringLiteral("AutorecoveryMaximum")].toLongLong()); + autorecovery_layout->addWidget(autorecovery_maximum_, row, 1); + + row++; + + QPushButton* browse_autorecoveries = new QPushButton(tr("Browse Auto-Recoveries")); + connect(browse_autorecoveries, &QPushButton::clicked, Core::instance(), &Core::BrowseAutoRecoveries); + autorecovery_layout->addWidget(browse_autorecoveries, row, 1); + } layout->addStretch(); } @@ -121,6 +172,11 @@ void PreferencesGeneralTab::Accept(MultiUndoCommand *command) Config::Current()[QStringLiteral("Language")] = set_language; Core::instance()->SetLanguage(set_language.isEmpty() ? QLocale::system().name() : set_language); } + + Config::Current()[QStringLiteral("AutorecoveryEnabled")] = autorecovery_enabled_->isChecked(); + Config::Current()[QStringLiteral("AutorecoveryInterval")] = QVariant::fromValue(autorecovery_interval_->GetValue()); + Config::Current()[QStringLiteral("AutorecoveryMaximum")] = QVariant::fromValue(autorecovery_maximum_->GetValue()); + Core::instance()->SetAutorecoveryInterval(autorecovery_interval_->GetValue()); } void PreferencesGeneralTab::AddLanguage(const QString &locale_name) diff --git a/app/dialog/preferences/tabs/preferencesgeneraltab.h b/app/dialog/preferences/tabs/preferencesgeneraltab.h index 5b18cc2ad..034356bd2 100644 --- a/app/dialog/preferences/tabs/preferencesgeneraltab.h +++ b/app/dialog/preferences/tabs/preferencesgeneraltab.h @@ -28,6 +28,7 @@ #include "dialog/configbase/configdialogbase.h" #include "project/item/sequence/sequence.h" #include "widget/slider/floatslider.h" +#include "widget/slider/integerslider.h" namespace olive { @@ -50,6 +51,12 @@ private: FloatSlider* default_still_length_; + QCheckBox* autorecovery_enabled_; + + IntegerSlider* autorecovery_interval_; + + IntegerSlider* autorecovery_maximum_; + }; } diff --git a/app/project/project.cpp b/app/project/project.cpp index f3afb83b0..69adafbbe 100644 --- a/app/project/project.cpp +++ b/app/project/project.cpp @@ -36,6 +36,9 @@ Project::Project() : is_modified_(false), autorecovery_saved_(true) { + // Generate UUID for this project + uuid_ = QUuid::createUuid(); + // Adds a color manager "node" to this project so that it synchronizes color_manager_ = new ColorManager(); color_manager_->setParent(this); @@ -82,6 +85,10 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint *layout = MainWindowLayoutInfo::fromXml(reader, xml_node_data); + } else if (reader->name() == QStringLiteral("uuid")) { + + uuid_ = QUuid::fromString(reader->readElementText()); + } else if (reader->name() == QStringLiteral("nodes")) { while (XMLReadNextStartElement(reader)) { @@ -149,6 +156,8 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint void Project::Save(QXmlStreamWriter *writer) const { + writer->writeTextElement(QStringLiteral("uuid"), uuid_.toString()); + writer->writeStartElement(QStringLiteral("nodes")); foreach (Node* node, nodes()) { @@ -231,7 +240,7 @@ bool Project::is_modified() const void Project::set_modified(bool e) { is_modified_ = e; - autorecovery_saved_ = !e; + set_autorecovery_saved(!e); emit ModifiedChanged(is_modified_); } diff --git a/app/project/project.h b/app/project/project.h index 8edced89f..85834a001 100644 --- a/app/project/project.h +++ b/app/project/project.h @@ -78,12 +78,19 @@ public: return footage_viewer_; } + const QUuid& GetUuid() const + { + return uuid_; + } + signals: void NameChanged(); void ModifiedChanged(bool e); private: + QUuid uuid_; + Folder* root_; QString filename_; diff --git a/app/task/project/save/save.cpp b/app/task/project/save/save.cpp index 2d50c8d29..f5da9b4c0 100644 --- a/app/task/project/save/save.cpp +++ b/app/task/project/save/save.cpp @@ -37,8 +37,10 @@ ProjectSaveTask::ProjectSaveTask(Project *project) : bool ProjectSaveTask::Run() { + QString using_filename = override_filename_.isEmpty() ? project_->filename() : override_filename_; + // File to temporarily save to (ensures we can't half-write the user's main file and crash) - QString temp_save = FileFunctions::GetSafeTemporaryFilename(project_->filename()); + QString temp_save = FileFunctions::GetSafeTemporaryFilename(using_filename); QFile project_file(temp_save); @@ -54,7 +56,7 @@ bool ProjectSaveTask::Run() // Allows easy integer math for checking project versions. writer.writeTextElement(QStringLiteral("version"), QString::number(Core::kProjectVersion)); - writer.writeTextElement("url", project_->filename()); + writer.writeTextElement("url", using_filename); writer.writeStartElement(QStringLiteral("project")); @@ -74,11 +76,11 @@ bool ProjectSaveTask::Run() } // Save was successful, we can now rewrite the original file - if (FileFunctions::RenameFileAllowOverwrite(temp_save, project_->filename())) { + if (FileFunctions::RenameFileAllowOverwrite(temp_save, using_filename)) { return true; } else { SetError(tr("Failed to overwrite \"%1\". Project has been saved as \"%2\" instead.") - .arg(project_->filename(), temp_save)); + .arg(using_filename, temp_save)); return false; } } else { diff --git a/app/task/project/save/save.h b/app/task/project/save/save.h index 85306318a..4f7f8b0c3 100644 --- a/app/task/project/save/save.h +++ b/app/task/project/save/save.h @@ -37,12 +37,19 @@ public: return project_; } + void SetOverrideFilename(const QString& filename) + { + override_filename_ = filename; + } + protected: virtual bool Run() override; private: Project* project_; + QString override_filename_; + }; } diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index 54bd69b4a..89838fee9 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -635,6 +635,8 @@ void MainWindow::showEvent(QShowEvent *e) { QMainWindow::showEvent(e); + QMetaObject::invokeMethod(Core::instance(), "CheckForAutoRecoveries", Qt::QueuedConnection); + #ifdef Q_OS_LINUX if (!checked_graphics_vendor_) { // Check for nouveau since that driver really doesn't work with Olive