From 3aa8ddfcaae883848f950603d2c07352efc214b8 Mon Sep 17 00:00:00 2001 From: Aleksei Nikiforov Date: Fri, 27 Mar 2020 10:47:15 +0300 Subject: [PATCH] Fix unmounting during preview generation Request all preview jobs for specific mount point to stop and proceed with unmount request only when all such jobs reported stopping. --- dolphin/src/dolphinmainwindow.cpp | 55 ++++++++++++++++--- dolphin/src/dolphinmainwindow.h | 10 ++++ dolphin/src/dolphintabpage.cpp | 27 +++++++++ dolphin/src/dolphintabpage.h | 7 +++ dolphin/src/dolphintabwidget.cpp | 15 +++++ dolphin/src/dolphintabwidget.h | 12 ++++ dolphin/src/kitemviews/kfileitemlistview.cpp | 10 ++++ dolphin/src/kitemviews/kfileitemlistview.h | 9 +++ .../kitemviews/kfileitemmodelrolesupdater.cpp | 46 ++++++++++++---- .../kitemviews/kfileitemmodelrolesupdater.h | 10 +++- dolphin/src/views/dolphinview.cpp | 6 ++ dolphin/src/views/dolphinview.h | 12 ++++ 12 files changed, 198 insertions(+), 21 deletions(-) diff --git a/dolphin/src/dolphinmainwindow.cpp b/dolphin/src/dolphinmainwindow.cpp index 52b3e8d6..28fffb5b 100644 --- a/dolphin/src/dolphinmainwindow.cpp +++ b/dolphin/src/dolphinmainwindow.cpp @@ -110,7 +110,10 @@ DolphinMainWindow::DolphinMainWindow() : m_placesPanel(nullptr), m_tearDownFromPlacesRequested(false), m_backAction(nullptr), - m_forwardAction(nullptr) + m_forwardAction(nullptr), + m_expectedUnmountSignals(0), + m_unmountSignals(0), + m_updateTerminalPanel(false) { Q_INIT_RESOURCE(dolphin); @@ -154,6 +157,10 @@ DolphinMainWindow::DolphinMainWindow() : this, &DolphinMainWindow::tabCountChanged); connect(m_tabWidget, &DolphinTabWidget::currentUrlChanged, this, &DolphinMainWindow::updateWindowTitle); + // use queued connection to ensure that expected results are counted before signals are processed + connect(m_tabWidget, &DolphinTabWidget::preparedToUnmount, + this, &DolphinMainWindow::slotStorageTearDownFromPlacesRequestTabFinished, + Qt::QueuedConnection); setCentralWidget(m_tabWidget); setupActions(); @@ -1325,12 +1332,36 @@ void DolphinMainWindow::slotStorageTearDownFromPlacesRequested(const QString& mo setViewsToHomeIfMountPathOpen(mountPath); }); - if (m_terminalPanel && m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) { - m_tearDownFromPlacesRequested = true; - m_terminalPanel->goHome(); - // m_placesPanel->proceedWithTearDown() will be called in slotTerminalDirectoryChanged - } else { - m_placesPanel->proceedWithTearDown(); + m_tearDownFromPlacesRequested = true; + m_updateTerminalPanel = (m_terminalPanel && m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)); + m_unmountSignals = 0; + m_expectedUnmountSignals = m_tabWidget->prepareToUnmount(mountPath); + + if (m_expectedUnmountSignals == 0) { + if (m_updateTerminalPanel) { + m_terminalPanel->goHome(); + // m_placesPanel->proceedWithTearDown() will be called in slotTerminalDirectoryChanged + } else { + m_placesPanel->proceedWithTearDown(); + } + } +} + +void DolphinMainWindow::slotStorageTearDownFromPlacesRequestTabFinished() +{ + if ((m_expectedUnmountSignals == 0) || (m_unmountSignals == m_expectedUnmountSignals)) { + return; + } + + ++m_unmountSignals; + + if (m_unmountSignals == m_expectedUnmountSignals) { + if (m_terminalPanel && m_updateTerminalPanel) { + m_terminalPanel->goHome(); + // m_placesPanel->proceedWithTearDown() will be called in slotTerminalDirectoryChanged + } else { + m_placesPanel->proceedWithTearDown(); + } } } @@ -1340,8 +1371,14 @@ void DolphinMainWindow::slotStorageTearDownExternallyRequested(const QString& mo setViewsToHomeIfMountPathOpen(mountPath); }); - if (m_terminalPanel && m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) { - m_tearDownFromPlacesRequested = false; + m_tearDownFromPlacesRequested = false; + m_updateTerminalPanel = (m_terminalPanel && m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)); + m_unmountSignals = 0; + m_expectedUnmountSignals = 0; // Don't count expected signals since dolphin doesn't do anything with it anyway + + m_tabWidget->prepareToUnmount(mountPath); + + if (m_updateTerminalPanel) { m_terminalPanel->goHome(); } } diff --git a/dolphin/src/dolphinmainwindow.h b/dolphin/src/dolphinmainwindow.h index 3a29d1c0..c627ee55 100644 --- a/dolphin/src/dolphinmainwindow.h +++ b/dolphin/src/dolphinmainwindow.h @@ -511,6 +511,12 @@ private Q_SLOTS: */ void slotStorageTearDownFromPlacesRequested(const QString& mountPath); + /** + * This slot is called when the user requested to unmount a removable media + * from the places menu and tab finished preparing for unmounting + */ + void slotStorageTearDownFromPlacesRequestTabFinished(); + /** * This slot is called when the user requested to unmount a removable media * _not_ from the dolphin's places menu (from the notification area for e.g.) @@ -658,6 +664,10 @@ private: KIO::OpenUrlJob *m_lastHandleUrlOpenJob; + int m_expectedUnmountSignals; + int m_unmountSignals; + bool m_updateTerminalPanel; + TerminalPanel* m_terminalPanel; PlacesPanel* m_placesPanel; bool m_tearDownFromPlacesRequested; --- a/dolphin/src/dolphintabpage.cpp +++ b/dolphin/src/dolphintabpage.cpp @@ -10,6 +10,7 @@ #include "dolphin_generalsettings.h" #include "dolphinviewcontainer.h" +#include #include #include #include @@ -405,6 +406,29 @@ void DolphinTabPage::slotAnimationValueC } +int DolphinTabPage::prepareToUnmount(const QString& mountPath) +{ + int result = 0; + + if (m_primaryViewContainer) { + QStorageInfo storageInfo(m_primaryViewContainer->view()->url().toLocalFile()); + if (storageInfo.rootPath() == mountPath) { + m_primaryViewContainer->view()->prepareToUnmount(); + ++result; + } + } + + if (m_secondaryViewContainer) { + QStorageInfo storageInfo(m_secondaryViewContainer->view()->url().toLocalFile()); + if (storageInfo.rootPath() == mountPath) { + m_secondaryViewContainer->view()->prepareToUnmount(); + ++result; + } + } + + return result; +} + void DolphinTabPage::slotViewActivated() { const DolphinView* oldActiveView = activeViewContainer()->view(); @@ -472,6 +496,9 @@ DolphinViewContainer* DolphinTabPage::cr connect(view, &DolphinView::toggleActiveViewRequested, this, &DolphinTabPage::switchActiveView); + connect(view, &DolphinView::preparedToUnmount, + this, &DolphinTabPage::preparedToUnmount); + return container; } diff --git a/dolphin/src/dolphintabpage.h b/dolphin/src/dolphintabpage.h index 57a0c334..525d35f9 100644 --- a/dolphin/src/dolphintabpage.h +++ b/dolphin/src/dolphintabpage.h @@ -134,10 +134,17 @@ public: */ void setActive(bool active); + /** + * Is called when some partition is going to be unmounted + * to to prevent blocking pending unmounting + */ + int prepareToUnmount(const QString& mountPath); + Q_SIGNALS: void activeViewChanged(DolphinViewContainer* viewContainer); void activeViewUrlChanged(const QUrl& url); void splitterMoved(int pos, int index); + void preparedToUnmount(); private Q_SLOTS: /** diff --git a/dolphin/src/dolphintabwidget.cpp b/dolphin/src/dolphintabwidget.cpp --- a/dolphin/src/dolphintabwidget.cpp +++ b/dolphin/src/dolphintabwidget.cpp @@ -162,6 +162,8 @@ void DolphinTabWidget::openNewTab(const this, &DolphinTabWidget::activeViewChanged); connect(tabPage, &DolphinTabPage::activeViewUrlChanged, this, &DolphinTabWidget::tabUrlChanged); + connect(tabPage, &DolphinTabPage::preparedToUnmount, + this, &DolphinTabWidget::preparedToUnmount); connect(tabPage->activeViewContainer(), &DolphinViewContainer::captionChanged, this, [this, tabPage]() { const int tabIndex = indexOf(tabPage); Q_ASSERT(tabIndex >= 0); @@ -347,6 +349,19 @@ void DolphinTabWidget::moveToInactiveSpl } } +int DolphinTabWidget::prepareToUnmount(const QString& mountPath) +{ + int result = 0; + + const int tabCount = count(); + for (int i = 0; i < tabCount; ++i) { + DolphinTabPage* tabPage = tabPageAt(i); + result += tabPage->prepareToUnmount(mountPath); + } + + return result; +} + void DolphinTabWidget::detachTab(int index) { Q_ASSERT(index >= 0); diff --git a/dolphin/src/dolphintabwidget.h b/dolphin/src/dolphintabwidget.h index e0146d7c..db5d8a4d 100644 --- a/dolphin/src/dolphintabwidget.h +++ b/dolphin/src/dolphintabwidget.h @@ -65,6 +65,12 @@ public: */ bool isUrlOpen(const QUrl& url) const; + /** + * Is called when some partition is going to be unmounted + * to to prevent blocking pending unmounting + */ + int prepareToUnmount(const QString& mountPath); + Q_SIGNALS: /** * Is emitted when the active view has been changed, by changing the current @@ -90,6 +96,12 @@ Q_SIGNALS: */ void currentUrlChanged(const QUrl& url); + /** + * Is emitted when prepareToUnmount() is called + * and all preparations are finished + */ + void preparedToUnmount(); + public Q_SLOTS: /** * Opens a new view with the current URL that is part of a tab and activates diff --git a/dolphin/src/kitemviews/kfileitemlistview.cpp b/dolphin/src/kitemviews/kfileitemlistview.cpp index adcc2d79..9e6bfa41 100644 --- a/dolphin/src/kitemviews/kfileitemlistview.cpp +++ b/dolphin/src/kitemviews/kfileitemlistview.cpp @@ -220,6 +220,15 @@ void KFileItemListView::setHoverSequenceState(const QUrl& itemUrl, int seqIdx) } } +void KFileItemListView::prepareToUnmount() +{ + if (m_modelRolesUpdater->isPreviewJobRunning()) { + m_modelRolesUpdater->setPaused(true, false); + } else { + Q_EMIT preparedToUnmount(); + } +} + KItemListWidgetCreatorBase* KFileItemListView::defaultWidgetCreator() const { return new KItemListWidgetCreator(); @@ -269,6 +278,7 @@ void KFileItemListView::onModelChanged(KItemModelBase* current, KItemModelBase* m_modelRolesUpdater = new KFileItemModelRolesUpdater(static_cast(current), this); m_modelRolesUpdater->setIconSize(availableIconSize()); m_modelRolesUpdater->setScanDirectories(scanDirectories()); + connect(m_modelRolesUpdater, &KFileItemModelRolesUpdater::previewJobFinished, this, &KFileItemListView::preparedToUnmount); applyRolesToModel(); } diff --git a/dolphin/src/kitemviews/kfileitemlistview.h b/dolphin/src/kitemviews/kfileitemlistview.h index bfbe85d1..931ab443 100644 --- a/dolphin/src/kitemviews/kfileitemlistview.h +++ b/dolphin/src/kitemviews/kfileitemlistview.h @@ -90,6 +90,15 @@ public: */ void setHoverSequenceState(const QUrl& itemUrl, int seqIdx); + /** + * Is called when some partition is going to be unmounted + * to to prevent blocking pending unmounting + */ + void prepareToUnmount(); + +Q_SIGNALS: + void preparedToUnmount(); + protected: KItemListWidgetCreatorBase* defaultWidgetCreator() const override; void initializeItemListWidget(KItemListWidget* item) override; diff --git a/dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp b/dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp index 978f5df6..308ce062 100644 --- a/dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp +++ b/dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp @@ -142,7 +142,7 @@ void KFileItemModelRolesUpdater::setIconSize(const QSize& size) { if (size != m_iconSize) { m_iconSize = size; - if (m_state == Paused) { + if (isPaused()) { m_iconSizeChangedDuringPausing = true; } else if (m_previewShown) { // An icon size change requires the regenerating of @@ -227,15 +227,19 @@ void KFileItemModelRolesUpdater::setEnabledPlugins(const QStringList& list) } } -void KFileItemModelRolesUpdater::setPaused(bool paused) +void KFileItemModelRolesUpdater::setPaused(bool paused, bool immediate) { - if (paused == (m_state == Paused)) { + if (paused == isPaused()) { return; } if (paused) { - m_state = Paused; - killPreviewJob(); + if (immediate) { + m_state = Paused; + killPreviewJob(); + } else { + m_state = PausePending; + } } else { const bool updatePreviews = (m_iconSizeChangedDuringPausing && m_previewShown) || m_previewChangedDuringPausing; @@ -290,7 +294,7 @@ void KFileItemModelRolesUpdater::setRoles(const QSet& roles) } #endif - if (m_state == Paused) { + if (isPaused()) { m_rolesChangedDuringPausing = true; } else { startUpdating(); @@ -305,7 +309,12 @@ QSet KFileItemModelRolesUpdater::roles() const bool KFileItemModelRolesUpdater::isPaused() const { - return m_state == Paused; + return (m_state == Paused) || (m_state == PausePending); +} + +bool KFileItemModelRolesUpdater::isPreviewJobRunning() const +{ + return m_state == PreviewJobRunning; } QStringList KFileItemModelRolesUpdater::enabledPlugins() const @@ -529,6 +538,12 @@ void KFileItemModelRolesUpdater::slotSortRoleChanged(const QByteArray& current, void KFileItemModelRolesUpdater::slotGotPreview(const KFileItem& item, const QPixmap& pixmap) { + if (m_state == PausePending) { + m_state = Paused; + killPreviewJob(); + Q_EMIT previewJobFinished(); + } + if (m_state != PreviewJobRunning) { return; } @@ -574,6 +589,12 @@ void KFileItemModelRolesUpdater::slotGotPreview(const KFileItem& item, const QPi void KFileItemModelRolesUpdater::slotPreviewFailed(const KFileItem& item) { + if (m_state == PausePending) { + m_state = Paused; + killPreviewJob(); + Q_EMIT previewJobFinished(); + } + if (m_state != PreviewJobRunning) { return; } @@ -600,6 +621,11 @@ void KFileItemModelRolesUpdater::slotPreviewJobFinished() { m_previewJob = nullptr; + if (m_state == PausePending) { + m_state = Paused; + Q_EMIT previewJobFinished(); + } + if (m_state != PreviewJobRunning) { return; } @@ -902,7 +928,7 @@ void KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived(const QStrin void KFileItemModelRolesUpdater::startUpdating() { - if (m_state == Paused) { + if (isPaused()) { return; } @@ -1164,7 +1190,7 @@ void KFileItemModelRolesUpdater::killHoverSequencePreviewJob() void KFileItemModelRolesUpdater::updateChangedItems() { - if (m_state == Paused) { + if (isPaused()) { return; } @@ -1372,7 +1398,7 @@ void KFileItemModelRolesUpdater::slotOverlaysChanged(const QUrl& url, const QStr void KFileItemModelRolesUpdater::updateAllPreviews() { - if (m_state == Paused) { + if (isPaused()) { m_previewChangedDuringPausing = true; } else { m_finishedItems.clear(); diff --git a/dolphin/src/kitemviews/kfileitemmodelrolesupdater.h b/dolphin/src/kitemviews/kfileitemmodelrolesupdater.h index a03ab513..67bb15c5 100644 --- a/dolphin/src/kitemviews/kfileitemmodelrolesupdater.h +++ b/dolphin/src/kitemviews/kfileitemmodelrolesupdater.h @@ -117,7 +117,7 @@ public: * State changes during pauses like changing the icon size or the preview-shown * will be remembered and handled after unpausing. */ - void setPaused(bool paused); + void setPaused(bool paused, bool immediate = true); bool isPaused() const; /** @@ -174,6 +174,11 @@ public: */ void setHoverSequenceState(const QUrl& itemUrl, int seqIdx); + bool isPreviewJobRunning() const; + +Q_SIGNALS: + void previewJobFinished(); + private Q_SLOTS: void slotItemsInserted(const KItemRangeList& itemRanges); void slotItemsRemoved(const KItemRangeList& itemRanges); @@ -343,7 +348,8 @@ private: Paused, ResolvingSortRole, ResolvingAllRoles, - PreviewJobRunning + PreviewJobRunning, + PausePending }; State m_state; diff --git a/dolphin/src/views/dolphinview.cpp b/dolphin/src/views/dolphinview.cpp --- a/dolphin/src/views/dolphinview.cpp +++ b/dolphin/src/views/dolphinview.cpp @@ -192,6 +192,7 @@ DolphinView::DolphinView(const QUrl& url this, &DolphinView::updatePlaceholderLabel); m_view->installEventFilter(this); + connect(m_view, &KFileItemListView::preparedToUnmount, this, &DolphinView::preparedToUnmount); connect(m_view, &DolphinItemListView::sortOrderChanged, this, &DolphinView::slotSortOrderChangedByHeader); connect(m_view, &DolphinItemListView::sortRoleChanged, @@ -1657,6 +1658,11 @@ void DolphinView::updateViewState() } } +void DolphinView::prepareToUnmount() +{ + m_view->prepareToUnmount(); +} + void DolphinView::hideToolTip(const ToolTipManager::HideBehavior behavior) { if (GeneralSettings::showToolTips()) { diff --git a/dolphin/src/views/dolphinview.h b/dolphin/src/views/dolphinview.h index e4d77983..e8a26e6c 100644 --- a/dolphin/src/views/dolphinview.h +++ b/dolphin/src/views/dolphinview.h @@ -315,6 +315,12 @@ public: */ void hideToolTip(const ToolTipManager::HideBehavior behavior = ToolTipManager::HideBehavior::Later); + /** + * Is called when some partition is going to be unmounted + * to to prevent blocking pending unmounting + */ + void prepareToUnmount(); + public Q_SLOTS: /** * Changes the directory to \a url. If the current directory is equal to @@ -595,6 +601,12 @@ Q_SIGNALS: void fileItemsChanged(const KFileItemList &changedFileItems); + /** + * Is emitted when prepareToUnmount() is called + * and all preparations are finished + */ + void preparedToUnmount(); + protected: /** Changes the zoom level if Control is pressed during a wheel event. */ void wheelEvent(QWheelEvent* event) override; -- 2.32.0