diff --git a/src/dolphinmainwindow.h b/src/dolphinmainwindow.h --- a/src/dolphinmainwindow.h +++ b/src/dolphinmainwindow.h @@ -501,6 +501,12 @@ */ 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,9 @@ QMenu m_searchTools; + int m_expectedUnmountSignals; + int m_unmountSignals; + bool m_updateTerminalPanel; }; inline DolphinViewContainer* DolphinMainWindow::activeViewContainer() const diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -116,7 +116,10 @@ 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); @@ -159,6 +162,10 @@ 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(); @@ -1273,19 +1280,49 @@ void DolphinMainWindow::slotStorageTearDownFromPlacesRequested(const QString& 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(); + } } } void DolphinMainWindow::slotStorageTearDownExternallyRequested(const QString& 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/src/dolphintabpage.h b/src/dolphintabpage.h --- a/src/dolphintabpage.h +++ b/src/dolphintabpage.h @@ -135,9 +135,16 @@ */ 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); + signals: void activeViewChanged(DolphinViewContainer* viewContainer); void activeViewUrlChanged(const QUrl& url); + void preparedToUnmount(); private slots: /** diff --git a/src/dolphintabpage.cpp b/src/dolphintabpage.cpp --- a/src/dolphintabpage.cpp +++ b/src/dolphintabpage.cpp @@ -24,6 +24,7 @@ #include #include +#include DolphinTabPage::DolphinTabPage(const QUrl &primaryUrl, const QUrl &secondaryUrl, QWidget* parent) : QWidget(parent), @@ -310,6 +311,29 @@ activeViewContainer()->setActive(active); } +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(); @@ -377,5 +401,8 @@ connect(view, &DolphinView::toggleActiveViewRequested, this, &DolphinTabPage::switchActiveView); + connect(view, &DolphinView::preparedToUnmount, + this, &DolphinTabPage::preparedToUnmount); + return container; } diff --git a/src/dolphintabwidget.h b/src/dolphintabwidget.h --- a/src/dolphintabwidget.h +++ b/src/dolphintabwidget.h @@ -84,6 +84,12 @@ */ 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); + signals: /** * Is emitted when the active view has been changed, by changing the current @@ -109,6 +115,12 @@ */ void currentUrlChanged(const QUrl& url); + /** + * Is emitted when prepareToUnmount() is called + * and all preparations are finished + */ + void preparedToUnmount(); + public slots: /** * Opens a new view with the current URL that is part of a tab and activates diff --git a/src/dolphintabwidget.cpp b/src/dolphintabwidget.cpp --- a/src/dolphintabwidget.cpp +++ b/src/dolphintabwidget.cpp @@ -175,6 +175,8 @@ this, &DolphinTabWidget::activeViewChanged); connect(tabPage, &DolphinTabPage::activeViewUrlChanged, this, &DolphinTabWidget::tabUrlChanged); + connect(tabPage, &DolphinTabPage::preparedToUnmount, + this, &DolphinTabWidget::preparedToUnmount); int newTabIndex = -1; if (tabPlacement == AfterCurrentTab) { newTabIndex = currentIndex() + 1; @@ -320,6 +322,19 @@ currentTabPage()->restoreState(state); } +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/src/kitemviews/kfileitemlistview.h b/src/kitemviews/kfileitemlistview.h --- a/src/kitemviews/kfileitemlistview.h +++ b/src/kitemviews/kfileitemlistview.h @@ -73,6 +73,15 @@ QPixmap createDragPixmap(const KItemSet& indexes) const override; + /** + * Is called when some partition is going to be unmounted + * to to prevent blocking pending unmounting + */ + void prepareToUnmount(); + +signals: + void preparedToUnmount(); + protected: KItemListWidgetCreatorBase* defaultWidgetCreator() const override; void initializeItemListWidget(KItemListWidget* item) override; diff --git a/src/kitemviews/kfileitemlistview.cpp b/src/kitemviews/kfileitemlistview.cpp --- a/src/kitemviews/kfileitemlistview.cpp +++ b/src/kitemviews/kfileitemlistview.cpp @@ -190,6 +190,15 @@ return dragPixmap; } +void KFileItemListView::prepareToUnmount() +{ + if (m_modelRolesUpdater->isPreviewJobRunning()) { + m_modelRolesUpdater->setPaused(true, false); + } else { + emit preparedToUnmount(); + } +} + KItemListWidgetCreatorBase* KFileItemListView::defaultWidgetCreator() const { return new KItemListWidgetCreator(); @@ -233,6 +242,7 @@ if (current) { m_modelRolesUpdater = new KFileItemModelRolesUpdater(static_cast(current), this); m_modelRolesUpdater->setIconSize(availableIconSize()); + connect(m_modelRolesUpdater, &KFileItemModelRolesUpdater::previewJobFinished, this, &KFileItemListView::preparedToUnmount); applyRolesToModel(); } diff --git a/src/kitemviews/kfileitemmodelrolesupdater.h b/src/kitemviews/kfileitemmodelrolesupdater.h --- a/src/kitemviews/kfileitemmodelrolesupdater.h +++ b/src/kitemviews/kfileitemmodelrolesupdater.h @@ -128,7 +128,7 @@ * 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; /** @@ -154,6 +154,11 @@ */ QStringList enabledPlugins() const; + bool isPreviewJobRunning() const; + +signals: + void previewJobFinished(); + private slots: void slotItemsInserted(const KItemRangeList& itemRanges); void slotItemsRemoved(const KItemRangeList& itemRanges); @@ -279,7 +284,8 @@ Paused, ResolvingSortRole, ResolvingAllRoles, - PreviewJobRunning + PreviewJobRunning, + PausePending }; State m_state; diff --git a/src/kitemviews/kfileitemmodelrolesupdater.cpp b/src/kitemviews/kfileitemmodelrolesupdater.cpp --- a/src/kitemviews/kfileitemmodelrolesupdater.cpp +++ b/src/kitemviews/kfileitemmodelrolesupdater.cpp @@ -147,7 +147,7 @@ { 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 @@ -232,15 +232,19 @@ } } -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; @@ -295,7 +299,7 @@ } #endif - if (m_state == Paused) { + if (isPaused()) { m_rolesChangedDuringPausing = true; } else { startUpdating(); @@ -310,7 +314,12 @@ 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 @@ -482,6 +491,12 @@ void KFileItemModelRolesUpdater::slotGotPreview(const KFileItem& item, const QPixmap& pixmap) { + if (m_state == PausePending) { + m_state = Paused; + killPreviewJob(); + emit previewJobFinished(); + } + if (m_state != PreviewJobRunning) { return; } @@ -561,6 +576,12 @@ void KFileItemModelRolesUpdater::slotPreviewFailed(const KFileItem& item) { + if (m_state == PausePending) { + m_state = Paused; + killPreviewJob(); + emit previewJobFinished(); + } + if (m_state != PreviewJobRunning) { return; } @@ -587,6 +608,11 @@ { m_previewJob = nullptr; + if (m_state == PausePending) { + m_state = Paused; + emit previewJobFinished(); + } + if (m_state != PreviewJobRunning) { return; } @@ -778,7 +804,7 @@ void KFileItemModelRolesUpdater::startUpdating() { - if (m_state == Paused) { + if (isPaused()) { return; } @@ -917,7 +943,7 @@ void KFileItemModelRolesUpdater::updateChangedItems() { - if (m_state == Paused) { + if (isPaused()) { return; } @@ -1113,7 +1139,7 @@ void KFileItemModelRolesUpdater::updateAllPreviews() { - if (m_state == Paused) { + if (isPaused()) { m_previewChangedDuringPausing = true; } else { m_finishedItems.clear(); diff --git a/src/views/dolphinview.h b/src/views/dolphinview.h --- a/src/views/dolphinview.h +++ b/src/views/dolphinview.h @@ -317,6 +317,12 @@ */ 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 slots: /** * Changes the directory to \a url. If the current directory is equal to @@ -574,6 +580,12 @@ */ void urlActivated(const QUrl& url); + /** + * 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; diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -173,6 +173,7 @@ this, &DolphinView::slotRoleEditingCanceled); connect(m_view->header(), &KItemListHeader::columnWidthChangeFinished, this, &DolphinView::slotHeaderColumnWidthChangeFinished); + connect(m_view, &KFileItemListView::preparedToUnmount, this, &DolphinView::preparedToUnmount); KItemListSelectionManager* selectionManager = controller->selectionManager(); connect(selectionManager, &KItemListSelectionManager::selectionChanged, @@ -1478,6 +1479,11 @@ } } +void DolphinView::prepareToUnmount() +{ + m_view->prepareToUnmount(); +} + void DolphinView::hideToolTip(const ToolTipManager::HideBehavior behavior) { #ifdef HAVE_BALOO