diff --git a/libs/ui/KisApplication.cpp b/libs/ui/KisApplication.cpp --- a/libs/ui/KisApplication.cpp +++ b/libs/ui/KisApplication.cpp @@ -665,21 +665,25 @@ { if (d->batchRun) return; - // Check for autosave files from a previous run. There can be several, and - // we want to offer a restore for every one. Including a nice thumbnail! - - QStringList filters; - filters << QString(".krita-*-*-autosave.kra"); - #ifdef Q_OS_WIN QDir dir = QDir::temp(); #else QDir dir = QDir::home(); #endif + // Check for autosave files from a previous run. There can be several, and + // we want to offer a restore for every one. Including a nice thumbnail! + + // Hidden autosave files + QStringList filters = QStringList() << QString(".krita-*-*-autosave.kra"); + // all autosave files for our application QStringList autosaveFiles = dir.entryList(filters, QDir::Files | QDir::Hidden); + // Visibile autosave files + filters = QStringList() << QString("krita-*-*-autosave.kra"); + autosaveFiles += dir.entryList(filters, QDir::Files); + // Allow the user to make their selection if (autosaveFiles.size() > 0) { if (d->splashScreen) { diff --git a/libs/ui/KisDocument.cpp b/libs/ui/KisDocument.cpp --- a/libs/ui/KisDocument.cpp +++ b/libs/ui/KisDocument.cpp @@ -394,11 +394,11 @@ } ~StrippedSafeSavingLocker() { - if (m_locked) { - m_imageLock.unlock(); - m_savingLock.unlock(); - } - } + if (m_locked) { + m_imageLock.unlock(); + m_savingLock.unlock(); + } + } bool successfullyLocked() const { return m_locked; @@ -423,9 +423,9 @@ KisResourceServerProvider::instance(); d->shapeController = new KisShapeController(this, d->nserver), - d->koShapeController = new KoShapeController(0, d->shapeController), + d->koShapeController = new KoShapeController(0, d->shapeController), - slotConfigChanged(); + slotConfigChanged(); } KisDocument::KisDocument(const KisDocument &rhs) @@ -438,9 +438,9 @@ setObjectName(rhs.objectName()); d->shapeController = new KisShapeController(this, d->nserver), - d->koShapeController = new KoShapeController(0, d->shapeController), + d->koShapeController = new KoShapeController(0, d->shapeController), - slotConfigChanged(); + slotConfigChanged(); // clone the image with keeping the GUIDs of the layers intact // NOTE: we expect the image to be locked! @@ -534,20 +534,43 @@ KisConfig cfg(true); if (cfg.backupFile() && filePathInfo.exists()) { - KBackup::numberedBackupFile(job.filePath); + + QString backupDir; + + switch(cfg.readEntry("backupfilelocation", 0)) { + case 1: + backupDir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); + break; + case 2: + backupDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation); + break; + default: + // Do nothing: the empty string is user file location + break; + } + + int numOfBackupsKept = cfg.readEntry("numberofbackupfiles", 1); + QString suffix = cfg.readEntry("backupfilesuffix", "~"); + + if (numOfBackupsKept == 1) { + KBackup::simpleBackupFile(job.filePath, backupDir, suffix); + } + else if (numOfBackupsKept > 2) { + KBackup::numberedBackupFile(job.filePath, backupDir, suffix, numOfBackupsKept); + } } KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!job.mimeType.isEmpty(), false); const QString actionName = - job.flags & KritaUtils::SaveIsExporting ? - i18n("Exporting Document...") : - i18n("Saving Document..."); + job.flags & KritaUtils::SaveIsExporting ? + i18n("Exporting Document...") : + i18n("Saving Document..."); bool started = - initiateSavingInBackground(actionName, - this, SLOT(slotCompleteSavingDocument(KritaUtils::ExportFileJob,KisImportExportFilter::ConversionStatus,QString)), - job, exportConfiguration); + initiateSavingInBackground(actionName, + this, SLOT(slotCompleteSavingDocument(KritaUtils::ExportFileJob,KisImportExportFilter::ConversionStatus,QString)), + job, exportConfiguration); if (!started) { emit canceled(QString()); @@ -738,7 +761,7 @@ const QString fileName = url.toLocalFile(); KisImportExportFilter::ConversionStatus status = - d->importExportManager-> + d->importExportManager-> exportDocument(fileName, fileName, mimeType, false, exportConfiguration); d->savingImage = 0; @@ -796,12 +819,12 @@ receiverObject, receiverMethod, Qt::UniqueConnection); bool started = - d->backgroundSaveDocument->startExportInBackground(actionName, - job.filePath, - job.filePath, - job.mimeType, - job.flags & KritaUtils::SaveShowWarnings, - exportConfiguration); + d->backgroundSaveDocument->startExportInBackground(actionName, + job.filePath, + job.filePath, + job.mimeType, + job.flags & KritaUtils::SaveShowWarnings, + exportConfiguration); if (!started) { // the state should have been deinitialized in slotChildCompletedSavingInBackground() @@ -939,12 +962,12 @@ KisImportExportFilter::ConversionStatus initializationStatus; d->childSavingFuture = - d->importExportManager->exportDocumentAsyc(location, - realLocation, - mimeType, - initializationStatus, - showWarnings, - exportConfiguration); + d->importExportManager->exportDocumentAsyc(location, + realLocation, + mimeType, + initializationStatus, + showWarnings, + exportConfiguration); if (initializationStatus != KisImportExportFilter::ConversionStatus::OK) { if (d->savingUpdater) { @@ -973,7 +996,7 @@ } KisImportExportFilter::ConversionStatus status = - d->childSavingFuture.result(); + d->childSavingFuture.result(); const QString errorMessage = this->errorMessage(); d->savingImage.clear(); @@ -1062,23 +1085,25 @@ // Using the extension allows to avoid relying on the mime magic when opening const QString extension (".kra"); - QRegularExpression autosavePattern("^\\..+-autosave.kra$"); + QString prefix = KisConfig(true).readEntry("autosavefileshidden") ? QString(".") : QString(); + QRegularExpression autosavePattern1("^\\..+-autosave.kra$"); + QRegularExpression autosavePattern2("^.+-autosave.kra$"); QFileInfo fi(path); QString dir = fi.absolutePath(); QString filename = fi.fileName(); - if (path.isEmpty() || autosavePattern.match(filename).hasMatch()) { + if (path.isEmpty() || autosavePattern1.match(filename).hasMatch() || autosavePattern2.match(filename).hasMatch()) { // Never saved? #ifdef Q_OS_WIN // On Windows, use the temp location (https://bugs.kde.org/show_bug.cgi?id=314921) - retval = QString("%1%2.%3-%4-%5-autosave%6").arg(QDir::tempPath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension); + retval = QString("%1%2%7%3-%4-%5-autosave%6").arg(QDir::tempPath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension).arg(prefix); #else // On Linux, use a temp file in $HOME then. Mark it with the pid so two instances don't overwrite each other's autosave file - retval = QString("%1%2.%3-%4-%5-autosave%6").arg(QDir::homePath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension); + retval = QString("%1%2%7%3-%4-%5-autosave%6").arg(QDir::homePath()).arg(QDir::separator()).arg("krita").arg(qApp->applicationPid()).arg(objectName()).arg(extension).arg(prefix); #endif } else { - retval = QString("%1%2.%3-autosave%4").arg(dir).arg(QDir::separator()).arg(filename).arg(extension); + retval = QString("%1%2%5%3-autosave%4").arg(dir).arg(QDir::separator()).arg(filename).arg(extension).arg(prefix); } //qDebug() << "generateAutoSaveFileName() for path" << path << ":" << retval; @@ -1454,14 +1479,19 @@ QFile::remove(asf); } - QRegularExpression autosavePattern("^\\..+-autosave.kra$"); + QList expressions; - if (wasRecovered && - !autosaveBaseName.isEmpty() && - autosavePattern.match(QFileInfo(autosaveBaseName).fileName()).hasMatch() && - QFile::exists(autosaveBaseName)) { + expressions << QRegularExpression("^\\..+-autosave.kra$") + << QRegularExpression("^.+-autosave.kra$"); - QFile::remove(autosaveBaseName); + Q_FOREACH(const QRegularExpression &rex, expressions) { + if (wasRecovered && + !autosaveBaseName.isEmpty() && + rex.match(QFileInfo(autosaveBaseName).fileName()).hasMatch() && + QFile::exists(autosaveBaseName)) { + + QFile::remove(autosaveBaseName); + } } } diff --git a/libs/ui/dialogs/kis_dlg_preferences.cc b/libs/ui/dialogs/kis_dlg_preferences.cc --- a/libs/ui/dialogs/kis_dlg_preferences.cc +++ b/libs/ui/dialogs/kis_dlg_preferences.cc @@ -90,6 +90,29 @@ # include #endif +struct BackupSuffixValidator : public QValidator { + BackupSuffixValidator(QObject *parent) + : QValidator(parent) + , invalidCharacters(QStringList() + << "0" << "1" << "2" << "3" << "4" << "5" << "6" << "7" << "8" << "9" + << "/" << "\\" << ":" << ";" << " ") + {} + + ~BackupSuffixValidator() override {} + + const QStringList invalidCharacters; + + State validate(QString &line, int &/*pos*/) const override + { + Q_FOREACH(const QString invalidChar, invalidCharacters) { + if (line.contains(invalidChar)) { + return Invalid; + } + } + return Acceptable; + } +}; + GeneralTab::GeneralTab(QWidget *_parent, const char *_name) : WdgGeneralSettings(_parent, _name) @@ -169,26 +192,34 @@ m_kineticScrollingSensitivitySlider->setValue(cfg.kineticScrollingSensitivity()); m_chkKineticScrollingHideScrollbars->setChecked(cfg.kineticScrollingHiddenScrollbars()); - // - // Miscellaneous + // File handling // - cmbStartupSession->addItem(i18n("Open default window")); - cmbStartupSession->addItem(i18n("Load previous session")); - cmbStartupSession->addItem(i18n("Show session manager")); - cmbStartupSession->setCurrentIndex(cfg.sessionOnStartup()); - - chkSaveSessionOnQuit->setChecked(cfg.saveSessionOnQuit(false)); - int autosaveInterval = cfg.autoSaveInterval(); //convert to minutes m_autosaveSpinBox->setValue(autosaveInterval / 60); m_autosaveCheckBox->setChecked(autosaveInterval > 0); + chkHideAutosaveFiles->setChecked(cfg.readEntry("autosavefileshidden", true)); m_chkCompressKra->setChecked(cfg.compressKra()); chkZip64->setChecked(cfg.useZip64()); m_backupFileCheckBox->setChecked(cfg.backupFile()); + cmbBackupFileLocation->setCurrentIndex(cfg.readEntry("backupfilelocation", 0)); + txtBackupFileSuffix->setText(cfg.readEntry("backupfilesuffix", "~")); + QValidator *validator = new BackupSuffixValidator(txtBackupFileSuffix); + txtBackupFileSuffix->setValidator(validator); + intNumBackupFiles->setValue(cfg.readEntry("numberofbackupfiles", 1)); + + // + // Miscellaneous + // + cmbStartupSession->addItem(i18n("Open default window")); + cmbStartupSession->addItem(i18n("Load previous session")); + cmbStartupSession->addItem(i18n("Show session manager")); + cmbStartupSession->setCurrentIndex(cfg.sessionOnStartup()); + + chkSaveSessionOnQuit->setChecked(cfg.saveSessionOnQuit(false)); m_chkConvertOnImport->setChecked(cfg.convertToImageColorspaceOnImport()); @@ -223,8 +254,15 @@ m_autosaveCheckBox->setChecked(cfg.autoSaveInterval(true) > 0); //convert to minutes m_autosaveSpinBox->setValue(cfg.autoSaveInterval(true) / 60); + chkHideAutosaveFiles->setChecked(true); + m_undoStackSize->setValue(cfg.undoStackLimit(true)); + m_backupFileCheckBox->setChecked(cfg.backupFile(true)); + cmbBackupFileLocation->setCurrentIndex(0); + txtBackupFileSuffix->setText("~"); + intNumBackupFiles->setValue(1); + m_showOutlinePainting->setChecked(cfg.showOutlineWhilePainting(true)); m_changeBrushOutline->setChecked(!cfg.forceAlwaysFullSizedOutline(true)); @@ -1344,7 +1382,13 @@ cfg.setMDIBackgroundColor(dialog->m_general->m_mdiColor->color().toQColor()); cfg.setMDIBackgroundImage(dialog->m_general->m_backgroundimage->text()); cfg.setAutoSaveInterval(dialog->m_general->autoSaveInterval()); + cfg.writeEntry("autosavefileshidden", dialog->m_general->chkHideAutosaveFiles->isChecked()); + cfg.setBackupFile(dialog->m_general->m_backupFileCheckBox->isChecked()); + cfg.writeEntry("backupfilelocation", dialog->m_general->cmbBackupFileLocation->currentIndex()); + cfg.writeEntry("backupfilesuffix", dialog->m_general->txtBackupFileSuffix->text()); + cfg.writeEntry("numberofbackupfiles", dialog->m_general->intNumBackupFiles->value()); + cfg.setShowCanvasMessages(dialog->m_general->showCanvasMessages()); cfg.setCompressKra(dialog->m_general->compressKra()); cfg.setUseZip64(dialog->m_general->useZip64()); diff --git a/libs/ui/forms/wdggeneralsettings.ui b/libs/ui/forms/wdggeneralsettings.ui --- a/libs/ui/forms/wdggeneralsettings.ui +++ b/libs/ui/forms/wdggeneralsettings.ui @@ -6,7 +6,7 @@ 0 0 - 759 + 552 468 @@ -495,6 +495,187 @@ + + + File Handling + + + + + + Enable Autosaving + + + true + + + + + + Autosave Interval: + + + + + + + + 0 + 0 + + + + + 75 + 0 + + + + min + + + Every + + + 1 + + + 1440 + + + 5 + + + 15 + + + + + + + Unnamed autosave files are hidden by default + + + true + + + + + + + + + + Create a Backup File on Saving + + + true + + + + + + Backup File Location + + + + + + + + Same Folder as Original File + + + + + User Folder + + + + + Temporary File Location + + + + + + + + Backup File Suffix: + + + + + + + ~ + + + 10 + + + + + + + Number of Backup Files Kept: + + + + + + + 1 + + + 1 + + + + + + + + + + Kra File Compression + + + + + + Compress .kra files more (slows loading/saving) + + + + + + + <html><head/><body><p>Only use this option for <span style=" font-weight:600;">very</span> large files: larger than 4 GiB on disk.</p></body></html> + + + Use Zip64 (for very large files: cannot be opened in versions of Krita older than 4.2.0) + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + Miscellaneous @@ -509,7 +690,7 @@ - When Krita starts + When Krita starts: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -526,96 +707,14 @@ - - - - Save session when Krita closes - - - - - - - Autosave: - - - - - - - - - - 0 - 0 - - - - Enabled - - - true - - - - - - - - 0 - 0 - - - - - 75 - 0 - - - - min - - - Every - - - 1 - - - 1440 - - - 5 - - - 15 - - - - - - - - Compress .kra files more (slows loading/saving) - - - - - - - Create backup file - - - - On importing images as layers, convert to the image colorspace - + @@ -631,7 +730,7 @@ - + @@ -659,30 +758,62 @@ - + 0 0 - Number of Palette Presets + Number of Palette Presets: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + + + + + 0 + 0 + + + + + 75 + 0 + + + + 10 + + + 30 + + + + Show root layer - + + + + Enable Logging for bug reports + + + true + + + + Warning: if you enable this setting and the file dialogs do weird stuff, do not report a bug. @@ -692,14 +823,14 @@ - + Maximum brush size: - + @@ -735,7 +866,7 @@ - + Qt::Vertical @@ -748,45 +879,10 @@ - - - - - 0 - 0 - - - - - 75 - 0 - - - - 10 - - - 30 - - - - - - - <html><head/><body><p>Only use this option for <span style=" font-weight:600;">very</span> large files: larger than 4 GiB on disk.</p></body></html> - - - Use Zip64 (for very large files: cannot be opened in versions of Krita older than 4.2.0) - - - - - + + - Enable Logging for bug reports - - - true + Save session when Krita closes @@ -815,22 +911,5 @@ - - - m_autosaveCheckBox - toggled(bool) - m_autosaveSpinBox - setEnabled(bool) - - - 20 - 20 - - - 20 - 20 - - - - +