Commit 46283124 authored by Boudewijn Rempt's avatar Boudewijn Rempt Committed by Scott Petrovic

Port the save-saving patch from 3.1

BUG:370193 Save a copy of the image not the original

This restores the delayed saving dialog, which is now even weirder,
with a cancel running operating button which mostly doesn't work,
a do not save button, and a don't care, save a broken copy button.

See also

https://phabricator.kde.org/D3258
https://phabricator.kde.org/T4220
https://bugs.kde.org/show_bug.cgi?id=370566
https://bugs.kde.org/show_bug.cgi?id=369368
https://bugs.kde.org/show_bug.cgi?id=370193
parent cecce7a4
......@@ -268,7 +268,8 @@ public:
macroNestDepth(0),
imageIdleWatcher(2000 /*ms*/),
suppressProgress(false),
fileProgressProxy(0)
fileProgressProxy(0),
savingLock(&savingMutex)
{
if (QLocale().measurementSystem() == QLocale::ImperialSystem) {
unit = KoUnit::Inch;
......@@ -333,6 +334,8 @@ public:
qint32 macroNestDepth;
KisImageSP image;
KisImageSP savingImage;
KisNodeSP preActivatedNode;
KisShapeController* shapeController;
KoShapeController* koShapeController;
......@@ -345,6 +348,8 @@ public:
QList<KisPaintingAssistantSP> assistants;
KisGridConfig gridConfig;
StdLockableWrapper<QMutex> savingLock;
void setImageAndInitIdleWatcher(KisImageSP _image) {
image = _image;
......@@ -365,10 +370,9 @@ class KisDocument::Private::SafeSavingLocker {
public:
SafeSavingLocker(KisDocument::Private *_d, KisDocument *document)
: d(_d)
, m_document(document)
, m_locked(false)
, m_imageLock(d->image, true)
, m_savingLock(&d->savingMutex)
, m_document(document)
{
const int realAutoSaveInterval = KisConfig().autoSaveInterval();
const int emergencyAutoSaveInterval = 10; // sec
......@@ -382,20 +386,20 @@ public:
* Since we are trying to lock multiple objects, so we should
* do it in a safe manner.
*/
m_locked = std::try_lock(m_imageLock, m_savingLock) < 0;
m_locked = std::try_lock(m_imageLock, d->savingLock) < 0;
if (!m_locked) {
if (d->isAutosaving) {
d->disregardAutosaveFailure = true;
if (realAutoSaveInterval) {
document->setAutoSaveDelay(emergencyAutoSaveInterval);
m_document->setAutoSaveDelay(emergencyAutoSaveInterval);
}
} else {
d->image->requestStrokeEnd();
QApplication::processEvents();
// one more try...
m_locked = std::try_lock(m_imageLock, m_savingLock) < 0;
m_locked = std::try_lock(m_imageLock, d->savingLock) < 0;
}
}
......@@ -407,7 +411,7 @@ public:
~SafeSavingLocker() {
if (m_locked) {
m_imageLock.unlock();
m_savingLock.unlock();
d->savingLock.unlock();
const int realAutoSaveInterval = KisConfig().autoSaveInterval();
m_document->setAutoSaveDelay(realAutoSaveInterval);
......@@ -420,11 +424,10 @@ public:
private:
KisDocument::Private *d;
KisDocument *m_document;
bool m_locked;
KisImageBarrierLockAdapter m_imageLock;
StdLockableWrapper<QMutex> m_savingLock;
KisDocument *m_document;
};
KisDocument::KisDocument()
......@@ -630,9 +633,7 @@ bool KisDocument::save(KisPropertiesConfigurationSP exportConfiguration)
bool KisDocument::saveFile(const QString &filePath, KisPropertiesConfigurationSP exportConfiguration)
{
Private::SafeSavingLocker locker(d, this);
if (!locker.successfullyLocked()) {
qWarning() << "Could not lock image for saving, it's still busy";
if (!prepareLocksForSaving()) {
return false;
}
......@@ -780,6 +781,7 @@ bool KisDocument::saveFile(const QString &filePath, KisPropertiesConfigurationSP
emit sigSavingFinished();
clearFileProgressUpdater();
unlockAfterSaving();
return ret;
}
......@@ -1616,6 +1618,11 @@ KisImageWSP KisDocument::image() const
return d->image;
}
KisImageSP KisDocument::savingImage() const
{
return d->savingImage;
}
void KisDocument::setCurrentImage(KisImageSP image)
{
......@@ -1651,3 +1658,36 @@ bool KisDocument::isAutosaving() const
{
return d->isAutosaving;
}
bool KisDocument::prepareLocksForSaving()
{
{
Private::SafeSavingLocker locker(d, this);
if (locker.successfullyLocked()) {
d->savingImage = d->image->clone(true);
}
else {
d->lastErrorMessage = i18n("The image was still busy while saving. Your saved image might be incomplete.");
d->image->lock();
d->savingImage = d->image->clone(true);
d->image->unlock();
}
}
if (!d->savingMutex.tryLock()) {
qWarning() << "Could not lock for saving!";
d->lastErrorMessage = i18n("Could not lock the image for saving.");
d->savingImage = 0;
return false;
}
Q_ASSERT(d->savingImage);
return true;
}
void KisDocument::unlockAfterSaving()
{
d->savingImage = 0;
d->savingMutex.unlock();
}
......@@ -508,6 +508,16 @@ public:
KisImageWSP image() const;
/**
* @brief savingImage provides a detached, shallow copy of the original image that must be used when saving.
* Any strokes in progress will not be applied to this image, so the result might be missing some data. On
* the other hand, it won't block.
*
* @return a shallow copy of the original image, or 0 is saving is not in progress
*/
KisImageSP savingImage() const;
/**
* Adds progressproxy for file operations
*/
......@@ -580,6 +590,10 @@ private Q_SLOTS:
private:
bool prepareLocksForSaving();
void unlockAfterSaving();
QString prettyPathOrUrl() const;
bool openUrlInternal(const QUrl &url);
......
......@@ -43,7 +43,10 @@ KisDelayedSaveDialog::KisDelayedSaveDialog(KisImageSP image, QWidget *parent)
ui->setupUi(this);
connect(ui->btnCancel, SIGNAL(clicked()), SLOT(slotCancelRequested()));
connect(ui->bnDontWait, SIGNAL(clicked()), SLOT(accept()));
connect(ui->bnDontSave, SIGNAL(clicked()), SLOT(reject()));
connect(ui->bnCancel, SIGNAL(clicked()), SLOT(slotCancelRequested()));
connect(&m_d->updateTimer, SIGNAL(timeout()), SLOT(slotTimerTimeout()));
m_d->image->compositeProgressProxy()->addProxy(ui->progressBar);
......
......@@ -9,8 +9,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>374</width>
<height>157</height>
<width>449</width>
<height>147</height>
</rect>
</property>
<property name="windowTitle">
......@@ -19,7 +19,7 @@
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="2,1,3,1">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="lblMessage">
<property name="text">
......@@ -54,11 +54,29 @@
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnCancel">
<property name="text">
<string>Cancel Operation and Save</string>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="bnDontWait">
<property name="text">
<string>Save without waiting</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="bnCancel">
<property name="text">
<string>Cancel Operation and Save</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="bnDontSave">
<property name="text">
<string>Close, do not save</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
......
......@@ -45,8 +45,8 @@ KisBMPExport::~KisBMPExport()
KisImportExportFilter::ConversionStatus KisBMPExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
QRect rc = document->image()->bounds();
QImage image = document->image()->projection()->convertToQImage(0, 0, 0, rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
QRect rc = document->savingImage()->bounds();
QImage image = document->savingImage()->projection()->convertToQImage(0, 0, 0, rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
image.save(io, QFileInfo(filename()).suffix().toLatin1());
return KisImportExportFilter::OK;
}
......
......@@ -64,7 +64,7 @@ KisImportExportFilter::ConversionStatus KisBrushExport::convert(KisDocument *doc
{
// XXX: Loading the parasite itself was commented out -- needs investigation
// KisAnnotationSP annotation = document->image()->annotation("ImagePipe Parasite");
// KisAnnotationSP annotation = document->savingImage()->annotation("ImagePipe Parasite");
// KisPipeBrushParasite parasite;
// if (annotation) {
// QBuffer buf(const_cast<QByteArray*>(&annotation->annotation()));
......@@ -74,8 +74,8 @@ KisImportExportFilter::ConversionStatus KisBrushExport::convert(KisDocument *doc
// }
KisBrushExportOptions exportOptions;
if (document->image()->dynamicPropertyNames().contains("brushspacing")) {
exportOptions.spacing = document->image()->property("brushspacing").toFloat();
if (document->savingImage()->dynamicPropertyNames().contains("brushspacing")) {
exportOptions.spacing = document->savingImage()->property("brushspacing").toFloat();
}
else {
exportOptions.spacing = configuration->getInt("spacing");
......@@ -84,7 +84,7 @@ KisImportExportFilter::ConversionStatus KisBrushExport::convert(KisDocument *doc
exportOptions.name = configuration->getString("name");
}
else {
exportOptions.name = document->image()->objectName();
exportOptions.name = document->savingImage()->objectName();
}
exportOptions.mask = configuration->getBool("mask");
exportOptions.selectionMode = configuration->getInt("selectionMode");
......@@ -103,17 +103,14 @@ KisImportExportFilter::ConversionStatus KisBrushExport::convert(KisDocument *doc
qApp->processEvents(); // For vector layers to be updated
// the image must be locked at the higher levels
KIS_SAFE_ASSERT_RECOVER_NOOP(document->image()->locked());
QRect rc = document->image()->bounds();
QRect rc = document->savingImage()->bounds();
brush->setName(exportOptions.name);
brush->setSpacing(exportOptions.spacing);
brush->setUseColorAsMask(exportOptions.mask);
int w = document->image()->width();
int h = document->image()->height();
int w = document->savingImage()->width();
int h = document->savingImage()->height();
KisImagePipeBrush *pipeBrush = dynamic_cast<KisImagePipeBrush*>(brush);
if (pipeBrush) {
......@@ -123,7 +120,7 @@ KisImportExportFilter::ConversionStatus KisBrushExport::convert(KisDocument *doc
KoProperties properties;
properties.setProperty("visible", true);
QList<KisNodeSP> layers = document->image()->root()->childNodes(QStringList("KisLayer"), properties);
QList<KisNodeSP> layers = document->savingImage()->root()->childNodes(QStringList("KisLayer"), properties);
Q_FOREACH (KisNodeSP node, layers) {
devices[0].push_back(node->projection().data());
......@@ -153,7 +150,7 @@ KisImportExportFilter::ConversionStatus KisBrushExport::convert(KisDocument *doc
pipeBrush->setDevices(devices, w, h);
}
else {
QImage image = document->image()->projection()->convertToQImage(0, 0, 0, rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
QImage image = document->savingImage()->projection()->convertToQImage(0, 0, 0, rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
brush->setImage(image);
}
......
......@@ -52,7 +52,7 @@
#include "csv_layer_record.h"
CSVSaver::CSVSaver(KisDocument *doc, bool batchMode)
: m_image(doc->image())
: m_image(doc->savingImage())
, m_doc(doc)
, m_batchMode(batchMode)
, m_stop(false)
......@@ -386,7 +386,7 @@ QString CSVSaver::convertToBlending(const QString &opid)
KisImageBuilder_Result CSVSaver::getLayer(CSVLayerRecord* layer, KisDocument* exportDoc, KisKeyframeSP keyframe, const QString &path, int frame, int idx)
{
//render to the temp layer
KisImageSP image = exportDoc->image();
KisImageSP image = exportDoc->savingImage();
KisPaintDeviceSP device = image->rootLayer()->firstChild()->projection();
if (!keyframe.isNull()) {
......
......@@ -76,24 +76,19 @@ KisConfigWidget *EXRExport::createConfigurationWidget(QWidget *parent, const QBy
KisImportExportFilter::ConversionStatus EXRExport::convert(KisDocument *document, QIODevice */*io*/, KisPropertiesConfigurationSP configuration)
{
KisImageSP image = document->image();
KisImageSP image = document->savingImage();
EXRConverter exrConverter(document, !batchMode());
KisImageBuilder_Result res;
if (configuration->getBool("flatten")) {
// the image must be locked at the higher levels
KIS_SAFE_ASSERT_RECOVER_NOOP(document->image()->locked());
KisPaintDeviceSP pd = new KisPaintDevice(*image->projection());
KisPaintLayerSP l = new KisPaintLayer(image, "projection", OPACITY_OPAQUE_U8, pd);
res = exrConverter.buildFile(filename(), l);
}
else {
// the image must be locked at the higher levels
KIS_SAFE_ASSERT_RECOVER_NOOP(document->image()->locked());
res = exrConverter.buildFile(filename(), image->rootLayer());
}
......
......@@ -91,9 +91,9 @@ void KisHeightMapExport::initializeCapabilities()
KisImportExportFilter::ConversionStatus KisHeightMapExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration)
{
KisImageSP image = document->image();
KisImageSP image = document->savingImage();
if (document->image()->width() != document->image()->height()) {
if (image->width() != image->height()) {
document->setErrorMessage(i18n("Cannot export this image to a heightmap: it is not square"));
return KisImportExportFilter::WrongFormat;
}
......@@ -104,8 +104,6 @@ KisImportExportFilter::ConversionStatus KisHeightMapExport::convert(KisDocument
bool downscale = false;
// the image must be locked at the higher levels
KIS_SAFE_ASSERT_RECOVER_NOOP(image->locked());
KisPaintDeviceSP pd = new KisPaintDevice(*image->projection());
QDataStream s(io);
......
......@@ -65,7 +65,7 @@ KisJPEGExport::~KisJPEGExport()
KisImportExportFilter::ConversionStatus KisJPEGExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration)
{
KisImageSP image = document->image();
KisImageSP image = document->savingImage();
Q_CHECK_PTR(image);
// An extra option to pass to the config widget to set the state correctly, this isn't saved
......@@ -90,8 +90,6 @@ KisImportExportFilter::ConversionStatus KisJPEGExport::convert(KisDocument *docu
m.setEnabledFilters(configuration->getString("filters").split(","));
options.filters = m.enabledFilters();
// the image must be locked at the higher levels
KIS_SAFE_ASSERT_RECOVER_NOOP(document->image()->locked());
KisPaintDeviceSP pd = new KisPaintDevice(*image->projection());
KisJPEGConverter kpc(document, batchMode());
......
......@@ -42,7 +42,7 @@ static const char CURRENT_DTD_VERSION[] = "2.0";
KraConverter::KraConverter(KisDocument *doc)
: m_doc(doc)
, m_image(doc->image())
, m_image(doc->savingImage())
{
}
......@@ -217,7 +217,7 @@ bool KraConverter::savePreview(KoStore *store)
QPixmap pix = m_doc->generatePreview(QSize(256, 256));
QImage preview(pix.toImage().convertToFormat(QImage::Format_ARGB32, Qt::ColorOnly));
if (preview.size() == QSize(0,0)) {
QSize newSize = m_doc->image()->bounds().size();
QSize newSize = m_doc->savingImage()->bounds().size();
newSize.scale(QSize(256, 256), Qt::KeepAspectRatio);
preview = QImage(newSize, QImage::Format_ARGB32);
preview.fill(QColor(0, 0, 0, 0));
......
......@@ -55,10 +55,9 @@ KraExport::~KraExport()
KisImportExportFilter::ConversionStatus KraExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
KisImageSP image = document->image();
KisImageSP image = document->savingImage();
Q_CHECK_PTR(image);
KisPaintDeviceSP pd = image->projection();
KraConverter kraConverter(document);
KisImageBuilder_Result res = kraConverter.buildFile(io);
......
......@@ -73,7 +73,7 @@ bool hasShapeLayerChild(KisNodeSP node)
KisImportExportFilter::ConversionStatus OraExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
KisImageSP image = document->image();
KisImageSP image = document->savingImage();
Q_CHECK_PTR(image);
......
......@@ -71,7 +71,7 @@ bool hasVisibleWidgets()
KisImportExportFilter::ConversionStatus KisPNGExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration)
{
KisImageSP image = document->image();
KisImageSP image = document->savingImage();
KisPNGOptions options;
......
......@@ -141,10 +141,7 @@ KisImportExportFilter::ConversionStatus KisPPMExport::convert(KisDocument *docum
bool bitmap = (mimeType() == "image/x-portable-bitmap");
KisImageSP image = document->image();
Q_CHECK_PTR(image);
// the image must be locked at the higher levels
KIS_SAFE_ASSERT_RECOVER_NOOP(document->image()->locked());
KisImageSP image = document->savingImage();
KisPaintDeviceSP pd = new KisPaintDevice(*image->projection());
// Test color space
......
......@@ -85,7 +85,7 @@ QPair<psd_color_mode, quint16> colormodelid_to_psd_colormode(const QString &colo
PSDSaver::PSDSaver(KisDocument *doc)
: m_image(doc->image())
: m_image(doc->savingImage())
, m_doc(doc)
, m_stop(false)
{
......
......@@ -43,7 +43,7 @@ QMLExport::~QMLExport()
KisImportExportFilter::ConversionStatus QMLExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/)
{
KisImageSP image = document->image();
KisImageSP image = document->savingImage();
Q_CHECK_PTR(image);
QMLConverter converter;
......
......@@ -465,13 +465,13 @@ KisImportExportFilter::ConversionStatus KisSpriterExport::convert(KisDocument *d
{
QFileInfo fi(filename());
m_image = document->image();
m_image = document->savingImage();
if (m_image->rootLayer()->childCount() == 0) {
return KisImportExportFilter::UsageError;
}
KisGroupLayerSP root = document->image()->rootLayer();
KisGroupLayerSP root = m_image->rootLayer();
m_boneLayer = qobject_cast<KisLayer*>(root->findChildByName("bone").data());
//qDebug() << "Found boneLayer" << m_boneLayer;
......@@ -479,7 +479,7 @@ KisImportExportFilter::ConversionStatus KisSpriterExport::convert(KisDocument *d
m_rootLayer= qobject_cast<KisGroupLayer*>(root->findChildByName("root").data());
//qDebug() << "Fond rootLayer" << m_rootLayer;
parseFolder(document->image()->rootLayer(), "", fi.absolutePath());
parseFolder(m_image->rootLayer(), "", fi.absolutePath());
m_rootBone = 0;
......
......@@ -48,8 +48,8 @@ KisTGAExport::~KisTGAExport()
KisImportExportFilter::ConversionStatus KisTGAExport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP configuration)
{
Q_UNUSED(configuration);
QRect rc = document->image()->bounds();
QImage image = document->image()->projection()->convertToQImage(0, 0, 0, rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
QRect rc = document->savingImage()->bounds();
QImage image = document->savingImage()->projection()->convertToQImage(0, 0, 0, rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
QDataStream s(io);
s.setByteOrder(QDataStream::LittleEndian);
......
......@@ -63,7 +63,7 @@ KisImportExportFilter::ConversionStatus KisTIFFExport::convert(KisDocument *docu
cfg = lastSavedConfiguration(KisDocument::nativeFormatMimeType(), "image/tiff");
}
const KoColorSpace* cs = document->image()->colorSpace();
const KoColorSpace* cs = document->savingImage()->colorSpace();
cfg->setProperty("type", (int)cs->channels()[0]->channelValueType());
cfg->setProperty("isCMYK", (cs->colorModelId() == CMYKAColorModelID));
......@@ -117,18 +117,15 @@ KisImportExportFilter::ConversionStatus KisTIFFExport::convert(KisDocument *docu
KisImageSP image;
if (options.flatten) {
image = new KisImage(0, document->image()->width(), document->image()->height(), document->image()->colorSpace(), "");
image->setResolution(document->image()->xRes(), document->image()->yRes());
KisPaintDeviceSP pd = KisPaintDeviceSP(new KisPaintDevice(*document->image()->projection()));
image = new KisImage(0, document->savingImage()->width(), document->savingImage()->height(), document->savingImage()->colorSpace(), "");
image->setResolution(document->savingImage()->xRes(), document->savingImage()->yRes());
KisPaintDeviceSP pd = KisPaintDeviceSP(new KisPaintDevice(*document->savingImage()->projection()));
KisPaintLayerSP l = KisPaintLayerSP(new KisPaintLayer(image.data(), "projection", OPACITY_OPAQUE_U8, pd));
image->addNode(KisNodeSP(l.data()), image->rootLayer().data());
} else {
image = document->image();
image = document->savingImage();
}
// the image must be locked at the higher levels
KIS_SAFE_ASSERT_RECOVER_NOOP(document->image()->locked());
KisTIFFConverter tiffConverter(document);
KisImageBuilder_Result res;
if ((res = tiffConverter.buildFile(filename(), image, options)) == KisImageBuilder_RESULT_OK) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment