diff --git a/libs/image/kis_layer.cc b/libs/image/kis_layer.cc --- a/libs/image/kis_layer.cc +++ b/libs/image/kis_layer.cc @@ -216,8 +216,10 @@ KisBaseNode::PropertyList l = KisBaseNode::sectionModelProperties(); l << KisBaseNode::Property(KoID("opacity", i18n("Opacity")), i18n("%1%", percentOpacity())); - if (compositeOp()) { - l << KisBaseNode::Property(KoID("compositeop", i18n("Composite Mode")), compositeOp()->description()); + const KoCompositeOp * compositeOp = this->compositeOp(); + + if (compositeOp) { + l << KisBaseNode::Property(KoID("compositeop", i18n("Composite Mode")), compositeOp->description()); } if (m_d->layerStyle && !m_d->layerStyle->isEmpty()) { diff --git a/libs/image/kis_legacy_undo_adapter.cpp b/libs/image/kis_legacy_undo_adapter.cpp --- a/libs/image/kis_legacy_undo_adapter.cpp +++ b/libs/image/kis_legacy_undo_adapter.cpp @@ -47,6 +47,7 @@ undoStore()->addCommand(command); } else { + // TODO: add feedback m_image->barrierLock(); undoStore()->addCommand(command); m_image->unlock(); @@ -56,6 +57,7 @@ void KisLegacyUndoAdapter::beginMacro(const KUndo2MagicString& macroName) { if(!m_macroCounter) { + // TODO: add feedback m_image->barrierLock(); } diff --git a/libs/ui/KisMainWindow.cpp b/libs/ui/KisMainWindow.cpp --- a/libs/ui/KisMainWindow.cpp +++ b/libs/ui/KisMainWindow.cpp @@ -882,7 +882,8 @@ std::unique_lock> l(wrapper, std::try_to_lock); if (!l.owns_lock()) return false; - KisDelayedSaveDialog dlg(document->image(), this); + // no busy wait for saving because it is dangerous! + KisDelayedSaveDialog dlg(document->image(), KisDelayedSaveDialog::SaveDialog, 0, this); dlg.blockIfImageIsBusy(); if (dlg.result() != QDialog::Accepted) { diff --git a/libs/ui/KisViewManager.h b/libs/ui/KisViewManager.h --- a/libs/ui/KisViewManager.h +++ b/libs/ui/KisViewManager.h @@ -165,6 +165,24 @@ int viewCount() const; + /** + * @brief blockUntillOperationsFinished blocks the GUI of the application until execution + * of actions on \p image is finished + * @param image the image which we should wait for + * @return true if the image has finished execution of the actions, false if + * the user cancelled operation + */ + bool blockUntillOperationsFinished(KisImageSP image); + + + /** + * @brief blockUntillOperationsFinished blocks the GUI of the application until execution + * of actions on \p image is finished. Does *not* provide a "Cancel" button. So the + * user is forced to wait. + * @param image the image which we should wait for + */ + void blockUntillOperationsFinishedForced(KisImageSP image); + public: KisGridManager * gridManager() const; diff --git a/libs/ui/KisViewManager.cpp b/libs/ui/KisViewManager.cpp --- a/libs/ui/KisViewManager.cpp +++ b/libs/ui/KisViewManager.cpp @@ -127,6 +127,7 @@ #include "kis_icon_utils.h" #include "kis_guides_manager.h" #include "kis_derived_resources.h" +#include "dialogs/kis_delayed_save_dialog.h" class BlockingUserInputEventFilter : public QObject @@ -242,6 +243,8 @@ KSelectAction *actionAuthor; // Select action for author profile. QByteArray canvasState; + + bool blockUntillOperationsFinishedImpl(KisImageSP image, bool force); }; @@ -744,6 +747,26 @@ return 0; } +bool KisViewManager::KisViewManagerPrivate::blockUntillOperationsFinishedImpl(KisImageSP image, bool force) +{ + const int busyWaitDelay = 1000; + KisDelayedSaveDialog dialog(image, !force ? KisDelayedSaveDialog::GeneralDialog : KisDelayedSaveDialog::ForcedDialog, busyWaitDelay, mainWindow); + dialog.blockIfImageIsBusy(); + + return dialog.result() == QDialog::Accepted; +} + + +bool KisViewManager::blockUntillOperationsFinished(KisImageSP image) +{ + return d->blockUntillOperationsFinishedImpl(image, false); +} + +void KisViewManager::blockUntillOperationsFinishedForced(KisImageSP image) +{ + d->blockUntillOperationsFinishedImpl(image, true); +} + void KisViewManager::slotCreateTemplate() { if (!document()) return; @@ -1261,5 +1284,3 @@ d->actionAuthor->setCurrentItem(0); } } - - diff --git a/libs/ui/actions/kis_selection_action_factories.cpp b/libs/ui/actions/kis_selection_action_factories.cpp --- a/libs/ui/actions/kis_selection_action_factories.cpp +++ b/libs/ui/actions/kis_selection_action_factories.cpp @@ -369,6 +369,7 @@ { KisImageWSP image = view->image(); if (!image) return; + if (!view->blockUntillOperationsFinished(image)) return; image->barrierLock(); KisPaintDeviceSP dev = image->root()->projection(); diff --git a/libs/ui/canvas/kis_canvas2.cpp b/libs/ui/canvas/kis_canvas2.cpp --- a/libs/ui/canvas/kis_canvas2.cpp +++ b/libs/ui/canvas/kis_canvas2.cpp @@ -286,15 +286,15 @@ void KisCanvas2::channelSelectionChanged() { - KisImageWSP image = this->image(); + KisImageSP image = this->image(); m_d->channelFlags = image->rootLayer()->channelFlags(); + m_d->view->viewManager()->blockUntillOperationsFinishedForced(image); + image->barrierLock(); m_d->canvasWidget->channelSelectionChanged(m_d->channelFlags); startUpdateInPatches(image->bounds()); - image->unlock(); - } void KisCanvas2::addCommand(KUndo2Command *command) @@ -522,12 +522,12 @@ void KisCanvas2::setDisplayFilter(QSharedPointer displayFilter) { m_d->displayColorConverter.setDisplayFilter(displayFilter); - KisImageWSP image = this->image(); + KisImageSP image = this->image(); - image->barrierLock(); + m_d->view->viewManager()->blockUntillOperationsFinishedForced(image); + image->barrierLock(); m_d->canvasWidget->setDisplayFilter(displayFilter); - image->unlock(); } diff --git a/libs/ui/dialogs/kis_delayed_save_dialog.h b/libs/ui/dialogs/kis_delayed_save_dialog.h --- a/libs/ui/dialogs/kis_delayed_save_dialog.h +++ b/libs/ui/dialogs/kis_delayed_save_dialog.h @@ -32,7 +32,14 @@ Q_OBJECT public: - explicit KisDelayedSaveDialog(KisImageSP image, QWidget *parent = 0); + enum Type { + SaveDialog, + GeneralDialog, + ForcedDialog + }; + +public: + explicit KisDelayedSaveDialog(KisImageSP image, Type type, int busyWait, QWidget *parent = 0); ~KisDelayedSaveDialog(); void blockIfImageIsBusy(); diff --git a/libs/ui/dialogs/kis_delayed_save_dialog.cpp b/libs/ui/dialogs/kis_delayed_save_dialog.cpp --- a/libs/ui/dialogs/kis_delayed_save_dialog.cpp +++ b/libs/ui/dialogs/kis_delayed_save_dialog.cpp @@ -20,32 +20,46 @@ #include "ui_kis_delayed_save_dialog.h" #include +#include +#include #include "kis_debug.h" #include "kis_image.h" #include "kis_composite_progress_proxy.h" struct KisDelayedSaveDialog::Private { - Private(KisImageSP _image) : image(_image) {} + Private(KisImageSP _image, int _busyWait) : image(_image), busyWait(_busyWait) {} KisImageSP image; QTimer updateTimer; + int busyWait; }; -KisDelayedSaveDialog::KisDelayedSaveDialog(KisImageSP image, QWidget *parent) +KisDelayedSaveDialog::KisDelayedSaveDialog(KisImageSP image, Type type, int busyWait, QWidget *parent) : QDialog(parent), ui(new Ui::KisDelayedSaveDialog), - m_d(new Private(image)) + m_d(new Private(image, busyWait)) { KIS_ASSERT_RECOVER_NOOP(image); ui->setupUi(this); - connect(ui->bnDontWait, SIGNAL(clicked()), SLOT(accept())); + if (type == SaveDialog) { + connect(ui->bnDontWait, SIGNAL(clicked()), SLOT(accept())); + connect(ui->bnCancel, SIGNAL(clicked()), SLOT(slotCancelRequested())); + } else { + ui->bnDontSave->setText(i18n("Cancel")); + ui->bnDontWait->setVisible(false); + ui->bnCancel->setVisible(false); + + if (type == ForcedDialog) { + ui->bnDontSave->setVisible(false); + } + } + connect(ui->bnDontSave, SIGNAL(clicked()), SLOT(reject())); - connect(ui->bnCancel, SIGNAL(clicked()), SLOT(slotCancelRequested())); connect(&m_d->updateTimer, SIGNAL(timeout()), SLOT(slotTimerTimeout())); @@ -67,6 +81,20 @@ m_d->image->requestStrokeEnd(); + QElapsedTimer t; + t.start(); + + while (t.elapsed() < m_d->busyWait) { + QApplication::processEvents(); + + if (m_d->image->isIdle()) { + setResult(Accepted); + return; + } + + QThread::yieldCurrentThread(); + } + m_d->updateTimer.start(200); exec(); m_d->updateTimer.stop(); diff --git a/libs/ui/kis_filter_manager.cc b/libs/ui/kis_filter_manager.cc --- a/libs/ui/kis_filter_manager.cc +++ b/libs/ui/kis_filter_manager.cc @@ -196,7 +196,9 @@ * The UI should show only after every running stroke is finished, * so a virtual barrier is added here. */ - d->view->image()->waitForDone(); + if (!d->view->blockUntillOperationsFinished(d->view->image())) { + return; + } Q_ASSERT(d->view); Q_ASSERT(d->view->activeNode()); diff --git a/libs/ui/kis_layer_manager.cc b/libs/ui/kis_layer_manager.cc --- a/libs/ui/kis_layer_manager.cc +++ b/libs/ui/kis_layer_manager.cc @@ -570,6 +570,8 @@ KisLayerSP layer = activeLayer(); if (!layer) return; + if (!m_view->blockUntillOperationsFinished(m_view->image())) return; + m_view->image()->rotateNode(layer, radians); } @@ -580,12 +582,16 @@ KisLayerSP layer = activeLayer(); if (!layer) return; + if (!m_view->blockUntillOperationsFinished(m_view->image())) return; + m_view->image()->shearNode(layer, angleX, angleY); } void KisLayerManager::flattenImage() { - KisImageWSP image = m_view->image(); + KisImageSP image = m_view->image(); + + if (!m_view->blockUntillOperationsFinished(image)) return; if (image) { bool doIt = true; @@ -634,12 +640,14 @@ void KisLayerManager::mergeLayer() { - KisImageWSP image = m_view->image(); + KisImageSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; + if (!m_view->blockUntillOperationsFinished(image)) return; + QList selectedNodes = m_view->nodeManager()->selectedNodes(); if (selectedNodes.size() > 1) { image->mergeMultipleLayers(selectedNodes, m_view->activeNode()); @@ -665,24 +673,28 @@ void KisLayerManager::flattenLayer() { - KisImageWSP image = m_view->image(); + KisImageSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; + if (!m_view->blockUntillOperationsFinished(image)) return; + image->flattenLayer(layer); m_view->updateGUI(); } void KisLayerManager::rasterizeLayer() { - KisImageWSP image = m_view->image(); + KisImageSP image = m_view->image(); if (!image) return; KisLayerSP layer = activeLayer(); if (!layer) return; + if (!m_view->blockUntillOperationsFinished(image)) return; + KisPaintLayerSP paintLayer = new KisPaintLayer(image, layer->name(), layer->opacity()); KisPainter gc(paintLayer->paintDevice()); QRect rc = layer->projection()->exactBounds(); @@ -749,7 +761,7 @@ QString extension = KisMimeDatabase::suffixesForMimeType(mimeType).first(); QString basename = f.baseName(); - KisImageWSP image = m_view->image(); + KisImageSP image = m_view->image(); if (!image) return; KisSaveGroupVisitor v(image, chkInvisible->isChecked(), chkDepth->isChecked(), f.absolutePath(), basename, extension, mimeType); @@ -805,6 +817,8 @@ KisLayerSP layer = activeLayer(); if (!layer) return; + if (!m_view->blockUntillOperationsFinished(image)) return; + KisPSDLayerStyleSP oldStyle; if (layer->layerStyle()) { oldStyle = layer->layerStyle()->clone(); diff --git a/libs/ui/kis_node_manager.cpp b/libs/ui/kis_node_manager.cpp --- a/libs/ui/kis_node_manager.cpp +++ b/libs/ui/kis_node_manager.cpp @@ -147,12 +147,13 @@ } else { KoShape * shape = view->document()->shapeForNode(node); - Q_ASSERT(shape); + KIS_ASSERT_RECOVER_RETURN_VALUE(shape, false); selection->select(shape); KoShapeLayer * shapeLayer = dynamic_cast(shape); - Q_ASSERT(shapeLayer); + KIS_ASSERT_RECOVER_RETURN_VALUE(shapeLayer, false); + // shapeLayer->setGeometryProtected(node->userLocked()); // shapeLayer->setVisible(node->visible()); selection->setActiveLayer(shapeLayer); diff --git a/libs/ui/tool/kis_tool.cc b/libs/ui/tool/kis_tool.cc --- a/libs/ui/tool/kis_tool.cc +++ b/libs/ui/tool/kis_tool.cc @@ -550,6 +550,10 @@ KisResourcesSnapshotSP resources = new KisResourcesSnapshot(image(), currentNode(), this->canvas()->resourceManager()); + KisCanvas2 * kiscanvas = static_cast(canvas()); + KisViewManager* viewManager = kiscanvas->viewManager(); + viewManager->blockUntillOperationsFinished(image()); + if (!KisToolUtils::clearImage(image(), resources->currentNode(), resources->activeSelection())) { KoToolBase::deleteSelection(); } diff --git a/plugins/extensions/clonesarray/dlg_clonesarray.cpp b/plugins/extensions/clonesarray/dlg_clonesarray.cpp --- a/plugins/extensions/clonesarray/dlg_clonesarray.cpp +++ b/plugins/extensions/clonesarray/dlg_clonesarray.cpp @@ -202,9 +202,9 @@ { cancelClicked(); - KisImageWSP image = m_view->image(); - image->barrierLock(); - image->unlock(); + KisImageSP image = m_view->image(); + + if (!m_view->blockUntillOperationsFinished(image)) return; m_applicator = new KisProcessingApplicator(image, 0, diff --git a/plugins/extensions/waveletdecompose/waveletdecompose.cpp b/plugins/extensions/waveletdecompose/waveletdecompose.cpp --- a/plugins/extensions/waveletdecompose/waveletdecompose.cpp +++ b/plugins/extensions/waveletdecompose/waveletdecompose.cpp @@ -82,6 +82,8 @@ KisImageSP image = m_view->image(); if (!image) return; + if (!m_view->blockUntillOperationsFinished(image)) return; + image->barrierLock(); KisPaintDeviceSP projection = new KisPaintDevice(*(image->projection()), false, 0);