diff options
| author | Méven Car <[email protected]> | 2023-05-08 17:46:51 +0200 |
|---|---|---|
| committer | Méven Car <[email protected]> | 2023-05-08 17:46:51 +0200 |
| commit | 863ee3a87cee8b1f22a311d6a6a62e56714b5eae (patch) | |
| tree | 728a2f70486e1835053e5c3431e72c62eb69ff82 /src | |
| parent | 69f6b9b78b71f8369dc0ab9b9f9aecfd928d3eb1 (diff) | |
| parent | 26808a188ccb5e35a05b37709e4fe61263c46032 (diff) | |
Merge branch 'master' into kf6
Diffstat (limited to 'src')
33 files changed, 524 insertions, 143 deletions
diff --git a/src/dolphincontextmenu.cpp b/src/dolphincontextmenu.cpp index 8130772d2..8c52aac39 100644 --- a/src/dolphincontextmenu.cpp +++ b/src/dolphincontextmenu.cpp @@ -260,6 +260,16 @@ void DolphinContextMenu::addItemContextMenu() m_copyToMenu.addActionsTo(this); } + if (m_mainWindow->isSplitViewEnabledInCurrentTab()) { + if (ContextMenuSettings::showCopyToOtherSplitView()) { + addAction(m_mainWindow->actionCollection()->action(QStringLiteral("copy_to_inactive_split_view"))); + } + + if (ContextMenuSettings::showMoveToOtherSplitView()) { + addAction(m_mainWindow->actionCollection()->action(QStringLiteral("move_to_inactive_split_view"))); + } + } + // insert 'Properties...' entry addSeparator(); QAction *propertiesAction = m_mainWindow->actionCollection()->action(QStringLiteral("properties")); diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index cf1b98f87..415f2e8b3 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -88,6 +88,8 @@ #include <QTimer> #include <QToolButton> +#include <algorithm> + namespace { // Used for GeneralSettings::version() to determine whether @@ -269,11 +271,41 @@ bool DolphinMainWindow::isInformationPanelEnabled() const #endif } +bool DolphinMainWindow::isSplitViewEnabledInCurrentTab() const +{ + return m_tabWidget->currentTabPage()->splitViewEnabled(); +} + void DolphinMainWindow::openFiles(const QStringList &files, bool splitView) { openFiles(QUrl::fromStringList(files), splitView); } +bool DolphinMainWindow::isOnCurrentDesktop() const +{ +#if HAVE_X11 + if (KWindowSystem::isPlatformX11()) { + const NET::Properties properties = NET::WMDesktop; + KWindowInfo info(this->winId(), properties); + return info.isOnCurrentDesktop(); + } +#endif + return true; +} + +bool DolphinMainWindow::isOnActivity(const QString &activityId) const +{ +#if HAVE_X11 && HAVE_KACTIVITIES + if (KWindowSystem::isPlatformX11()) { + const NET::Properties properties = NET::Supported; + const NET::Properties2 properties2 = NET::WM2Activities; + KWindowInfo info(this->winId(), properties, properties2); + return info.activities().contains(activityId); + } +#endif + return true; +} + void DolphinMainWindow::activateWindow(const QString &activationToken) { window()->setAttribute(Qt::WA_NativeWindow, true); @@ -519,6 +551,19 @@ void DolphinMainWindow::showTarget() }); } +bool DolphinMainWindow::event(QEvent *event) +{ + if (event->type() == QEvent::ShortcutOverride) { + const QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); + if (keyEvent->key() == Qt::Key_Space && m_activeViewContainer->view()->handleSpaceAsNormalKey()) { + event->accept(); + return true; + } + } + + return KXmlGuiWindow::event(event); +} + void DolphinMainWindow::showEvent(QShowEvent *event) { KXmlGuiWindow::showEvent(event); @@ -1476,7 +1521,7 @@ void DolphinMainWindow::slotStorageTearDownFromPlacesRequested(const QString &mo setViewsToHomeIfMountPathOpen(mountPath); }); - if (m_terminalPanel && m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) { + if (m_terminalPanel && m_terminalPanel->currentWorkingDirectoryIsChildOf(mountPath)) { m_tearDownFromPlacesRequested = true; m_terminalPanel->goHome(); // m_placesPanel->proceedWithTearDown() will be called in slotTerminalDirectoryChanged @@ -1491,7 +1536,7 @@ void DolphinMainWindow::slotStorageTearDownExternallyRequested(const QString &mo setViewsToHomeIfMountPathOpen(mountPath); }); - if (m_terminalPanel && m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) { + if (m_terminalPanel && m_terminalPanel->currentWorkingDirectoryIsChildOf(mountPath)) { m_tearDownFromPlacesRequested = false; m_terminalPanel->goHome(); } @@ -1613,8 +1658,8 @@ void DolphinMainWindow::setupActions() + cutCopyPastePara); QAction *copyToOtherViewAction = actionCollection()->addAction(QStringLiteral("copy_to_inactive_split_view")); - copyToOtherViewAction->setText(i18nc("@action:inmenu", "Copy to Inactive Split View")); - m_actionTextHelper->registerTextWhenNothingIsSelected(copyToOtherViewAction, i18nc("@action:inmenu", "Copy to Inactive Split View…")); + copyToOtherViewAction->setText(i18nc("@action:inmenu", "Copy to Other View")); + m_actionTextHelper->registerTextWhenNothingIsSelected(copyToOtherViewAction, i18nc("@action:inmenu", "Copy to Other View…")); copyToOtherViewAction->setWhatsThis(xi18nc("@info:whatsthis Copy", "This copies the selected items from " "the <emphasis>active</emphasis> view to the inactive split view.")); @@ -1624,8 +1669,8 @@ void DolphinMainWindow::setupActions() connect(copyToOtherViewAction, &QAction::triggered, this, &DolphinMainWindow::copyToInactiveSplitView); QAction *moveToOtherViewAction = actionCollection()->addAction(QStringLiteral("move_to_inactive_split_view")); - moveToOtherViewAction->setText(i18nc("@action:inmenu", "Move to Inactive Split View")); - m_actionTextHelper->registerTextWhenNothingIsSelected(moveToOtherViewAction, i18nc("@action:inmenu", "Move to Inactive Split View…")); + moveToOtherViewAction->setText(i18nc("@action:inmenu", "Move to Other View")); + m_actionTextHelper->registerTextWhenNothingIsSelected(moveToOtherViewAction, i18nc("@action:inmenu", "Move to Other View…")); moveToOtherViewAction->setWhatsThis(xi18nc("@info:whatsthis Move", "This moves the selected items from " "the <emphasis>active</emphasis> view to the inactive split view.")); @@ -1694,6 +1739,7 @@ void DolphinMainWindow::setupActions() "</para>")); toggleSelectionModeAction->setIcon(QIcon::fromTheme(QStringLiteral("quickwizard"))); toggleSelectionModeAction->setCheckable(true); + actionCollection()->setDefaultShortcut(toggleSelectionModeAction, Qt::Key_Space ); connect(toggleSelectionModeAction, &QAction::triggered, this, &DolphinMainWindow::toggleSelectionMode); // A special version of the toggleSelectionModeAction for the toolbar that also contains a menu @@ -2284,7 +2330,7 @@ void DolphinMainWindow::updateFileAndEditActions() duplicateAction->setEnabled(capabilitiesSource.supportsWriting()); } - if (m_tabWidget->currentTabPage()->splitViewEnabled()) { + if (m_tabWidget->currentTabPage()->splitViewEnabled() && !list.isEmpty()) { DolphinTabPage *tabPage = m_tabWidget->currentTabPage(); KFileItem capabilitiesDestination; @@ -2294,8 +2340,14 @@ void DolphinMainWindow::updateFileAndEditActions() capabilitiesDestination = tabPage->primaryViewContainer()->rootItem(); } - copyToOtherViewAction->setEnabled(capabilitiesDestination.isWritable()); - moveToOtherViewAction->setEnabled((list.isEmpty() || capabilitiesSource.supportsMoving()) && capabilitiesDestination.isWritable()); + const auto destUrl = capabilitiesDestination.url(); + const bool allNotTargetOrigin = std::all_of(list.cbegin(), list.cend(), [destUrl](const KFileItem &item) { + return item.url().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash) != destUrl; + }); + + copyToOtherViewAction->setEnabled(capabilitiesDestination.isWritable() && allNotTargetOrigin); + moveToOtherViewAction->setEnabled((list.isEmpty() || capabilitiesSource.supportsMoving()) && capabilitiesDestination.isWritable() + && allNotTargetOrigin); } else { copyToOtherViewAction->setEnabled(false); moveToOtherViewAction->setEnabled(false); diff --git a/src/dolphinmainwindow.h b/src/dolphinmainwindow.h index 3a97f557c..92ddb24c3 100644 --- a/src/dolphinmainwindow.h +++ b/src/dolphinmainwindow.h @@ -118,6 +118,7 @@ public: bool isFoldersPanelEnabled() const; bool isInformationPanelEnabled() const; + bool isSplitViewEnabledInCurrentTab() const; public Q_SLOTS: /** @@ -203,6 +204,9 @@ public Q_SLOTS: /** @see GeneralSettings::splitViewChanged() */ void slotSplitViewChanged(); + bool isOnActivity(const QString &activityId) const; + bool isOnCurrentDesktop() const; + Q_SIGNALS: /** * Is sent if the selection of the currently active view has @@ -234,6 +238,9 @@ Q_SIGNALS: void settingsChanged(); protected: + /** @see QObject::event() */ + bool event(QEvent *event) override; + /** @see QWidget::showEvent() */ void showEvent(QShowEvent *event) override; diff --git a/src/dolphinpartactions.desktop b/src/dolphinpartactions.desktop index ae36ac0d9..9a547f0ca 100644 --- a/src/dolphinpartactions.desktop +++ b/src/dolphinpartactions.desktop @@ -107,7 +107,7 @@ Name[nb]=Kompakt Name[nl]=Compact Name[nn]=Kompakt Name[pa]=ਸੰਖੇਪ -Name[pl]=Kompaktowy +Name[pl]=Zwarty Name[pt]=Compacta Name[pt_BR]=Compacto Name[ro]=Compactă diff --git a/src/dolphintabpage.cpp b/src/dolphintabpage.cpp index c8da707df..2979cb568 100644 --- a/src/dolphintabpage.cpp +++ b/src/dolphintabpage.cpp @@ -164,6 +164,15 @@ DolphinViewContainer *DolphinTabPage::activeViewContainer() const return m_primaryViewActive ? m_primaryViewContainer : m_secondaryViewContainer; } +DolphinViewContainer *DolphinTabPage::inactiveViewContainer() const +{ + if (!splitViewEnabled()) { + return nullptr; + } + + return primaryViewActive() ? secondaryViewContainer() : primaryViewContainer(); +} + KFileItemList DolphinTabPage::selectedItems() const { KFileItemList items = m_primaryViewContainer->view()->selectedItems(); diff --git a/src/dolphintabpage.h b/src/dolphintabpage.h index 1c8ae094b..4e89d22ee 100644 --- a/src/dolphintabpage.h +++ b/src/dolphintabpage.h @@ -67,6 +67,12 @@ public: DolphinViewContainer *activeViewContainer() const; /** + * @return DolphinViewContainer of the inactive view + * if split view is enabled, or nullptr otherwise. + */ + DolphinViewContainer *inactiveViewContainer() const; + + /** * Returns the selected items. The list is empty if no item has been * selected. */ diff --git a/src/dolphintabwidget.cpp b/src/dolphintabwidget.cpp index bb3680f58..d4271847f 100644 --- a/src/dolphintabwidget.cpp +++ b/src/dolphintabwidget.cpp @@ -325,36 +325,34 @@ void DolphinTabWidget::restoreClosedTab(const QByteArray &state) void DolphinTabWidget::copyToInactiveSplitView() { - const DolphinTabPage *tabPage = tabPageAt(currentIndex()); - DolphinViewContainer *activeViewContainer = currentTabPage()->activeViewContainer(); - if (!tabPage->splitViewEnabled() || activeViewContainer->view()->selectedItems().isEmpty()) { + const DolphinTabPage *tabPage = currentTabPage(); + if (!tabPage->splitViewEnabled()) { return; } - if (tabPage->primaryViewActive()) { - // copy from left panel to right - activeViewContainer->view()->copySelectedItems(activeViewContainer->view()->selectedItems(), tabPage->secondaryViewContainer()->url()); - } else { - // copy from right panel to left - activeViewContainer->view()->copySelectedItems(activeViewContainer->view()->selectedItems(), tabPage->primaryViewContainer()->url()); + const KFileItemList selectedItems = tabPage->activeViewContainer()->view()->selectedItems(); + if (selectedItems.isEmpty()) { + return; } + + DolphinView *const inactiveView = tabPage->inactiveViewContainer()->view(); + inactiveView->copySelectedItems(selectedItems, inactiveView->url()); } void DolphinTabWidget::moveToInactiveSplitView() { - const DolphinTabPage *tabPage = tabPageAt(currentIndex()); - DolphinViewContainer *activeViewContainer = currentTabPage()->activeViewContainer(); - if (!tabPage->splitViewEnabled() || activeViewContainer->view()->selectedItems().isEmpty()) { + const DolphinTabPage *tabPage = currentTabPage(); + if (!tabPage->splitViewEnabled()) { return; } - if (tabPage->primaryViewActive()) { - // move from left panel to right - activeViewContainer->view()->moveSelectedItems(activeViewContainer->view()->selectedItems(), tabPage->secondaryViewContainer()->url()); - } else { - // move from right panel to left - activeViewContainer->view()->moveSelectedItems(activeViewContainer->view()->selectedItems(), tabPage->primaryViewContainer()->url()); + const KFileItemList selectedItems = tabPage->activeViewContainer()->view()->selectedItems(); + if (selectedItems.isEmpty()) { + return; } + + DolphinView *const inactiveView = tabPage->inactiveViewContainer()->view(); + inactiveView->moveSelectedItems(selectedItems, inactiveView->url()); } void DolphinTabWidget::detachTab(int index) diff --git a/src/filterbar/filterbar.cpp b/src/filterbar/filterbar.cpp index 76e23d420..e24025c5b 100644 --- a/src/filterbar/filterbar.cpp +++ b/src/filterbar/filterbar.cpp @@ -10,6 +10,7 @@ #include <KLocalizedString> +#include <QApplication> #include <QHBoxLayout> #include <QKeyEvent> #include <QLineEdit> @@ -47,6 +48,9 @@ FilterBar::FilterBar(QWidget *parent) hLayout->addWidget(m_lockButton); hLayout->addWidget(m_filterInput); hLayout->addWidget(closeButton); + + setTabOrder(m_lockButton, closeButton); + setTabOrder(closeButton, m_filterInput); } FilterBar::~FilterBar() @@ -96,10 +100,8 @@ void FilterBar::showEvent(QShowEvent *event) } } -void FilterBar::keyReleaseEvent(QKeyEvent *event) +void FilterBar::keyPressEvent(QKeyEvent *event) { - QWidget::keyReleaseEvent(event); - switch (event->key()) { case Qt::Key_Escape: if (m_filterInput->text().isEmpty()) { @@ -107,14 +109,28 @@ void FilterBar::keyReleaseEvent(QKeyEvent *event) } else { m_filterInput->clear(); } - break; + return; case Qt::Key_Enter: case Qt::Key_Return: Q_EMIT focusViewRequest(); - break; + return; + + case Qt::Key_Down: + case Qt::Key_PageDown: + case Qt::Key_Up: + case Qt::Key_PageUp: { + Q_EMIT focusViewRequest(); + QWidget *focusWidget = QApplication::focusWidget(); + if (focusWidget && focusWidget != this) { + QApplication::sendEvent(focusWidget, event); + } + return; + } default: break; } + + QWidget::keyPressEvent(event); } diff --git a/src/filterbar/filterbar.h b/src/filterbar/filterbar.h index 8a0b81431..353055883 100644 --- a/src/filterbar/filterbar.h +++ b/src/filterbar/filterbar.h @@ -62,7 +62,7 @@ Q_SIGNALS: protected: void showEvent(QShowEvent *event) override; - void keyReleaseEvent(QKeyEvent *event) override; + void keyPressEvent(QKeyEvent *event) override; private: QLineEdit *m_filterInput; diff --git a/src/global.cpp b/src/global.cpp index 554eb41fa..8babbbddc 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -16,6 +16,9 @@ #include <KIO/ApplicationLauncherJob> #include <KService> #include <KWindowSystem> +#ifdef HAVE_KACTIVITIES +#include <KActivities/Consumer> +#endif #include <QApplication> @@ -140,13 +143,37 @@ bool Dolphin::attachToExistingInstance(const QList<QUrl> &inputUrls, QVector<QPair<QSharedPointer<OrgKdeDolphinMainWindowInterface>, QStringList>> Dolphin::dolphinGuiInstances(const QString &preferredService) { +#ifdef HAVE_KACTIVITIES + static std::once_flag one_consumer; + static KActivities::Consumer *consumer; + std::call_once(one_consumer, []() { + consumer = new KActivities::Consumer(); + // ensures the consumer is ready for query + QEventLoop loop; + QObject::connect(consumer, &KActivities::Consumer::serviceStatusChanged, &loop, &QEventLoop::quit); + loop.exec(); + }); +#endif + QVector<QPair<QSharedPointer<OrgKdeDolphinMainWindowInterface>, QStringList>> dolphinInterfaces; - if (!preferredService.isEmpty()) { - QSharedPointer<OrgKdeDolphinMainWindowInterface> preferredInterface( - new OrgKdeDolphinMainWindowInterface(preferredService, QStringLiteral("/dolphin/Dolphin_1"), QDBusConnection::sessionBus())); - if (preferredInterface->isValid() && !preferredInterface->lastError().isValid()) { - dolphinInterfaces.append(qMakePair(preferredInterface, QStringList())); + const auto tryAppendInterface = [&dolphinInterfaces](const QString &service) { + // Check if instance can handle our URLs + QSharedPointer<OrgKdeDolphinMainWindowInterface> interface( + new OrgKdeDolphinMainWindowInterface(service, QStringLiteral("/dolphin/Dolphin_1"), QDBusConnection::sessionBus())); + if (interface->isValid() && !interface->lastError().isValid()) { +#ifdef HAVE_KACTIVITIES + const auto currentActivity = consumer->currentActivity(); + if (currentActivity.isEmpty() || currentActivity == QStringLiteral("00000000-0000-0000-0000-000000000000") + || interface->isOnActivity(consumer->currentActivity())) +#endif + if (interface->isOnCurrentDesktop()) { + dolphinInterfaces.append(qMakePair(interface, QStringList())); + } } + }; + + if (!preferredService.isEmpty()) { + tryAppendInterface(preferredService); } // Look for dolphin instances among all available dbus services. @@ -158,12 +185,7 @@ QVector<QPair<QSharedPointer<OrgKdeDolphinMainWindowInterface>, QStringList>> Do const QString myPid = QLatin1Char('-') + QString::number(QCoreApplication::applicationPid()); for (const QString &service : dbusServices) { if (service.startsWith(pattern) && !service.endsWith(myPid)) { - // Check if instance can handle our URLs - QSharedPointer<OrgKdeDolphinMainWindowInterface> interface( - new OrgKdeDolphinMainWindowInterface(service, QStringLiteral("/dolphin/Dolphin_1"), QDBusConnection::sessionBus())); - if (interface->isValid() && !interface->lastError().isValid()) { - dolphinInterfaces.append(qMakePair(interface, QStringList())); - } + tryAppendInterface(service); } } diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index c7b3af76b..ebcd4b912 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -324,8 +324,13 @@ int KFileItemModel::indexForKeyboardSearch(const QString &text, int startFromInd bool KFileItemModel::supportsDropping(int index) const { - const KFileItem item = fileItem(index); - return !item.isNull() && (item.isDir() || item.isDesktopFile()); + KFileItem item; + if (index == -1) { + item = rootItem(); + } else { + item = fileItem(index); + } + return !item.isNull() && ((item.isDir() && item.isWritable()) || item.isDesktopFile()); } QString KFileItemModel::roleDescription(const QByteArray &role) const diff --git a/src/kitemviews/kfileitemmodelrolesupdater.cpp b/src/kitemviews/kfileitemmodelrolesupdater.cpp index 4ff730652..8d5656daf 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.cpp +++ b/src/kitemviews/kfileitemmodelrolesupdater.cpp @@ -1214,8 +1214,9 @@ void KFileItemModelRolesUpdater::applySortRole(int index) } data.insert("type", item.mimeComment()); - } else if (m_model->sortRole() == "size" && item.isLocalFile() && !item.isSlow() && item.isDir()) { + } else if (m_model->sortRole() == "size" && item.isLocalFile() && item.isDir()) { startDirectorySizeCounting(item, index); + return; } else { // Probably the sort role is a baloo role - just determine all roles. data = rolesData(item, index); @@ -1277,6 +1278,10 @@ bool KFileItemModelRolesUpdater::applyResolvedRoles(int index, ResolveHint hint) void KFileItemModelRolesUpdater::startDirectorySizeCounting(const KFileItem &item, int index) { + if (item.isSlow()) { + return; + } + // Tell m_directoryContentsCounter that we want to count the items // inside the directory. The result will be received in slotDirectoryContentsCountReceived. if (m_scanDirectories && item.isLocalFile()) { @@ -1294,7 +1299,7 @@ QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileIte const bool getSizeRole = m_roles.contains("size"); const bool getIsExpandableRole = m_roles.contains("isExpandable"); - if ((getSizeRole || getIsExpandableRole) && !item.isSlow() && item.isDir()) { + if ((getSizeRole || getIsExpandableRole) && item.isDir()) { startDirectorySizeCounting(item, index); } diff --git a/src/kitemviews/kitemlistcontroller.cpp b/src/kitemviews/kitemlistcontroller.cpp index 54a856fb8..2e7d2f057 100644 --- a/src/kitemviews/kitemlistcontroller.cpp +++ b/src/kitemviews/kitemlistcontroller.cpp @@ -228,6 +228,11 @@ bool KItemListController::selectionMode() const return m_selectionMode; } +bool KItemListController::isSearchAsYouTypeActive() const +{ + return m_keyboardManager->isSearchAsYouTypeActive(); +} + bool KItemListController::keyPressEvent(QKeyEvent *event) { int index = m_selectionManager->currentItem(); @@ -439,10 +444,13 @@ bool KItemListController::keyPressEvent(QKeyEvent *event) m_selectionManager->setSelected(index, 1, KItemListSelectionManager::Toggle); m_selectionManager->beginAnchoredSelection(index); break; - } else if (m_keyboardManager->addKeyBeginsNewSearch()) { // File names shouldn't start with a space, - // so we can use this press as a keyboard shortcut instead. - Q_EMIT selectionModeChangeRequested(!m_selectionMode); - break; + } else { + // Select the current item if it is not selected yet. + const int current = m_selectionManager->currentItem(); + if (!m_selectionManager->isSelected(current)) { + m_selectionManager->setSelected(current); + break; + } } } Q_FALLTHROUGH(); // fall through to the default case and add the Space to the current search string. @@ -534,7 +542,7 @@ void KItemListController::slotAutoActivationTimeout() * * See Bug 293200 and 305783 */ - if (m_model->supportsDropping(index) && m_view->isUnderMouse()) { + if (m_view->isUnderMouse()) { if (m_view->supportsItemExpanding() && m_model->isExpandable(index)) { const bool expanded = m_model->isExpanded(index); m_model->setExpanded(index, !expanded); @@ -738,6 +746,7 @@ bool KItemListController::dragMoveEvent(QGraphicsSceneDragDropEvent *event, cons const QPointF pos = transform.map(event->pos()); KItemListWidget *newHoveredWidget = widgetForDropPos(pos); + int index = -1; if (oldHoveredWidget != newHoveredWidget) { m_autoActivationTimer->stop(); @@ -755,25 +764,23 @@ bool KItemListController::dragMoveEvent(QGraphicsSceneDragDropEvent *event, cons droppingBetweenItems = (m_view->showDropIndicator(pos) >= 0); } - const int index = newHoveredWidget->index(); + index = newHoveredWidget->index(); if (m_model->isDir(index)) { hoveredDir = m_model->url(index); } if (!droppingBetweenItems) { - if (m_model->supportsDropping(index)) { - // Something has been dragged on an item. - m_view->hideDropIndicator(); - if (!newHoveredWidget->isHovered()) { - newHoveredWidget->setHovered(true); - Q_EMIT itemHovered(index); - } + // Something has been dragged on an item. + m_view->hideDropIndicator(); + if (!newHoveredWidget->isHovered()) { + newHoveredWidget->setHovered(true); + Q_EMIT itemHovered(index); + } - if (!m_autoActivationTimer->isActive() && m_autoActivationTimer->interval() >= 0) { - m_autoActivationTimer->setProperty("index", index); - m_autoActivationTimer->start(); - } + if (!m_autoActivationTimer->isActive() && m_autoActivationTimer->interval() >= 0) { + m_autoActivationTimer->setProperty("index", index); + m_autoActivationTimer->start(); } } else { m_autoActivationTimer->stop(); @@ -790,8 +797,13 @@ bool KItemListController::dragMoveEvent(QGraphicsSceneDragDropEvent *event, cons event->setDropAction(Qt::IgnoreAction); event->ignore(); } else { - event->setDropAction(event->proposedAction()); - event->accept(); + if (m_model->supportsDropping(index)) { + event->setDropAction(event->proposedAction()); + event->accept(); + } else { + event->setDropAction(Qt::IgnoreAction); + event->ignore(); + } } return false; } diff --git a/src/kitemviews/kitemlistcontroller.h b/src/kitemviews/kitemlistcontroller.h index d75a8a22d..0969ed21a 100644 --- a/src/kitemviews/kitemlistcontroller.h +++ b/src/kitemviews/kitemlistcontroller.h @@ -123,6 +123,11 @@ public: void setSelectionModeEnabled(bool enabled); bool selectionMode() const; + /** + * @return \c true if search as you type is active, or \c false otherwise. + */ + bool isSearchAsYouTypeActive() const; + bool processEvent(QEvent *event, const QTransform &transform); Q_SIGNALS: diff --git a/src/kitemviews/kitemlistwidget.cpp b/src/kitemviews/kitemlistwidget.cpp index 25a347517..1d6b9641a 100644 --- a/src/kitemviews/kitemlistwidget.cpp +++ b/src/kitemviews/kitemlistwidget.cpp @@ -49,6 +49,7 @@ KItemListWidget::KItemListWidget(KItemListWidgetInformant *informant, QGraphicsI , m_selectionToggle(nullptr) , m_editedRole() , m_iconSize(-1) + , m_sidePadding(0) { connect(&m_hoverSequenceTimer, &QTimer::timeout, this, &KItemListWidget::slotHoverSequenceTimerTimeout); } diff --git a/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp b/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp index b7318b344..c74ef1638 100644 --- a/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp +++ b/src/kitemviews/private/kitemlistkeyboardsearchmanager.cpp @@ -27,6 +27,11 @@ bool KItemListKeyboardSearchManager::shouldClearSearchIfInputTimeReached() return (keyboardInputTimeElapsed > m_timeout) || !keyboardTimeWasValid; } +bool KItemListKeyboardSearchManager::isSearchAsYouTypeActive() const +{ + return !m_searchedString.isEmpty() && !m_keyboardInputTime.hasExpired(m_timeout); +} + void KItemListKeyboardSearchManager::addKeys(const QString &keys) { if (shouldClearSearchIfInputTimeReached()) { @@ -63,11 +68,6 @@ void KItemListKeyboardSearchManager::addKeys(const QString &keys) m_keyboardInputTime.start(); } -bool KItemListKeyboardSearchManager::addKeyBeginsNewSearch() const -{ - return m_keyboardInputTime.hasExpired(m_timeout) || m_searchedString.isEmpty(); -} - void KItemListKeyboardSearchManager::setTimeout(qint64 milliseconds) { m_timeout = milliseconds; diff --git a/src/kitemviews/private/kitemlistkeyboardsearchmanager.h b/src/kitemviews/private/kitemlistkeyboardsearchmanager.h index d370bc9ba..981d98cd3 100644 --- a/src/kitemviews/private/kitemlistkeyboardsearchmanager.h +++ b/src/kitemviews/private/kitemlistkeyboardsearchmanager.h @@ -34,11 +34,6 @@ public: * Add \a keys to the text buffer used for searching. */ void addKeys(const QString &keys); - /** - * @returns true if the next call to addKeys() will trigger a new search. - * Returns false if the next added key char will be added to the search string that was used previously. - */ - bool addKeyBeginsNewSearch() const; /** * Sets the delay after which the search is cancelled to \a milliseconds. @@ -51,6 +46,11 @@ public: void cancelSearch(); + /** + * @return \c true if search as you type is active, or \c false otherwise. + */ + bool isSearchAsYouTypeActive() const; + public Q_SLOTS: void slotCurrentChanged(int current, int previous); @@ -71,7 +71,6 @@ Q_SIGNALS: private: bool shouldClearSearchIfInputTimeReached(); -private: QString m_searchedString; bool m_isSearchRestarted; /** Measures the time since the last key press. */ diff --git a/src/main.cpp b/src/main.cpp index e1e559f97..2d1ccd0c8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -54,10 +54,16 @@ int main(int argc, char **argv) // Prohibit using sudo or kdesu (but allow using the root user directly) if (getuid() == 0) { if (!qEnvironmentVariableIsEmpty("SUDO_USER")) { - std::cout << "Running Dolphin with sudo can cause bugs and expose you to security vulnerabilities." << std::endl; + std::cout << "Running Dolphin with sudo is not supported as it can cause bugs and expose you to security vulnerabilities. Instead, install the " + "`kio-admin` package from your distro and use it to manage root-owned locations by right-clicking on them and selecting \"Open as " + "Administrator\"." + << std::endl; return EXIT_FAILURE; } else if (!qEnvironmentVariableIsEmpty("KDESU_USER")) { - std::cout << "Running Dolphin with kdesu can cause bugs and expose you to security vulnerabilities." << std::endl; + std::cout << "Running Dolphin with kdesu is not supported as it can cause bugs and expose you to security vulnerabilities. Instead, install the " + "`kio-admin` package from your distro and use it to manage root-owned locations by right-clicking on them and selecting \"Open as " + "Administrator\"." + << std::endl; return EXIT_FAILURE; } } diff --git a/src/org.kde.dolphin.appdata.xml b/src/org.kde.dolphin.appdata.xml index b0cc80740..010f81c4c 100644 --- a/src/org.kde.dolphin.appdata.xml +++ b/src/org.kde.dolphin.appdata.xml @@ -121,7 +121,7 @@ <summary xml:lang="zh-TW">檔案管理員</summary> <description> <p>Dolphin is KDE's file manager that lets you navigate and browse the contents of your hard drives, USB sticks, SD cards, and more. Creating, moving, or deleting files and folders is simple and fast.</p> - <p xml:lang="ar">دولفين هو مدير ملفات كدي الذي يتيح لك التنقل وتصفح محتويات محركات الأقراص الثابتة و USB وبطاقات SD والمزيد. يعد إنشاء الملفات والمجلدات أو نقلها أو حذفها أمرًا بسيطًا وسريعًا.</p> + <p xml:lang="ar">دولفين هو مدير ملفات كِيدِي الذي يتيح لك التنقل وتصفح محتويات محركات الأقراص الثابتة و USB وبطاقات SD والمزيد. يعد إنشاء الملفات والمجلدات أو نقلها أو حذفها أمرًا بسيطًا وسريعًا.</p> <p xml:lang="az">Dolphin sizin sərt disklərinizin, USB yaddaş qurğularınızın, SD katlarınızın və s. tərkibindəkiləri nəzərdən keçirmənizə imkan verən fayl meneceridir. Faylların və qovluqların yaradılması, Köçürülməsi və ya silinməsi sadə, rahat və cəld icra edilir.</p> <p xml:lang="be">Dolphin - гэта кіраўнік файлаў KDE, які дазваляе вам перамяшчацца і праглядаць змесціва вашых цвёрдых дыскаў, USB-назапашвальнікаў, SD-картак і інш. Ствараць, перамяшчаць або выдаляць файлы і каталогі проста і хутка.</p> <p xml:lang="bg">Dolphin e файловият мениджър на KDE, който ви позволява да навигирате и да преглеждате съдържанието на вашите твърди дискове, USB, SD карти и др. Създаването, преместването или изтриването на файлове и папки е просто и бързо.</p> @@ -135,6 +135,7 @@ <p xml:lang="eu">Dolphin zure disko zurrun, USB memoria, SD txartel, eta gehiagoren edukian nabigatu eta arakatzen uzten dizun KDEren fitxategi-kudeatzailea da. Fitxategi eta karpetak sortzea, mugitzea, edo ezabatzea erraza eta azkarra da.</p> <p xml:lang="fi">Dolphin on KDE:n tiedostonhallinta, jolla voit selata kiintolevyjen, USB-tikkujen, SD-korttien ja muiden tallennusvälineiden sisältöä. Tiedostojen ja kansioiden luominen, siirtäminen ja poistaminen on yksinkertaista ja helppoa.</p> <p xml:lang="fr">Dolphin est le gestionnaire de fichiers de KDE, vous permettant de naviguer et d'explorer le contenu de vos disques durs, vos clés USB et bien plus. La création, le déplacement ou la suppression de fichiers et de dossiers sont simples et rapides.</p> + <p xml:lang="gl">Dolphin é o xestor de ficheiros de KDE que lle permite navegar e explorar o contido dos seus discos duros, memorias USB, tarxetas SD, e máis. Fai que tarefas como crear, mover, e eliminar ficheiros e cartafoles sexan doadas e rápidas.</p> <p xml:lang="hi">डॉल्फ़िन केडीई का फ़ाइल प्रबंधक है जो आपको अपनी हार्ड ड्राइव, यूएसबी स्टिक, एसडी कार्ड आदि की सामग्री को संचालन और विचरण करने देता है। इसमें फ़ाइलों और फ़ोल्डरों को बनाना, स्थानांतरित करना या हटाना सरल और तेज़ है।</p> <p xml:lang="hu">A Dolphin a KDE fájlkezelője, amely lehetővé teszi a merevlemezei, USB-kulcsai, SD-kártyái és más adathordozói tartalmának böngészését. Gyorsan és egyszerűen hozhat létre, helyezhet át vagy törölhet vele fájlokat és mappákat.</p> <p xml:lang="ia">Dolphin es le gerente de file de KDE que te permitte navigar e explorar le contentos de tu discos dur, claves de memoria USB, cartas SD, e altere. Crear, mover o deler file e dossieres es simpe e rapide.</p> @@ -176,6 +177,7 @@ <p xml:lang="eu">Dolphin-ek denbora aurreztuko dizuten produktibitate-ezaugarri ugari ditu. Fitxa anitzen eta ikuspegi zatituen ezaugarriek aldi berean karpeta anitz nabigatzen uzten dute, eta fitxategiak ikuspegien artean arrastatu eta jaregin ditzakezu haiek erraz mugitu edo kopiatzeko. Dolphin-en eskuin-klik laster-menuak, besteak beste, fitxategiak konprimatzeko, partekatzeko, eta bikoizteko ekintza azkarrak eskaintzen ditu. Zuk nahi dituzun ekintzak ere erants ditzakezu.</p> <p xml:lang="fi">Dolphinin monet tuottavuusominaisuudet säästävät aikaasi. Välilehdet ja jaetut näkyvät mahdollistavat useamman kansion tarkastelun yhtä aikaa, jolloin tiedostoja voi helposti kopioida tai siirtää näkymästä toiseen vetämällä ja pudottamalla. Dolphinin kontekstivalikon tarjoamin pikatoiminnoin tiedostoja voi muun muassa pakata, jakaa ja monistaa. Myös omia mukautettuja toimintoja voi luoda.</p> <p xml:lang="fr">Dolphin contient de nombreuses fonctionnalités pour la productivité vous permettant de gagner du temps. Les fonctionnalités d'onglets multiples et d'affichage scindé vous permettent de naviguer dans plusieurs dossiers en même temps. Vous pouvez aussi réaliser facilement des glisser et déposer entre les affichages pour les déplacer ou les copier. Le menu de Dolphin accessible par un clic droit fournit plusieurs actions rapides vous permettant de compresser, partager et dupliquer des fichiers, entre autres actions. Vous pouvez aussi ajouter votre propres actions personnalisées.</p> + <p xml:lang="gl">Dolphin contén una morea de funcionalidades de produtividade para aforrarlle tempo. As funcionalidades de lapelas e vista dividida permiten navegar varios cartafoles ao mesmo tempo, e pode arrastrar e soltar ficheiros entre as súas vistas facilmente para movelos ou copialos. O menú de clic dereito de Dolphin fornece moitas accións rápidas que lle permiten comprimir, compartir, e duplicar ficheiros, entre outras cousas. Tamén pode engadir as súas propias accións personalizadas.</p> <p xml:lang="hi">डॉल्फिन में बहुत सारी उत्पादकता विशेषताएं होती हैं जो आपका समय बचाएगी। एकाधिक टैब और विभाजित दृश्य सुविधाएं एक ही समय में एकाधिक फ़ोल्डरों को संचालित करने में काम आतीं हैं, और आप फ़ाइलों को स्थानांतरित करने या नकल करने के लिए आसानी से फ़ाइलों को खींच कर छोड़ सकते हैं। डॉल्फ़िन का दाहिना-क्लिक मेनू कई द्रुत क्रियाएं प्रदान करता है जो आपको कई अन्य चीजों के साथ-साथ फ़ाइलों को संपीड़ित, साझा और प्रतिलिपि बनाने देता है। आप अपनी खुद की तदनुकूल कार्रवाइयां भी जोड़ सकते हैं।</p> <p xml:lang="hu">A Dolphin számos olyan funkcióval bír, amelyek időt spórolnak Önnek. A lapokkal és osztott nézetekkel egyszerre navigálhat több mappában egyszerre, és könnyen húzhat át fájlokat az egyik nézetből a másikba, átmásolva vagy áthelyezve azokat. A Dolphin jobb gombos menüje rengeteg gyors műveletet biztosít, például tömörítést, megosztást vagy fájlduplikálást. Akár saját műveletekkel is bővítheti azt.</p> <p xml:lang="ia">Dolphin contine abundantia de characteristicas de productivitate que te salveguardera tempore. Le characteristicas de multiple schedas e vista dividite permitte navigar dossieres multiple al mesme tempore, e tu pote facilemente traher e deponer files inter vistas per mover o copiar los. Le menu de pulsar a dextere de Dolphines fornite con multe actiones rapide que te permitte comprimer, compartir, e duplicar files, inter multe altere cosas. Tu anqu pote adder tu actiones personalisate. </p> @@ -217,6 +219,7 @@ <p xml:lang="eu">Dolphin oso arina da, baina, aldi berean, zure beharretara egoki dezakezu. Horrek esan nahi du fitxategien kudeaketa zuk nahi duzun modura egin dezakezula. Dolphin-ek hiru ikuspegi modu ezberdin onartzen ditu: fitxategi guztien sareta-ikuspegi klasiko bat, xehetasun gehiago dituen ikuspegi bat, eta zuhaitz-ikuspegi bat. Dolphin-en jokabide gehienak ere konfigura ditzakezu.</p> <p xml:lang="fi">Dolphin on hyvin kevyt mutta samalla sovitettavissa tarpeisiisi, joten voit hallita tiedostojasi juuri kuten haluat. Dolphin tukee kolmea eri näkymää: perinteistä ruudukkoa, yksityiskohtaisempaa tilanäkymää ja puunäkymää. Useimpia Dolphinin toiminta-asetuksia voi säätää.</p> <p xml:lang="fr">Dolphin est peu consommateur de ressources mais, en même temps, vous pouvez l'adapter à vos besoins spécifiques. Cela signifie que vous pouvez configurer votre gestion de fichiers, exactement comme vous le souhaitez. Dolphin prend en charge trois modes différents d'affichage : un affichage classique en grille pour tous vos fichiers, un affichage plus détaillé et un affichage en arborescence. Vous pouvez configurer la plupart des comportement de Dolphin.</p> + <p xml:lang="gl">Dolphin é moi lixeiro, pero ao mesmo tempo pode adaptarse ás súas necesidades concretas. Isto significa que pode xestionar os seus ficheiros como mellor lle pareza. Dolphin permite tres modos de vista distintos: a clásica vista de grade de todos os ficheiros, unha vista máis detallada, e unha vista de árbore. Tamén pode configurar a meirande parte do comportamento de Dolphin.</p> <p xml:lang="hi">डॉल्फ़िन बहुत हल्का है, लेकिन साथ ही, आप इसे अपनी विशिष्ट ज़रूरतों के अनुसार अनुकूलित कर सकते हैं। इसका मतलब है कि आप अपने फ़ाइल प्रबंधन को ठीक वैसे ही कर सकते हैं जैसे आप चाहते हैं। डॉल्फ़िन तीन अलग-अलग दृश्य मोड का समर्थन करता है: सभी फ़ाइलों का एक क्लासिक ग्रिड दृश्य, एक अधिक विस्तृत दृश्य और एक ट्री व्यू। आप अधिकांश डॉल्फ़िन के व्यवहार को भी विन्यस्त कर सकते हैं।</p> <p xml:lang="hu">A Dolphin pehelysúlyú, ugyanakkor a saját igényeire is szabhatja, vagyis a fájlkezelést teljesen a saját szája íze szerint végezheti. A Dolphin három nézetmódot támogat: a klasszikus rácsnézetet, a részletes nézetet és a fastruktúra nézetet. A legtöbb funkciót módosíthatja.</p> <p xml:lang="ia">Dolphin es multe legier, ma al mesme tempore, tu pote adaptar lo a tu necessitates speific. Isto significa que tu pote complir tu gestion de file exactemente in le modo que tu vole. Dolphin supporta tres modos de vista differente: un vista classic de grillia de omne le files, un vista plus detaliate, e un vista a arbore. Tu anque pote configurar le majoritate del comportamento de Dolphin.</p> @@ -258,6 +261,7 @@ <p xml:lang="eu">Dolphin-ek Interneteko hodei-zerbitzu askotako eta urruneko beste makina batzuetako fitxategi eta karpetak zure mahaigainean egongo balira bezala erakuts ditzake.</p> <p xml:lang="fi">Dolphin osaa näyttää tiedostot ja kansiot monista internetin pilvipalveluista sekä etäkoneilta kuin ne olisivat välittömästi työpöydälläsi.</p> <p xml:lang="fr">Dolphin peut afficher des fichiers et des dossiers à partir de nombreux services de stockage sur Internet (Cloud) et d'ordinateurs distants, comme s'ils étaient directement sur votre ordinateur.</p> + <p xml:lang="gl">Dolphin pode mostrar ficheiros e cartafoles de moitos servizos de nube de internet e outras máquinas remotas como se estivesen no seu escritorio.</p> <p xml:lang="hi">डॉल्फ़िन कई इंटरनेट क्लाउड सेवाओं और अन्य दूरस्थ मशीनों से फ़ाइलें और फ़ोल्डर प्रदर्शित इस प्रकार कर सकता है जैसे कि वे आपके डेस्कटॉप पर ही हैं।</p> <p xml:lang="hu">A Dolphin úgy képes fájlokat megjeleníteni számos felhőszolgáltatásból vagy távoli gépekről, mintha azok a saját számítógépén lennének.</p> <p xml:lang="ia">Dolphin pote monstrar files e dossieres ab multe servicios cloud de internet e altere machinas remote como si illos esseva exactemente sur tu scriptorio.</p> @@ -283,7 +287,7 @@ <p xml:lang="uk">Dolphin здатен показувати файли і теки із багатьох «хмарних» служб інтернету та віддалених комп'ютерів так, наче усі ці дані зберігаються на вашому робочому комп'ютері.</p> <p xml:lang="vi">Dolphin có thể hiển thị tệp và thư mục từ nhiều dịch vụ đám mây Liên Mạng và các máy ở xa khác như thể chúng ở ngay trên máy tính của bạn vậy.</p> <p xml:lang="x-test">xxDolphin can display files and folders from many Internet cloud services and other remote machines as if they were right there on your desktop.xx</p> - <p xml:lang="zh-CN">Dolphin 可以显示多种云存储服务和远程计算机中的文件和文件夹,使用体验与本机文件完全一致。</p> + <p xml:lang="zh-CN">Dolphin 可以显示多种云存储服务和远程计算机中的文件和文件夹,使用体验与本地文件完全一致。</p> <p xml:lang="zh-TW">Dolphin 可以把許多網路雲端服務或是其他遠端裝置上的檔案當作好像在您的桌面上一樣顯示出來。</p> <p>Dolphin also comes with an integrated terminal that allows you to run commands on the current folder. You can extend the capabilities of Dolphin even further with powerful plugins to adapt it to your workflow. You can use the git integration plugin to interact with git repositories, or the Nextcloud plugin to synchronize your files online, and much more.</p> <p xml:lang="ar">يأتي دولفين أيضًا مع محطة طرفية متكاملة تتيح لك تشغيل الأوامر في المجلد الحالي. يمكنك توسيع إمكانيات دولفين إلى أبعد من ذلك باستخدام المكونات الإضافية القوية لتكييفها مع سير عملك. يمكنك استخدام المكون الإضافي git للتكامل للتفاعل مع مستودعات git ، أو المكون الإضافي Nextcloud لمزامنة ملفاتك عبر الإنترنت ، وغير ذلك الكثير.</p> @@ -300,6 +304,7 @@ <p xml:lang="eu">Dolphin-ek terminal bat ere badu, uneko karpetan komandoak erabiltzeko aukera ematen duena. Dolphin-en gaitasunak are gehiago hedatu ditzakezu zure lan egiteko modura egokitzeko plugin ahaltsuak erabiliz. Git bateratzeko plugina erabil dezakezu Git gordetegiekin elkarreragiteko, edo Nextcloud plugina zure fitxategiak lerroan sinkronizatzeko, eta askoz gehiago.</p> <p xml:lang="fi">Dolphinissa on myös sisään rakennettu pääte komentojen suorittamiseksi nykyisessä kansiossa. Dolphinin ominaisuuksia voi myös laajentaa sopimaan työtapaasi tehokkain laajennuksin. Git-integrointiliitännäinen auttaa toimimaan Git-lähteiden kanssa, Nextcloud-liitännäisellä voi synkronoida tiedostot verkkoon jne.</p> <p xml:lang="fr">Dolphin est aussi livré avec un terminal intégré, vous permettant d'exécuter des commandes sur votre dossier courant. Vous pouvez étendre les fonctionnalités de Dolphin encore plus avec de puissants modules externes pour s'adapter à vos processus de travail. Vous pouvez utiliser le module externe « git » pour interagir avec les dépôts « git » ou le module externe « Nextcloud » pour synchroniser vos fichiers en ligne et bien d'autres choses encore.</p> + <p xml:lang="gl">Dolphin inclúe tamén un terminal integrado que lle permite executar ordes no cartafol actual. Pode estender as capacidades de Dolphin aínda máis con potentes complementos para adaptalo ao seu fluxo de traballo. Pode usar o complemento de integración con Git para interactuar con repositorios de Git, ou o complemento de Nextcloud para sincronizar os seus ficheiros por internet, e moito máis.</p> <p xml:lang="hi">डॉल्फ़िन भी एक एकीकृत टर्मिनल के साथ आता है जो आपको वर्तमान फ़ोल्डर पर समादेश चलाने की अनुमति देता है। आप डॉल्फ़िन क्षमताओं को अपने कार्यप्रवाह में अनुकूलित करने के लिए शक्तिशाली प्लग इन के साथ और भी अधिक बढ़ा सकते हैं। आप git रिपॉजिटरी के साथ अंत:क्रिया करने के लिए git एकीकरण प्लगइन का उपयोग कर सकते हैं, या अपनी फ़ाइलों को ऑनलाइन समकालन करने के लिए नेक्स्टक्लाउड प्लगइन का उपयोग कर सकते हैं, और भी बहुत कुछ।</p> <p xml:lang="hu">A Dolphin tovább tartalmaz egy integrált terminálemulátort, amely lehetővé teszi parancsok futtatását az aktuális mappában. Bővítményekkel még jobban kiterjesztheti a Dolphin képességeinek határait. A git integrációs bővítménnyek git tárolókat kezelhet, a Nextcloud bővítménnyel szinkronizálhatja fájljait, és így tovább.</p> <p xml:lang="ia">Dolphin anque deveni con un terminal integrate que te permitte executar commandos sur le dossier currente. Tu pote extender le capacitates de Dolphin anque plus avante con plugins potente per adaptar lo a tu fluxo de labor. Tu pote usar le plugin de integrationde git per affectar con repositorios de git, o le plugin de Nextcloud per synchronisar tu files in linea, a multe altere.</p> @@ -403,6 +408,7 @@ <caption xml:lang="eu">Dolphinen txertatutako terminala</caption> <caption xml:lang="fi">Upotettu pääte Dolphinissa</caption> <caption xml:lang="fr">Terminal intégré dans Dolphin</caption> + <caption xml:lang="gl">Terminal incrustado en Dolphin</caption> <caption xml:lang="hi">डॉल्फिन में सन्निहित टर्मिनल</caption> <caption xml:lang="hu">Beágyazott terminál a Dolphinban</caption> <caption xml:lang="ia">Terminal incorporate in Dolphin</caption> @@ -450,6 +456,7 @@ <caption xml:lang="eu">Dolphinek zure fitxategi-kudeatzailea nahi duzun eran konfiguratzen uzten dizu</caption> <caption xml:lang="fi">Dolphinilla voit tehdä tiedostonhallintaa juuri kuten haluat</caption> <caption xml:lang="fr">Dolphin vous permet de configurer votre gestionnaire de fichiers, exactement comme vous le souhaitez.</caption> + <caption xml:lang="gl">Dolphin permítelle configurar o seu xestor de ficheiros como queira</caption> <caption xml:lang="hi">डॉल्फ़िन आपको अपने फ़ाइल प्रबंधक को ठीक वैसे ही विन्यस्त करने देता है जैसे आप चाहते हैं </caption> <caption xml:lang="hu">A Dolphin lehetővé teszi, hogy úgy állítsa be a fájlkezelőkét, ahogyan szeretné</caption> <caption xml:lang="ia">Dolphin te permitte configurar tu gerente de file exactemente como tu lo vole</caption> @@ -486,10 +493,10 @@ <binary>dolphin</binary> </provides> <releases> + <release version="23.04.1" date="2023-05-11"/> + <release version="23.04.0" date="2023-04-20"/> <release version="22.12.3" date="2023-03-02"/> <release version="22.12.2" date="2023-02-02"/> - <release version="22.12.1" date="2023-01-05"/> - <release version="22.12.0" date="2022-12-08"/> </releases> <content_rating type="oars-1.1"/> </component> diff --git a/src/org.kde.dolphin.desktop b/src/org.kde.dolphin.desktop index 8b957e66e..b041e02ac 100755 --- a/src/org.kde.dolphin.desktop +++ b/src/org.kde.dolphin.desktop @@ -134,10 +134,12 @@ Keywords[da]=filer;filhåndtering;stifinder;filbrowsing;samba;netværksressource Keywords[de]=Dateien;Dateiverwaltung;Netzwerkfreigaben; Keywords[el]=αρχεία;διαχείριση αρχείων;περιήγηση;samba;δικτυακοί πόροι;Εξερευνητής;Αναζήτηση; Keywords[en_GB]=files;file management;file browsing;samba;network shares;Explorer;Finder; +Keywords[eo]=dosieroj;dosieradministrado;dosierfoliumado;samba;retkunhavigoj;Explorer;Finder; Keywords[es]=archivos;gestión de archivos;administración de archivos;exploración de archivos;samba;recursos compartidos de red;gestor de archivos;administrador de archivos;explorador;buscador; Keywords[eu]=fitxategiak;fitxategi kudeaketa;fitxategiak arakatzea;samba;sare partekatzeak; Esploratzailea;Bilatzailea; Keywords[fi]=tiedostot;tiedostonhallinta;tiedostojen selaus;samba;verkko;verkkojaot;Explorer;Finder; Keywords[fr]=fichiers ; gestion de fichiers ; navigation parmi les fichiers ; samba ; partages sur réseau ; explorateur ; chercheur ; +Keywords[gl]=ficheiros;xestión de ficheiros;navegación de ficheiros;samba;comparticións de rede;Explorer;Finder;arquivos Keywords[hi]=फ़ाइलें;फ़ाइल प्रबंधन;फ़ाइल ब्राउज़िंग;सांबा;नेटवर्क शेयर;एक्सप्लोरर;खोजक; Keywords[hu]=fájlok;fájlkezelés;fájlböngészés;samba;hálózati megosztások;Explorer;Finder; Keywords[ia]=files; gestion de file; navigation de file;samba; partes de rete;Explorator;Trovator; diff --git a/src/panels/information/dolphin_informationpanelsettings.kcfg b/src/panels/information/dolphin_informationpanelsettings.kcfg index 7a5d29728..0f34e7310 100644 --- a/src/panels/information/dolphin_informationpanelsettings.kcfg +++ b/src/panels/information/dolphin_informationpanelsettings.kcfg @@ -14,6 +14,10 @@ <label>Auto-Play media files</label> <default>false</default> </entry> + <entry name="showHovered" type="Bool"> + <label>Show item on hover</label> + <default>true</default> + </entry> <entry name="dateFormat" type="Enum"> <label>Date display format</label> <choices> diff --git a/src/panels/information/informationpanel.cpp b/src/panels/information/informationpanel.cpp index a0dff0d5e..6060e2d8c 100644 --- a/src/panels/information/informationpanel.cpp +++ b/src/panels/information/informationpanel.cpp @@ -62,13 +62,13 @@ void InformationPanel::setSelection(const KFileItemList &selection) if ((count == 1) && !selection.first().url().isEmpty()) { m_urlCandidate = selection.first().url(); } - m_infoTimer->start(); + showItemInfo(); } } void InformationPanel::requestDelayedItemInfo(const KFileItem &item) { - if (!isVisible()) { + if (!isVisible() || !InformationPanelSettings::showHovered()) { return; } @@ -160,6 +160,11 @@ void InformationPanel::showContextMenu(const QPoint &pos) previewAutoPlayAction->setCheckable(true); previewAutoPlayAction->setChecked(InformationPanelSettings::previewsAutoPlay()); + QAction *showHoveredAction = popup.addAction(i18nc("@action:inmenu", "Show item on hover")); + showHoveredAction->setIcon(QIcon::fromTheme(QStringLiteral("followmouse"))); + showHoveredAction->setCheckable(true); + showHoveredAction->setChecked(InformationPanelSettings::showHovered()); + QAction *configureAction = popup.addAction(i18nc("@action:inmenu", "Configure...")); configureAction->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); if (m_inConfigurationMode) { @@ -188,18 +193,23 @@ void InformationPanel::showContextMenu(const QPoint &pos) if (action == previewAction) { InformationPanelSettings::setPreviewsShown(isChecked); m_content->refreshPreview(); + } else if (action == previewAutoPlayAction) { + InformationPanelSettings::setPreviewsAutoPlay(isChecked); + m_content->setPreviewAutoPlay(isChecked); + } else if (action == showHoveredAction) { + InformationPanelSettings::setShowHovered(isChecked); + if (!isChecked) { + m_hoveredItem = KFileItem(); + showItemInfo(); + } } else if (action == configureAction) { m_inConfigurationMode = true; m_content->configureShownProperties(); - } - if (action == dateformatAction) { + } else if (action == dateformatAction) { int dateFormat = static_cast<int>(isChecked ? Baloo::DateFormats::ShortFormat : Baloo::DateFormats::LongFormat); InformationPanelSettings::setDateFormat(dateFormat); m_content->refreshMetaData(); - } else if (action == previewAutoPlayAction) { - InformationPanelSettings::setPreviewsAutoPlay(isChecked); - m_content->setPreviewAutoPlay(isChecked); } } @@ -218,7 +228,7 @@ void InformationPanel::showItemInfo() } else { // The information for exactly one item should be shown KFileItem item; - if (!m_hoveredItem.isNull()) { + if (!m_hoveredItem.isNull() && InformationPanelSettings::showHovered()) { item = m_hoveredItem; } else if (!m_selection.isEmpty()) { Q_ASSERT(m_selection.count() == 1); @@ -281,8 +291,6 @@ void InformationPanel::slotFileRenamed(const QString &source, const QString &des // item is selected, as the name of the item is shown. If this should change // in future: Before parsing the whole selection take care to test possible // performance bottlenecks when renaming several hundreds of files. - } else { - m_hoveredItem = KFileItem(destUrl); } showItemInfo(); diff --git a/src/panels/places/placespanel.cpp b/src/panels/places/placespanel.cpp index 2155f29bd..2d508ba65 100644 --- a/src/panels/places/placespanel.cpp +++ b/src/panels/places/placespanel.cpp @@ -14,7 +14,6 @@ #include "dolphin_generalsettings.h" #include "dolphin_placespanelsettings.h" #include "dolphinplacesmodelsingleton.h" -#include "global.h" #include "settings/dolphinsettingsdialog.h" #include "views/draganddrophelper.h" @@ -50,7 +49,7 @@ PlacesPanel::PlacesPanel(QWidget *parent) connect(this, &PlacesPanel::contextMenuAboutToShow, this, &PlacesPanel::slotContextMenuAboutToShow); - connect(this, &PlacesPanel::iconSizeChanged, this, [this](const QSize &newSize) { + connect(this, &PlacesPanel::iconSizeChanged, this, [](const QSize &newSize) { int iconSize = qMin(newSize.width(), newSize.height()); if (iconSize == 0) { // Don't store 0 size, let's keep -1 for default/small/automatic diff --git a/src/panels/terminal/terminalpanel.cpp b/src/panels/terminal/terminalpanel.cpp index 53464e620..9e0391c41 100644 --- a/src/panels/terminal/terminalpanel.cpp +++ b/src/panels/terminal/terminalpanel.cpp @@ -54,12 +54,12 @@ void TerminalPanel::goHome() sendCdToTerminal(QDir::homePath(), HistoryPolicy::SkipHistory); } -QString TerminalPanel::currentWorkingDirectory() +bool TerminalPanel::currentWorkingDirectoryIsChildOf(const QString &path) const { if (m_terminal) { - return m_terminal->currentWorkingDirectory(); + return m_terminal->currentWorkingDirectory().startsWith(path); } - return QString(); + return false; } void TerminalPanel::terminalExited() diff --git a/src/panels/terminal/terminalpanel.h b/src/panels/terminal/terminalpanel.h index a1b7af03a..8eee3c10f 100644 --- a/src/panels/terminal/terminalpanel.h +++ b/src/panels/terminal/terminalpanel.h @@ -45,7 +45,7 @@ public: * home when an unmounting request is received. */ void goHome(); - QString currentWorkingDirectory(); + bool currentWorkingDirectoryIsChildOf(const QString &path) const; bool isHiddenInVisibleWindow() const; bool terminalHasFocus() const; bool hasProgramRunning() const; diff --git a/src/settings/contextmenu/contextmenusettingspage.cpp b/src/settings/contextmenu/contextmenusettingspage.cpp index ebdda1c1c..78d8ccf8a 100644 --- a/src/settings/contextmenu/contextmenusettingspage.cpp +++ b/src/settings/contextmenu/contextmenusettingspage.cpp @@ -128,6 +128,10 @@ bool ContextMenuSettingsPage::entryVisible(const QString &id) return ContextMenuSettings::showDuplicateHere(); } else if (id == "open_terminal_here") { return ContextMenuSettings::showOpenTerminal(); + } else if (id == "copy_to_inactive_split_view") { + return ContextMenuSettings::showCopyToOtherSplitView(); + } else if (id == "move_to_inactive_split_view") { + return ContextMenuSettings::showMoveToOtherSplitView(); } return false; } @@ -150,6 +154,10 @@ void ContextMenuSettingsPage::setEntryVisible(const QString &id, bool visible) ContextMenuSettings::setShowDuplicateHere(visible); } else if (id == "open_terminal_here") { ContextMenuSettings::setShowOpenTerminal(visible); + } else if (id == "copy_to_inactive_split_view") { + ContextMenuSettings::setShowCopyToOtherSplitView(visible); + } else if (id == "move_to_inactive_split_view") { + ContextMenuSettings::setShowMoveToOtherSplitView(visible); } } diff --git a/src/settings/dolphin_contextmenusettings.kcfg b/src/settings/dolphin_contextmenusettings.kcfg index 44fd83513..63ca079af 100644 --- a/src/settings/dolphin_contextmenusettings.kcfg +++ b/src/settings/dolphin_contextmenusettings.kcfg @@ -42,5 +42,13 @@ <label>Show 'Open Terminal' in context menu.</label> <default>true</default> </entry> + <entry name="ShowCopyToOtherSplitView" type="Bool"> + <label>Show 'Copy to other split view' in context menu.</label> + <default>true</default> + </entry> + <entry name="ShowMoveToOtherSplitView" type="Bool"> + <label>Show 'Move to other split view' in context menu.</label> + <default>true</default> + </entry> </group> </kcfg> diff --git a/src/settings/dolphinsettingsdialog.cpp b/src/settings/dolphinsettingsdialog.cpp index ec92cb635..af3dbc865 100644 --- a/src/settings/dolphinsettingsdialog.cpp +++ b/src/settings/dolphinsettingsdialog.cpp @@ -84,7 +84,9 @@ DolphinSettingsDialog::DolphinSettingsDialog(const QUrl &url, QWidget *parent, K QStringLiteral("open_in_new_window"), QStringLiteral("copy_location"), QStringLiteral("duplicate"), - QStringLiteral("open_terminal_here")}); + QStringLiteral("open_terminal_here"), + QStringLiteral("copy_to_inactive_split_view"), + QStringLiteral("move_to_inactive_split_view")}); KPageWidgetItem *contextMenuSettingsFrame = addPage(contextMenuSettingsPage, i18nc("@title:group", "Context Menu")); contextMenuSettingsFrame->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-menu-edit"))); connect(contextMenuSettingsPage, &ContextMenuSettingsPage::changed, this, &DolphinSettingsDialog::enableApply); diff --git a/src/settings/kcm/kcmdolphingeneral.json b/src/settings/kcm/kcmdolphingeneral.json index f6ad02235..584aed1f0 100644 --- a/src/settings/kcm/kcmdolphingeneral.json +++ b/src/settings/kcm/kcmdolphingeneral.json @@ -18,6 +18,7 @@ "Name[eu]": "Orokorra", "Name[fi]": "Perusasetukset", "Name[fr]": "Général", + "Name[gl]": "Xeral", "Name[hi]": "साधारण", "Name[ia]": "General", "Name[id]": "Umum", diff --git a/src/tests/dolphinmainwindowtest.cpp b/src/tests/dolphinmainwindowtest.cpp index e849c4937..009008002 100644 --- a/src/tests/dolphinmainwindowtest.cpp +++ b/src/tests/dolphinmainwindowtest.cpp @@ -38,6 +38,7 @@ private Q_SLOTS: void testWindowTitle(); void testPlacesPanelWidthResistance(); void testGoActions(); + void testOpenFiles(); void cleanupTestCase(); private: @@ -419,6 +420,110 @@ void DolphinMainWindowTest::testGoActions() QVERIFY(m_mainWindow->actionCollection()->action(QStringLiteral("undo_close_tab"))->isEnabled()); } +void DolphinMainWindowTest::testOpenFiles() +{ + QScopedPointer<TestDir> testDir{new TestDir()}; + QString testDirUrl(QDir::cleanPath(testDir->url().toString())); + testDir->createDir("a"); + testDir->createDir("a/b"); + testDir->createDir("a/b/c"); + testDir->createDir("a/b/c/d"); + m_mainWindow->openDirectories({testDirUrl}, false); + m_mainWindow->show(); + + // We only see the unselected "a" folder in the test dir. There are no other tabs. + QVERIFY(m_mainWindow->isUrlOpen(testDirUrl)); + QVERIFY(m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a")); + QVERIFY(!m_mainWindow->isUrlOpen(testDirUrl + "/a")); + QVERIFY(!m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a/b")); + QCOMPARE(m_mainWindow->m_tabWidget->count(), 1); + QCOMPARE(m_mainWindow->m_tabWidget->currentIndex(), 0); + QCOMPARE(m_mainWindow->m_activeViewContainer->view()->selectedItems().count(), 0); + + // "a" is already in view, so "opening" "a" should simply select it without opening a new tab. + m_mainWindow->openFiles({testDirUrl + "/a"}, false); + QTRY_COMPARE(m_mainWindow->m_activeViewContainer->view()->selectedItems().count(), 1); + QCOMPARE(m_mainWindow->m_tabWidget->count(), 1); + QVERIFY(m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a")); + + // "b" is not in view, so "opening" "b" should open a new active tab of the parent folder "a" and select "b" there. + m_mainWindow->openFiles({testDirUrl + "/a/b"}, false); + QTRY_VERIFY(m_mainWindow->isUrlOpen(testDirUrl + "/a")); + QCOMPARE(m_mainWindow->m_tabWidget->count(), 2); + QCOMPARE(m_mainWindow->m_tabWidget->currentIndex(), 1); + QTRY_VERIFY(m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a/b")); + QVERIFY2(!m_mainWindow->isUrlOpen(testDirUrl + "/a/b"), "The directory b is supposed to be visible but not open in its own tab."); + QTRY_COMPARE(m_mainWindow->m_activeViewContainer->view()->selectedItems().count(), 1); + + QVERIFY(m_mainWindow->isUrlOpen(testDirUrl)); + QVERIFY(m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a")); + // "a" is still in view in the first tab, so "opening" "a" should switch to the first tab and select "a" there. + m_mainWindow->openFiles({testDirUrl + "/a"}, false); + QCOMPARE(m_mainWindow->m_tabWidget->count(), 2); + QCOMPARE(m_mainWindow->m_tabWidget->currentIndex(), 0); + QVERIFY(m_mainWindow->isUrlOpen(testDirUrl)); + QVERIFY(m_mainWindow->isUrlOpen(testDirUrl + "/a")); + + // Directory "a" is already open in the second tab in which "b" is selected, so opening the directory "a" should switch to that tab. + m_mainWindow->openDirectories({testDirUrl + "/a"}, false); + QCOMPARE(m_mainWindow->m_tabWidget->count(), 2); + QCOMPARE(m_mainWindow->m_tabWidget->currentIndex(), 1); + + // In the details view mode directories can be expanded, which changes if openFiles() needs to open a new tab or not to open a file. + m_mainWindow->actionCollection()->action(QStringLiteral("details"))->trigger(); + QTRY_VERIFY(m_mainWindow->activeViewContainer()->view()->itemsExpandable()); + + // Expand the already selected "b" with the right arrow key. This should make "c" visible. + QVERIFY2(!m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a/b/c"), "The parent folder wasn't expanded yet, so c shouldn't be visible."); + QTest::keyClick(m_mainWindow->activeViewContainer()->view()->m_container, Qt::Key::Key_Right); + QTRY_VERIFY(m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a/b/c")); + QVERIFY2(!m_mainWindow->isUrlOpen(testDirUrl + "/a/b"), "b is supposed to be expanded, however it shouldn't be open in its own tab."); + QVERIFY(m_mainWindow->isUrlOpen(testDirUrl + "/a")); + + // Switch to first tab by opening it even though it is already open. + m_mainWindow->openDirectories({testDirUrl}, false); + QCOMPARE(m_mainWindow->m_tabWidget->count(), 2); + QCOMPARE(m_mainWindow->m_tabWidget->currentIndex(), 0); + + // "c" is in view in the second tab because "b" is expanded there, so "opening" "c" should switch to that tab and select "c" there. + m_mainWindow->openFiles({testDirUrl + "/a/b/c"}, false); + QCOMPARE(m_mainWindow->m_tabWidget->count(), 2); + QCOMPARE(m_mainWindow->m_tabWidget->currentIndex(), 1); + QTRY_COMPARE(m_mainWindow->m_activeViewContainer->view()->selectedItems().count(), 1); + QVERIFY(m_mainWindow->isUrlOpen(testDirUrl)); + QVERIFY(m_mainWindow->isUrlOpen(testDirUrl + "/a")); + + // Opening the directory "c" on the other hand will open it in a new tab even though it is already visible in the view + // because openDirecories() and openFiles() serve different purposes. One opens views at urls, the other selects files within views. + m_mainWindow->openDirectories({testDirUrl + "/a/b/c/d", testDirUrl + "/a/b/c"}, true); + QCOMPARE(m_mainWindow->m_tabWidget->count(), 3); + QCOMPARE(m_mainWindow->m_tabWidget->currentIndex(), 2); + QVERIFY(m_mainWindow->m_tabWidget->currentTabPage()->splitViewEnabled()); + QVERIFY(m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a/b/c")); // It should still be visible in the second tab. + QTRY_COMPARE(m_mainWindow->m_activeViewContainer->view()->selectedItems().count(), 0); + QVERIFY(m_mainWindow->isUrlOpen(testDirUrl + "/a/b/c/d")); + QVERIFY(m_mainWindow->isUrlOpen(testDirUrl + "/a/b/c")); + + // "c" is in view in the second tab because "b" is expanded there, + // so "opening" "c" should switch to that tab even though "c" as a directory is open in the current tab. + m_mainWindow->openFiles({testDirUrl + "/a/b/c"}, false); + QCOMPARE(m_mainWindow->m_tabWidget->count(), 3); + QCOMPARE(m_mainWindow->m_tabWidget->currentIndex(), 1); + QVERIFY2(m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a/b/c/d"), "It should be visible in the secondary view of the third tab."); + + // Select "b" and un-expand it with the left arrow key. This should make "c" invisible. + m_mainWindow->openFiles({testDirUrl + "/a/b"}, false); + QTest::keyClick(m_mainWindow->activeViewContainer()->view()->m_container, Qt::Key::Key_Left); + QTRY_VERIFY(!m_mainWindow->isItemVisibleInAnyView(testDirUrl + "/a/b/c")); + + // "d" is in view in the third tab in the secondary view, so "opening" "d" should select that view. + m_mainWindow->openFiles({testDirUrl + "/a/b/c/d"}, false); + QCOMPARE(m_mainWindow->m_tabWidget->count(), 3); + QCOMPARE(m_mainWindow->m_tabWidget->currentIndex(), 2); + QVERIFY(m_mainWindow->m_tabWidget->currentTabPage()->secondaryViewContainer()->isActive()); + QTRY_COMPARE(m_mainWindow->m_activeViewContainer->view()->selectedItems().count(), 1); +} + void DolphinMainWindowTest::cleanupTestCase() { m_mainWindow->showNormal(); diff --git a/src/tests/kitemlistcontrollertest.cpp b/src/tests/kitemlistcontrollertest.cpp index a7842475a..d1a9cfc7c 100644 --- a/src/tests/kitemlistcontrollertest.cpp +++ b/src/tests/kitemlistcontrollertest.cpp @@ -321,8 +321,7 @@ void KItemListControllerTest::testKeyboardNavigation_data() << qMakePair(KeyPress(Qt::Key_Enter), ViewState(0, KItemSet(), true)) << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(0, KItemSet() << 0)) << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(0, KItemSet())) - << qMakePair(KeyPress(Qt::Key_Space), ViewState(0, KItemSet())) // This used to select, but we are now using it to trigger either - // selection mode or "QuickLook". Ctrl+Space still works for selecting as expected. + << qMakePair(KeyPress(Qt::Key_Space), ViewState(0, KItemSet() << 0)) << qMakePair(KeyPress(Qt::Key_E), ViewState(13, KItemSet() << 13)) << qMakePair(KeyPress(Qt::Key_Space), ViewState(14, KItemSet() << 14)) << qMakePair(KeyPress(Qt::Key_3), ViewState(15, KItemSet() << 15)) diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp index 4ccb4a2e3..320524e65 100644 --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -299,7 +299,9 @@ DolphinView::Mode DolphinView::viewMode() const void DolphinView::setSelectionModeEnabled(const bool enabled) { if (enabled) { - m_proxyStyle = std::make_unique<SelectionMode::SingleClickSelectionProxyStyle>(); + if (!m_proxyStyle) { + m_proxyStyle = std::make_unique<SelectionMode::SingleClickSelectionProxyStyle>(); + } setStyle(m_proxyStyle.get()); m_view->setStyle(m_proxyStyle.get()); m_view->setEnabledSelectionToggles(DolphinItemListView::SelectionTogglesEnabled::False); @@ -422,6 +424,7 @@ int DolphinView::selectedItemsCount() const void DolphinView::markUrlsAsSelected(const QList<QUrl> &urls) { m_selectedUrls = urls; + m_selectJobCreatedItems = false; } void DolphinView::markUrlAsCurrent(const QUrl &url) @@ -709,6 +712,7 @@ void DolphinView::invertSelection() void DolphinView::clearSelection() { + m_selectJobCreatedItems = false; m_selectedUrls.clear(); m_container->controller()->selectionManager()->clearSelection(); } @@ -809,21 +813,39 @@ 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); connect(job, &KIO::DropJob::result, this, &DolphinView::slotJobResult); - connect(job, &KIO::CopyJob::copyingDone, this, &DolphinView::slotCopyingDone); + connect(job, &KIO::CopyJob::copying, this, &DolphinView::slotItemCreatedFromJob); + connect(job, &KIO::CopyJob::copyingDone, this, &DolphinView::slotItemCreatedFromJob); KIO::FileUndoManager::self()->recordCopyJob(job); } 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); connect(job, &KIO::DropJob::result, this, &DolphinView::slotJobResult); - connect(job, &KIO::CopyJob::copyingDone, this, &DolphinView::slotCopyingDone); + connect(job, &KIO::CopyJob::moving, this, &DolphinView::slotItemCreatedFromJob); + connect(job, &KIO::CopyJob::copyingDone, this, &DolphinView::slotItemCreatedFromJob); KIO::FileUndoManager::self()->recordCopyJob(job); } @@ -1353,7 +1375,17 @@ void DolphinView::dropUrls(const QUrl &destUrl, QDropEvent *dropEvent, QWidget * // Mark the dropped urls as selected. m_clearSelectionBeforeSelectingNewItems = true; m_markFirstNewlySelectedItemAsCurrent = true; + m_selectJobCreatedItems = true; connect(job, &KIO::DropJob::itemCreated, this, &DolphinView::slotItemCreated); + connect(job, &KIO::DropJob::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); + }); + }); } } } @@ -1400,7 +1432,7 @@ void DolphinView::slotSelectedItemTextPressed(int index) } } -void DolphinView::slotCopyingDone(KIO::Job *, const QUrl &, const QUrl &to) +void DolphinView::slotItemCreatedFromJob(KIO::Job *, const QUrl &, const QUrl &to) { slotItemCreated(to); } @@ -1411,7 +1443,9 @@ void DolphinView::slotItemCreated(const QUrl &url) markUrlAsCurrent(url); m_markFirstNewlySelectedItemAsCurrent = false; } - m_selectedUrls << url; + if (m_selectJobCreatedItems && !m_selectedUrls.contains(url)) { + m_selectedUrls << url; + } } void DolphinView::slotJobResult(KJob *job) @@ -1419,8 +1453,35 @@ void DolphinView::slotJobResult(KJob *job) if (job->error() && job->error() != KIO::ERR_USER_CANCELED) { Q_EMIT errorMessage(job->errorString()); } + if (!m_selectJobCreatedItems) { + m_selectedUrls.clear(); + return; + } if (!m_selectedUrls.isEmpty()) { m_selectedUrls = KDirModel::simplifiedUrlList(m_selectedUrls); + + 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); + } else { + m_selectJobCreatedItems = false; + m_selectedUrls.clear(); + } } } @@ -1682,6 +1743,40 @@ void DolphinView::slotDirectoryRedirection(const QUrl &oldUrl, const QUrl &newUr } } +void DolphinView::updateSelectionState() +{ + if (!m_selectedUrls.isEmpty()) { + KItemListSelectionManager *selectionManager = m_container->controller()->selectionManager(); + + // 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) { + if (m_clearSelectionBeforeSelectingNewItems) { + selectionManager->clearSelection(); + m_clearSelectionBeforeSelectingNewItems = false; + } + + KItemSet selectedItems = selectionManager->selectedItems(); + + QList<QUrl>::iterator it = m_selectedUrls.begin(); + while (it != m_selectedUrls.end()) { + const int index = m_model->index(*it); + if (index >= 0) { + selectedItems.insert(index); + it = m_selectedUrls.erase(it); + } else { + ++it; + } + } + + if (!selectedItems.isEmpty()) { + selectionManager->beginAnchoredSelection(selectionManager->currentItem()); + selectionManager->setSelectedItems(selectedItems); + } + } + } +} + void DolphinView::updateViewState() { if (m_currentItemUrl != QUrl()) { @@ -1716,35 +1811,7 @@ void DolphinView::updateViewState() m_container->verticalScrollBar()->setValue(y); } - if (!m_selectedUrls.isEmpty()) { - KItemListSelectionManager *selectionManager = m_container->controller()->selectionManager(); - - // if there is a selection already, leave it that way - if (!selectionManager->hasSelection()) { - if (m_clearSelectionBeforeSelectingNewItems) { - selectionManager->clearSelection(); - m_clearSelectionBeforeSelectingNewItems = false; - } - - KItemSet selectedItems = selectionManager->selectedItems(); - - QList<QUrl>::iterator it = m_selectedUrls.begin(); - while (it != m_selectedUrls.end()) { - const int index = m_model->index(*it); - if (index >= 0) { - selectedItems.insert(index); - it = m_selectedUrls.erase(it); - } else { - ++it; - } - } - - if (!selectedItems.isEmpty()) { - selectionManager->beginAnchoredSelection(selectionManager->currentItem()); - selectionManager->setSelectedItems(selectedItems); - } - } - } + updateSelectionState(); } void DolphinView::hideToolTip(const ToolTipManager::HideBehavior behavior) @@ -1760,6 +1827,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(); @@ -2158,6 +2230,8 @@ void DolphinView::pasteToUrl(const QUrl &url) KJobWidgets::setWindow(job, this); 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::result, this, &DolphinView::slotJobResult); } diff --git a/src/views/dolphinview.h b/src/views/dolphinview.h index 6394b885c..705272308 100644 --- a/src/views/dolphinview.h +++ b/src/views/dolphinview.h @@ -335,6 +335,17 @@ public: */ void hideToolTip(const ToolTipManager::HideBehavior behavior = ToolTipManager::HideBehavior::Later); + /** + * Check if the space key should be handled as a normal key, even if it's + * used as a keyboard shortcut. + * + * See BUG 465489 + */ + bool handleSpaceAsNormalKey() const; + + /** Activates the view if the item list container gets focus. */ + bool eventFilter(QObject *watched, QEvent *event) override; + public Q_SLOTS: /** * Changes the directory to \a url. If the current directory is equal to @@ -426,9 +437,6 @@ public Q_SLOTS: */ void updateViewState(); - /** Activates the view if the item list container gets focus. */ - bool eventFilter(QObject *watched, QEvent *event) override; - Q_SIGNALS: /** * Is emitted if the view has been activated by e. g. a mouse click. @@ -666,7 +674,7 @@ private Q_SLOTS: void slotMouseButtonPressed(int itemIndex, Qt::MouseButtons buttons); void slotRenameDialogRenamingFinished(const QList<QUrl> &urls); void slotSelectedItemTextPressed(int index); - void slotCopyingDone(KIO::Job *, const QUrl &, const QUrl &to); + void slotItemCreatedFromJob(KIO::Job *, const QUrl &, const QUrl &to); void slotIncreaseZoom(); void slotDecreaseZoom(); void slotSwipeUp(); @@ -928,9 +936,11 @@ private: // resolution scroll wheels) int m_controlWheelAccumulatedDelta; - QList<QUrl> m_selectedUrls; // Used for making the view to remember selections after F5 + QList<QUrl> m_selectedUrls; // Used for making the view to remember selections after F5 and file operations bool m_clearSelectionBeforeSelectingNewItems; bool m_markFirstNewlySelectedItemAsCurrent; + /// Decides whether items created by jobs should automatically be selected. + bool m_selectJobCreatedItems; VersionControlObserver *m_versionControlObserver; @@ -950,6 +960,7 @@ private: friend class DolphinDetailsViewTest; friend class DolphinMainWindowTest; friend class DolphinPart; // Accesses m_model + void updateSelectionState(); }; /// Allow using DolphinView::Mode in QVariant |
