X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/040e2afcfafaa8edd365d959760d4d0e7f3e48d3..65d7ef184acf97e7881050df88ddace97bde4b93:/src/views/dolphinview.cpp diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp index 66b6c23a8..d42d9cfcd 100644 --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -35,7 +35,7 @@ #include #include #include -#include +#include #include #include #include @@ -49,11 +49,6 @@ #include -#include -#if KIO_VERSION >= QT_VERSION_CHECK(5, 100, 0) -#include -#endif - #include #include #include @@ -92,6 +87,7 @@ DolphinView::DolphinView(const QUrl &url, QWidget *parent) , m_currentItemUrl() , m_scrollToCurrentItem(false) , m_restoredContentsPosition() + , m_controlWheelAccumulatedDelta(0) , m_selectedUrls() , m_clearSelectionBeforeSelectingNewItems(false) , m_markFirstNewlySelectedItemAsCurrent(false) @@ -123,6 +119,7 @@ DolphinView::DolphinView(const QUrl &url, QWidget *parent) KItemListController *controller = new KItemListController(m_model, m_view, this); const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1; controller->setAutoActivationDelay(delay); + connect(controller, &KItemListController::doubleClickViewBackground, this, &DolphinView::doubleClickViewBackground); // The EnlargeSmallPreviews setting can only be changed after the model // has been set in the view by KItemListController. @@ -130,6 +127,9 @@ DolphinView::DolphinView(const QUrl &url, QWidget *parent) m_container = new KItemListContainer(controller, this); m_container->installEventFilter(this); +#ifndef QT_NO_ACCESSIBILITY + m_view->setAccessibleParentsObject(m_container); +#endif setFocusProxy(m_container); connect(m_container->horizontalScrollBar(), &QScrollBar::valueChanged, this, [=] { hideToolTip(); @@ -147,11 +147,13 @@ DolphinView::DolphinView(const QUrl &url, QWidget *parent) // This is made using a heavily-modified QLabel rather than a KTitleWidget // because KTitleWidget can't be told to turn off mouse-selectable text m_placeholderLabel = new QLabel(this); + // Don't consume mouse events + m_placeholderLabel->setAttribute(Qt::WA_TransparentForMouseEvents); + QFont placeholderLabelFont; // To match the size of a level 2 Heading/KTitleWidget placeholderLabelFont.setPointSize(qRound(placeholderLabelFont.pointSize() * 1.3)); m_placeholderLabel->setFont(placeholderLabelFont); - m_placeholderLabel->setTextInteractionFlags(Qt::NoTextInteraction); m_placeholderLabel->setWordWrap(true); m_placeholderLabel->setAlignment(Qt::AlignCenter); // Match opacity of QML placeholder label component @@ -164,10 +166,6 @@ DolphinView::DolphinView(const QUrl &url, QWidget *parent) auto *centeringLayout = new QVBoxLayout(m_container); centeringLayout->addWidget(m_placeholderLabel); centeringLayout->setAlignment(m_placeholderLabel, Qt::AlignCenter); - m_placeholderLabel->setContextMenuPolicy(Qt::CustomContextMenu); - connect(m_placeholderLabel, &QWidget::customContextMenuRequested, this, [this](const QPoint &pos) { - slotViewContextMenuRequested(m_placeholderLabel->mapToGlobal(pos)); - }); controller->setSelectionBehavior(KItemListController::MultiSelection); connect(controller, &KItemListController::itemActivated, this, &DolphinView::slotItemActivated); @@ -201,10 +199,10 @@ DolphinView::DolphinView(const QUrl &url, QWidget *parent) connect(m_model, &KFileItemModel::directoryRedirection, this, &DolphinView::slotDirectoryRedirection); connect(m_model, &KFileItemModel::urlIsFileError, this, &DolphinView::urlIsFileError); connect(m_model, &KFileItemModel::fileItemsChanged, this, &DolphinView::fileItemsChanged); - connect(m_model, &KFileItemModel::currentDirectoryRemoved, this, &DolphinView::currentDirectoryRemoved); + // #473377: Use a QueuedConnection to avoid modifying KCoreDirLister before KCoreDirListerCache::deleteDir() returns. + connect(m_model, &KFileItemModel::currentDirectoryRemoved, this, &DolphinView::currentDirectoryRemoved, Qt::QueuedConnection); connect(this, &DolphinView::itemCountChanged, this, &DolphinView::updatePlaceholderLabel); - connect(this, &DolphinView::itemCountChanged, this, &DolphinView::updateSelectionState); m_view->installEventFilter(this); connect(m_view, &DolphinItemListView::sortOrderChanged, this, &DolphinView::slotSortOrderChangedByHeader); @@ -269,7 +267,6 @@ void DolphinView::setActive(bool active) if (active) { m_container->setFocus(); Q_EMIT activated(); - Q_EMIT writeStateChanged(m_isFolderWritable); } } @@ -281,6 +278,10 @@ bool DolphinView::isActive() const void DolphinView::setViewMode(Mode mode) { if (mode != m_mode) { + // Reset scrollbars before changing the view mode. + m_container->horizontalScrollBar()->setValue(0); + m_container->verticalScrollBar()->setValue(0); + ViewProperties props(viewPropertiesUrl()); props.setViewMode(mode); @@ -307,8 +308,8 @@ void DolphinView::setSelectionModeEnabled(const bool enabled) m_view->setStyle(m_proxyStyle.get()); m_view->setEnabledSelectionToggles(DolphinItemListView::SelectionTogglesEnabled::False); } else { - setStyle(QApplication::style()); - m_view->setStyle(QApplication::style()); + setStyle(nullptr); + m_view->setStyle(nullptr); m_view->setEnabledSelectionToggles(DolphinItemListView::SelectionTogglesEnabled::FollowSetting); } m_container->controller()->setSelectionModeEnabled(enabled); @@ -469,7 +470,13 @@ int DolphinView::zoomLevel() const void DolphinView::setSortRole(const QByteArray &role) { if (role != sortRole()) { - updateSortRole(role); + ViewProperties props(viewPropertiesUrl()); + props.setSortRole(role); + + KItemModelBase *model = m_container->controller()->model(); + model->setSortRole(role); + + Q_EMIT sortRoleChanged(role); } } @@ -482,7 +489,12 @@ QByteArray DolphinView::sortRole() const void DolphinView::setSortOrder(Qt::SortOrder order) { if (sortOrder() != order) { - updateSortOrder(order); + ViewProperties props(viewPropertiesUrl()); + props.setSortOrder(order); + + m_model->setSortOrder(order); + + Q_EMIT sortOrderChanged(order); } } @@ -624,7 +636,7 @@ void DolphinView::requestStatusBarText() return; } - m_statJobForStatusBarText = KIO::statDetails(m_model->rootItem().url(), KIO::StatJob::SourceSide, KIO::StatRecursiveSize, KIO::HideProgressInfo); + m_statJobForStatusBarText = KIO::stat(m_model->rootItem().url(), KIO::StatJob::SourceSide, KIO::StatRecursiveSize, KIO::HideProgressInfo); connect(m_statJobForStatusBarText, &KJob::result, this, &DolphinView::slotStatJobResult); m_statJobForStatusBarText->start(); } @@ -639,11 +651,11 @@ void DolphinView::emitStatusBarText(const int folderCount, const int fileCount, if (selection == HasSelection) { // At least 2 items are selected because the case of 1 selected item is handled in // DolphinView::requestStatusBarText(). - foldersText = i18ncp("@info:status", "1 Folder selected", "%1 Folders selected", folderCount); - filesText = i18ncp("@info:status", "1 File selected", "%1 Files selected", fileCount); + foldersText = i18ncp("@info:status", "1 folder selected", "%1 folders selected", folderCount); + filesText = i18ncp("@info:status", "1 file selected", "%1 files selected", fileCount); } else { - foldersText = i18ncp("@info:status", "1 Folder", "%1 Folders", folderCount); - filesText = i18ncp("@info:status", "1 File", "%1 Files", fileCount); + foldersText = i18ncp("@info:status", "1 folder", "%1 folders", folderCount); + filesText = i18ncp("@info:status", "1 file", "%1 files", fileCount); } if (fileCount > 0 && folderCount > 0) { @@ -653,7 +665,7 @@ void DolphinView::emitStatusBarText(const int folderCount, const int fileCount, } else if (folderCount > 0) { summary = foldersText; } else { - summary = i18nc("@info:status", "0 Folders, 0 Files"); + summary = i18nc("@info:status", "0 folders, 0 files"); } Q_EMIT statusBarTextChanged(summary); } @@ -728,17 +740,18 @@ void DolphinView::renameSelectedItems() if (items.count() == 1 && GeneralSettings::renameInline()) { const int index = m_model->index(items.first()); - QMetaObject::Connection *const connection = new QMetaObject::Connection; - *connection = connect(m_view, &KItemListView::scrollingStopped, this, [=]() { - QObject::disconnect(*connection); - delete connection; - - m_view->editRole(index, "text"); + connect( + m_view, + &KItemListView::scrollingStopped, + this, + [this, index]() { + m_view->editRole(index, "text"); - hideToolTip(); + hideToolTip(); - connect(m_view, &DolphinItemListView::roleEditingFinished, this, &DolphinView::slotRoleEditingFinished); - }); + connect(m_view, &DolphinItemListView::roleEditingFinished, this, &DolphinView::slotRoleEditingFinished); + }, + Qt::SingleShotConnection); m_view->scrollToItem(index); } else { @@ -758,43 +771,22 @@ void DolphinView::trashSelectedItems() { const QList list = simplifiedSelectedUrls(); -#if KIO_VERSION >= QT_VERSION_CHECK(5, 100, 0) using Iface = KIO::AskUserActionInterface; auto *trashJob = new KIO::DeleteOrTrashJob(list, Iface::Trash, Iface::DefaultConfirmation, this); connect(trashJob, &KJob::result, this, &DolphinView::slotTrashFileFinished); m_selectNextItem = true; trashJob->start(); -#else - KIO::JobUiDelegate uiDelegate; - uiDelegate.setWindow(window()); - if (uiDelegate.askDeleteConfirmation(list, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation)) { - KIO::Job *job = KIO::trash(list); - KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Trash, list, QUrl(QStringLiteral("trash:/")), job); - KJobWidgets::setWindow(job, this); - connect(job, &KIO::Job::result, this, &DolphinView::slotTrashFileFinished); - } -#endif } void DolphinView::deleteSelectedItems() { const QList list = simplifiedSelectedUrls(); -#if KIO_VERSION >= QT_VERSION_CHECK(5, 100, 0) using Iface = KIO::AskUserActionInterface; auto *trashJob = new KIO::DeleteOrTrashJob(list, Iface::Delete, Iface::DefaultConfirmation, this); connect(trashJob, &KJob::result, this, &DolphinView::slotTrashFileFinished); m_selectNextItem = true; trashJob->start(); -#else - KIO::JobUiDelegate uiDelegate; - uiDelegate.setWindow(window()); - if (uiDelegate.askDeleteConfirmation(list, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::DefaultConfirmation)) { - KIO::Job *job = KIO::del(list); - KJobWidgets::setWindow(job, this); - connect(job, &KIO::Job::result, this, &DolphinView::slotDeleteFileFinished); - } -#endif } void DolphinView::cutSelectedItemsToClipboard() @@ -814,6 +806,14 @@ void DolphinView::copySelectedItemsToClipboard() void DolphinView::copySelectedItems(const KFileItemList &selection, const QUrl &destinationUrl) { + if (selection.isEmpty() || !destinationUrl.isValid()) { + return; + } + + m_clearSelectionBeforeSelectingNewItems = true; + m_markFirstNewlySelectedItemAsCurrent = true; + m_selectJobCreatedItems = true; + KIO::CopyJob *job = KIO::copy(selection.urlList(), destinationUrl, KIO::DefaultFlags); KJobWidgets::setWindow(job, this); @@ -825,6 +825,14 @@ void DolphinView::copySelectedItems(const KFileItemList &selection, const QUrl & void DolphinView::moveSelectedItems(const KFileItemList &selection, const QUrl &destinationUrl) { + if (selection.isEmpty() || !destinationUrl.isValid()) { + return; + } + + m_clearSelectionBeforeSelectingNewItems = true; + m_markFirstNewlySelectedItemAsCurrent = true; + m_selectJobCreatedItems = true; + KIO::CopyJob *job = KIO::move(selection.urlList(), destinationUrl, KIO::DefaultFlags); KJobWidgets::setWindow(job, this); @@ -856,9 +864,12 @@ void DolphinView::duplicateSelectedItems() const QMimeDatabase db; + m_clearSelectionBeforeSelectingNewItems = true; + m_markFirstNewlySelectedItemAsCurrent = true; + m_selectJobCreatedItems = true; + // Duplicate all selected items and append "copy" to the end of the file name // but before the filename extension, if present - QList newSelection; for (const auto &item : itemList) { const QUrl originalURL = item.url(); const QString originalDirectoryPath = originalURL.adjusted(QUrl::RemoveFilename).path(); @@ -883,15 +894,14 @@ void DolphinView::duplicateSelectedItems() } KIO::CopyJob *job = KIO::copyAs(originalURL, duplicateURL); + job->setAutoRename(true); KJobWidgets::setWindow(job, this); - if (job) { - newSelection << duplicateURL; - KIO::FileUndoManager::self()->recordCopyJob(job); - } + connect(job, &KIO::CopyJob::result, this, &DolphinView::slotJobResult); + connect(job, &KIO::CopyJob::copyingDone, this, &DolphinView::slotItemCreatedFromJob); + connect(job, &KIO::CopyJob::copyingLinkDone, this, &DolphinView::slotItemLinkCreatedFromJob); + KIO::FileUndoManager::self()->recordCopyJob(job); } - - forceUrlsSelection(newSelection.first(), newSelection); } void DolphinView::stopLoading() @@ -1065,24 +1075,14 @@ void DolphinView::slotItemsActivated(const KItemSet &indexes) if (indexes.count() > 5) { QString question = i18np("Are you sure you want to open 1 item?", "Are you sure you want to open %1 items?", indexes.count()); -#if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0) - const int answer = KMessageBox::warningTwoActions( + const int answer = KMessageBox::warningContinueCancel( this, question, {}, -#else - const int answer = - KMessageBox::warningYesNo(this, - question, - {}, -#endif KGuiItem(i18ncp("@action:button", "Open %1 Item", "Open %1 Items", indexes.count()), QStringLiteral("document-open")), - KStandardGuiItem::cancel()); -#if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0) - if (answer != KMessageBox::PrimaryAction) { -#else - if (answer != KMessageBox::Yes) { -#endif + KStandardGuiItem::cancel(), + QStringLiteral("ConfirmOpenManyFolders")); + if (answer != KMessageBox::PrimaryAction && answer != KMessageBox::Continue) { return; } } @@ -1134,6 +1134,8 @@ void DolphinView::slotItemMiddleClicked(int index) } else { Q_EMIT tabRequested(item.url()); } + } else { + Q_EMIT fileMiddleClickActivated(item); } } @@ -1158,7 +1160,7 @@ void DolphinView::slotHeaderContextMenuRequested(const QPointF &pos) { ViewProperties props(viewPropertiesUrl()); - QPointer menu = new QMenu(QApplication::activeWindow()); + QPointer menu = new QMenu(this); KItemListView *view = m_container->controller()->view(); const QList visibleRolesSet = view->visibleRoles(); @@ -1422,6 +1424,11 @@ void DolphinView::slotItemCreatedFromJob(KIO::Job *, const QUrl &, const QUrl &t slotItemCreated(to); } +void DolphinView::slotItemLinkCreatedFromJob(KIO::Job *, const QUrl &, const QString &, const QUrl &to) +{ + slotItemCreated(to); +} + void DolphinView::slotItemCreated(const QUrl &url) { if (m_markFirstNewlySelectedItemAsCurrent) { @@ -1433,6 +1440,16 @@ void DolphinView::slotItemCreated(const QUrl &url) } } +void DolphinView::onDirectoryLoadingCompletedAfterJob() +{ + // the model should now contain all the items created by the job + m_selectJobCreatedItems = true; // to make sure we overwrite selection + // update the view: scroll into View and selection + updateViewState(); + m_selectJobCreatedItems = false; + m_selectedUrls.clear(); +} + void DolphinView::slotJobResult(KJob *job) { if (job->error() && job->error() != KIO::ERR_USER_CANCELED) { @@ -1448,21 +1465,7 @@ void DolphinView::slotJobResult(KJob *job) updateSelectionState(); if (!m_selectedUrls.isEmpty()) { // not all urls were found, the model may not be up to date - // TODO KF6 replace with Qt::singleShotConnection - QMetaObject::Connection *const connection = new QMetaObject::Connection; - *connection = connect( - m_model, - &KFileItemModel::directoryLoadingCompleted, - this, - [this, connection]() { - // the model should now contain all the items created by the job - updateSelectionState(); - m_selectJobCreatedItems = false; - m_selectedUrls.clear(); - QObject::disconnect(*connection); - delete connection; - }, - Qt::UniqueConnection); + connect(m_model, &KFileItemModel::directoryLoadingCompleted, this, &DolphinView::onDirectoryLoadingCompletedAfterJob, Qt::SingleShotConnection); } else { m_selectJobCreatedItems = false; m_selectedUrls.clear(); @@ -1519,27 +1522,6 @@ void DolphinView::slotStatJobResult(KJob *job) emitStatusBarText(folderCount, fileCount, totalFileSize, NoSelection); } -void DolphinView::updateSortRole(const QByteArray &role) -{ - ViewProperties props(viewPropertiesUrl()); - props.setSortRole(role); - - KItemModelBase *model = m_container->controller()->model(); - model->setSortRole(role); - - Q_EMIT sortRoleChanged(role); -} - -void DolphinView::updateSortOrder(Qt::SortOrder order) -{ - ViewProperties props(viewPropertiesUrl()); - props.setSortOrder(order); - - m_model->setSortOrder(order); - - Q_EMIT sortOrderChanged(order); -} - void DolphinView::updateSortFoldersFirst(bool foldersFirst) { ViewProperties props(viewPropertiesUrl()); @@ -1733,6 +1715,7 @@ void DolphinView::updateSelectionState() if (!m_selectedUrls.isEmpty()) { KItemListSelectionManager *selectionManager = m_container->controller()->selectionManager(); + const bool shouldScrollToCurrentItem = m_clearSelectionBeforeSelectingNewItems; // if there is a selection already, leave it that way // unless some drop/paste job are in the process of creating items if (!selectionManager->hasSelection() || m_selectJobCreatedItems) { @@ -1757,6 +1740,10 @@ void DolphinView::updateSelectionState() if (!selectedItems.isEmpty()) { selectionManager->beginAnchoredSelection(selectionManager->currentItem()); selectionManager->setSelectedItems(selectedItems); + selectionManager->endAnchoredSelection(); + if (shouldScrollToCurrentItem) { + m_view->scrollToItem(selectedItems.first()); + } } } } @@ -1775,7 +1762,7 @@ void DolphinView::updateViewState() // scroll to current item and reset the state if (m_scrollToCurrentItem) { - m_view->scrollToItem(currentIndex); + m_view->scrollToItem(currentIndex, KItemListView::ViewItemPosition::Middle); m_scrollToCurrentItem = false; } m_currentItemUrl = QUrl(); @@ -1812,6 +1799,11 @@ void DolphinView::hideToolTip(const ToolTipManager::HideBehavior behavior) } } +bool DolphinView::handleSpaceAsNormalKey() const +{ + return !m_container->hasFocus() || m_container->controller()->isSearchAsYouTypeActive(); +} + void DolphinView::slotTwoClicksRenamingTimerTimeout() { const KItemListSelectionManager *selectionManager = m_container->controller()->selectionManager(); @@ -2000,13 +1992,8 @@ void DolphinView::slotRoleEditingFinished(int index, const QByteArray &role, con if (!hiddenFilesShown() && newName.startsWith(QLatin1Char('.')) && !oldItem.name().startsWith(QLatin1Char('.'))) { KGuiItem yesGuiItem(i18nc("@action:button", "Rename and Hide"), QStringLiteral("view-hidden")); -#if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0) const auto code = KMessageBox::questionTwoActions(this, -#else - const auto code = - KMessageBox::questionYesNo(this, -#endif oldItem.isFile() ? i18n("Adding a dot to the beginning of this file's name will hide it from view.\n" "Do you still want to rename it?") : i18n("Adding a dot to the beginning of this folder's name will hide it from view.\n" @@ -2016,11 +2003,7 @@ void DolphinView::slotRoleEditingFinished(int index, const QByteArray &role, con KStandardGuiItem::cancel(), QStringLiteral("ConfirmHide")); -#if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0) if (code == KMessageBox::SecondaryAction) { -#else - if (code == KMessageBox::No) { -#endif return; } } @@ -2043,9 +2026,9 @@ void DolphinView::slotRoleEditingFinished(int index, const QByteArray &role, con KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Rename, {oldUrl}, newUrl, job); job->uiDelegate()->setAutoErrorHandlingEnabled(true); - forceUrlsSelection(newUrl, {newUrl}); - if (!newNameExistsAlready) { + forceUrlsSelection(newUrl, {newUrl}); + // Only connect the result signal if there is no item with the new name // in the model yet, see bug 328262. connect(job, &KJob::result, this, &DolphinView::slotRenamingResult); @@ -2211,8 +2194,16 @@ void DolphinView::pasteToUrl(const QUrl &url) m_clearSelectionBeforeSelectingNewItems = true; m_markFirstNewlySelectedItemAsCurrent = true; m_selectJobCreatedItems = true; - // TODO KF6 use KIO::PasteJob::copyJobStarted to hook to earlier events copying/moving connect(job, &KIO::PasteJob::itemCreated, this, &DolphinView::slotItemCreated); + connect(job, &KIO::PasteJob::copyJobStarted, this, [this](const KIO::CopyJob *copyJob) { + connect(copyJob, &KIO::CopyJob::copying, this, &DolphinView::slotItemCreatedFromJob); + connect(copyJob, &KIO::CopyJob::moving, this, &DolphinView::slotItemCreatedFromJob); + connect(copyJob, &KIO::CopyJob::linking, this, [this](KIO::Job *job, const QString &src, const QUrl &dest) { + Q_UNUSED(job) + Q_UNUSED(src) + slotItemCreated(dest); + }); + }); connect(job, &KIO::PasteJob::result, this, &DolphinView::slotJobResult); } @@ -2263,6 +2254,11 @@ void DolphinView::updateWritableState() } } +bool DolphinView::isFolderWritable() const +{ + return m_isFolderWritable; +} + QUrl DolphinView::viewPropertiesUrl() const { if (m_viewPropertiesContext.isEmpty()) { @@ -2303,7 +2299,7 @@ void DolphinView::copyPathToClipboard() if (clipboard == nullptr) { return; } - clipboard->setText(path); + clipboard->setText(QDir::toNativeSeparators(path)); } void DolphinView::slotIncreaseZoom() @@ -2323,7 +2319,7 @@ void DolphinView::slotSwipeUp() void DolphinView::showLoadingPlaceholder() { - m_placeholderLabel->setText(i18n("Loading...")); + m_placeholderLabel->setText(i18n("Loading…")); m_placeholderLabel->setVisible(true); } @@ -2398,3 +2394,5 @@ bool DolphinView::tryShowNameToolTip(QHelpEvent *event) } return false; } + +#include "moc_dolphinview.cpp"