From 8e97c4981ceee5dacd1e2d63f7ca95f95b574b9b Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Sun, 15 Mar 2020 19:54:17 +0100 Subject: [PATCH 1/4] GIT_SILENT Upgrade release service version to 20.03.80. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ab79e643c..db2549a1de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.0) # KDE Application Version, managed by release script set (RELEASE_SERVICE_VERSION_MAJOR "20") set (RELEASE_SERVICE_VERSION_MINOR "03") -set (RELEASE_SERVICE_VERSION_MICRO "70") +set (RELEASE_SERVICE_VERSION_MICRO "80") set (RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}") project(Dolphin VERSION ${RELEASE_SERVICE_VERSION}) From 405dd624fb6b708eea8ec82ef913fe820c51c654 Mon Sep 17 00:00:00 2001 From: Nathaniel Graham Date: Fri, 20 Dec 2019 10:07:25 -0700 Subject: [PATCH 2/4] Add Duplicate feature Summary: Adds a Duplicate feature to Dolphin, showing up as a menu item in the File menu that appears when one or more items are selected and the directory is writable. Duplicated items receive the names of the original files with " copy" appended before the file extension, if any. Test Plan: {F5201386} {F5201393} Test cases: - Try to duplicate when nothing is selected: **PASS**: menu item is grayed out - Try to duplicate anything on a read-only local volume: **PASS**: menu item is grayed out - Try to duplicate anything on a read-only samba share: **PASS**: menu item is grayed out - Duplicate single local file on R/W volume: **PASS**: item is duplicated and named correctly - Duplicate multiple local files on R/W volume: **PASS**: 3 items are duplicated, named correctly, and selected - Duplicate single local directory on R/W volume: **PASS**: item is duplicated and named correctly, but a rename operation is not initiated - Duplicate multiple local directories on R/W volume: **PASS**: 3 items are duplicated, named correctly, and selected - Duplicate single file on R/W samba share: **PASS**: item is duplicated and correctly - Duplicate multiple files on R/W samba share: **PASS**: 3 items are duplicated, named correctly, and selected - Duplicate single directory on R/W samba share: **PASS**: item is duplicated and named correctly - Duplicate multiple directory on R/W samba share: **PASS**: 3 items are duplicated, named correctly, and selected - Try to undo a successful duplication: **PASS**: operation is undone This is my first attempt at a big change like this and I'm sure it's full of issues. I will accept any and all suggestions for improvement. :) Reviewers: #dolphin, #kde_applications, elvisangelaccio, dfaure, broulik, davidedmundson Subscribers: kfm-devel, meven, markg, fazevedo, cfeck, #dolphin Tags: #dolphin Differential Revision: https://phabricator.kde.org/D8208 --- src/dolphincontextmenu.cpp | 1 + src/dolphinmainwindow.cpp | 2 ++ src/dolphinui.rc | 5 ++- src/views/dolphinview.cpp | 45 ++++++++++++++++++++++++++ src/views/dolphinview.h | 6 ++++ src/views/dolphinviewactionhandler.cpp | 13 ++++++++ src/views/dolphinviewactionhandler.h | 5 +++ 7 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/dolphincontextmenu.cpp b/src/dolphincontextmenu.cpp index 9f39671997..e80283c58b 100644 --- a/src/dolphincontextmenu.cpp +++ b/src/dolphincontextmenu.cpp @@ -396,6 +396,7 @@ void DolphinContextMenu::insertDefaultItemActions(const KFileItemListProperties& addAction(collection->action(KStandardAction::name(KStandardAction::Cut))); addAction(collection->action(KStandardAction::name(KStandardAction::Copy))); addAction(createPasteAction()); + addAction(m_mainWindow->actionCollection()->action(QStringLiteral("duplicate"))); addSeparator(); diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index 642c24e60d..399901688a 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -1905,6 +1905,7 @@ void DolphinMainWindow::updateFileAndEditActions() QAction* cutAction = col->action(KStandardAction::name(KStandardAction::Cut)); QAction* deleteWithTrashShortcut = col->action(QStringLiteral("delete_shortcut")); // see DolphinViewActionHandler QAction* showTarget = col->action(QStringLiteral("show_target")); + QAction* duplicateAction = col->action(QStringLiteral("duplicate")); // see DolphinViewActionHandler if (list.length() == 1 && list.first().isDir()) { addToPlacesAction->setEnabled(true); @@ -1921,6 +1922,7 @@ void DolphinMainWindow::updateFileAndEditActions() deleteWithTrashShortcut->setEnabled(capabilities.supportsDeleting() && !enableMoveToTrash); cutAction->setEnabled(capabilities.supportsMoving()); showTarget->setEnabled(list.length() == 1 && list.at(0).isLink()); + duplicateAction->setEnabled(capabilities.supportsWriting()); } } diff --git a/src/dolphinui.rc b/src/dolphinui.rc index e1bb9ee58d..e717b67ae9 100644 --- a/src/dolphinui.rc +++ b/src/dolphinui.rc @@ -1,5 +1,5 @@ - + @@ -11,6 +11,7 @@ + @@ -81,6 +82,7 @@ + @@ -91,6 +93,7 @@ + diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp index cfece0fe6b..776436032e 100644 --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -63,6 +63,7 @@ #include #include #include +#include #include #include #include @@ -704,6 +705,50 @@ void DolphinView::pasteIntoFolder() } } +void DolphinView::duplicateSelectedItems() +{ + const KFileItemList itemList = selectedItems(); + if (itemList.isEmpty()) { + return; + } + + const QMimeDatabase db; + + // 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 originalFileName = item.name(); + QString extension = db.suffixForFileName(originalFileName); + + QUrl duplicateURL = originalURL; + + // No extension; new filename is " copy" + if (extension.isEmpty()) { + duplicateURL.setPath(i18nc(" copy", "%1 copy", originalURL.path())); + // There's an extension; new filename is " copy." + } else { + // Need to add a dot since QMimeDatabase::suffixForFileName() doesn't include it + extension = QLatin1String(".") + extension; + const QString directoryPath = originalURL.adjusted(QUrl::RemoveFilename).path(); + const QString originalFilenameWithoutExtension = originalFileName.chopped(extension.size()); + // Preserve file's original filename extension in case the casing differs + // from what QMimeDatabase::suffixForFileName() returned + const QString originalExtension = originalFileName.right(extension.size()); + duplicateURL.setPath(i18nc(" copy.", "%1%2 copy%3", directoryPath, originalFilenameWithoutExtension, originalExtension)); + } + + KIO::CopyJob* job = KIO::copyAs(originalURL, duplicateURL, KIO::HideProgressInfo); + KJobWidgets::setWindow(job, this); + + if (job) { + newSelection << duplicateURL; + KIO::FileUndoManager::self()->recordCopyJob(job); + } + } +} + void DolphinView::stopLoading() { m_model->cancelDirectoryLoading(); diff --git a/src/views/dolphinview.h b/src/views/dolphinview.h index 60ecb2a952..83c5f92a4d 100644 --- a/src/views/dolphinview.h +++ b/src/views/dolphinview.h @@ -374,6 +374,12 @@ public slots: */ void pasteIntoFolder(); + /** + * Creates duplicates of selected items, appending "copy" + * to the end. + */ + void duplicateSelectedItems(); + /** * Handles a drop of @p dropEvent onto widget @p dropWidget and destination @p destUrl */ diff --git a/src/views/dolphinviewactionhandler.cpp b/src/views/dolphinviewactionhandler.cpp index ef9f317ee1..c61e1aaa9f 100644 --- a/src/views/dolphinviewactionhandler.cpp +++ b/src/views/dolphinviewactionhandler.cpp @@ -136,6 +136,13 @@ void DolphinViewActionHandler::createActions() deleteWithTrashShortcut->setEnabled(false); connect(deleteWithTrashShortcut, &QAction::triggered, this, &DolphinViewActionHandler::slotDeleteItems); + QAction* duplicateAction = m_actionCollection->addAction(QStringLiteral("duplicate")); + duplicateAction->setText(i18nc("@action:inmenu File", "Duplicate Here")); + duplicateAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-duplicate"))); + m_actionCollection->setDefaultShortcut(duplicateAction, Qt::CTRL | Qt::Key_D); + duplicateAction->setEnabled(false); + connect(duplicateAction, &QAction::triggered, this, &DolphinViewActionHandler::slotDuplicate); + QAction *propertiesAction = m_actionCollection->addAction( QStringLiteral("properties") ); // Well, it's the File menu in dolphinmainwindow and the Edit menu in dolphinpart... :) propertiesAction->setText( i18nc("@action:inmenu File", "Properties") ); @@ -680,6 +687,12 @@ void DolphinViewActionHandler::slotAdjustViewProperties() delete dialog; } +void DolphinViewActionHandler::slotDuplicate() +{ + emit actionBeingHandled(); + m_currentView->duplicateSelectedItems(); +} + void DolphinViewActionHandler::slotProperties() { KPropertiesDialog* dialog = nullptr; diff --git a/src/views/dolphinviewactionhandler.h b/src/views/dolphinviewactionhandler.h index f931b3b9c5..3228e7364c 100644 --- a/src/views/dolphinviewactionhandler.h +++ b/src/views/dolphinviewactionhandler.h @@ -206,6 +206,11 @@ private Q_SLOTS: */ void slotAdjustViewProperties(); + /** + * Begins a duplicate operation on the selected files + */ + void slotDuplicate(); + /** * Connected to the "properties" action. * Opens the properties dialog for the selected items of the From fa806d48dafec0c47141381740a5d7604293d32d Mon Sep 17 00:00:00 2001 From: Elvis Angelaccio Date: Tue, 24 Dec 2019 18:28:26 +0100 Subject: [PATCH 3/4] Exclude daemonized processes from Dolphin::attachToExistingInstance() Summary: `dolphin --daemon` does not have the `/dolphin/Dolphin_1` dbus path, because it doesn't have any DolphinMainWindow. Instead of working around this issue (as we did in D21666 and D25510), just exclude these processes from the list of dbus instances checked by `Dolphin::attachToExistingInstance()`. CCBUG: 408244 Test Plan: Same test plan as in D21666 and D25510 Reviewers: #dolphin Subscribers: kfm-devel Tags: #dolphin Differential Revision: https://phabricator.kde.org/D26213 --- src/dbusinterface.cpp | 14 ++++++++++++-- src/dbusinterface.h | 13 +++++++++++++ src/global.cpp | 2 +- src/global.h | 4 +++- src/main.cpp | 1 + 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/dbusinterface.cpp b/src/dbusinterface.cpp index fd2d229a29..abdb3a0fef 100644 --- a/src/dbusinterface.cpp +++ b/src/dbusinterface.cpp @@ -44,7 +44,7 @@ void DBusInterface::ShowFolders(const QStringList& uriList, const QString& start if (urls.isEmpty()) { return; } - const auto serviceName = QStringLiteral("org.kde.dolphin-%1").arg(QCoreApplication::applicationPid()); + const auto serviceName = isDaemon() ? QString() : QStringLiteral("org.kde.dolphin-%1").arg(QCoreApplication::applicationPid()); if(!Dolphin::attachToExistingInstance(urls, false, GeneralSettings::splitView(), serviceName)) { Dolphin::openNewWindow(urls); } @@ -57,7 +57,7 @@ void DBusInterface::ShowItems(const QStringList& uriList, const QString& startUp if (urls.isEmpty()) { return; } - const auto serviceName = QStringLiteral("org.kde.dolphin-%1").arg(QCoreApplication::applicationPid()); + const auto serviceName = isDaemon() ? QString() : QStringLiteral("org.kde.dolphin-%1").arg(QCoreApplication::applicationPid()); if(!Dolphin::attachToExistingInstance(urls, true, GeneralSettings::splitView(), serviceName)) { Dolphin::openNewWindow(urls, nullptr, Dolphin::OpenNewWindowFlag::Select); }; @@ -71,3 +71,13 @@ void DBusInterface::ShowItemProperties(const QStringList& uriList, const QString KPropertiesDialog::showDialog(urls); } } + +void DBusInterface::setAsDaemon() +{ + m_isDaemon = true; +} + +bool DBusInterface::isDaemon() const +{ + return m_isDaemon; +} diff --git a/src/dbusinterface.h b/src/dbusinterface.h index baf804f68a..391916d627 100644 --- a/src/dbusinterface.h +++ b/src/dbusinterface.h @@ -32,6 +32,19 @@ public: Q_SCRIPTABLE void ShowFolders(const QStringList& uriList, const QString& startUpId); Q_SCRIPTABLE void ShowItems(const QStringList& uriList, const QString& startUpId); Q_SCRIPTABLE void ShowItemProperties(const QStringList& uriList, const QString& startUpId); + + /** + * Set whether this interface has been created by dolphin --deamon. + */ + void setAsDaemon(); + + /** + * @return Whether this interface has been created by dolphin --deamon. + */ + bool isDaemon() const; + +private: + bool m_isDaemon = false; }; #endif // DBUSINTERFACE_H diff --git a/src/global.cpp b/src/global.cpp index 9aff25b26d..34ed4e824b 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -82,7 +82,7 @@ bool Dolphin::attachToExistingInstance(const QList& inputUrls, bool openFi QSharedPointer preferredInterface( new QDBusInterface(preferredService, QStringLiteral("/dolphin/Dolphin_1"), - QString()) // #414402: use empty interface name to prevent QtDBus from caching the interface. + QStringLiteral("org.kde.dolphin.MainWindow")) ); if (preferredInterface->isValid() && !preferredInterface->lastError().isValid()) { dolphinInterfaces.append(qMakePair(preferredInterface, QStringList())); diff --git a/src/global.h b/src/global.h index f203615f1d..7ee564581c 100644 --- a/src/global.h +++ b/src/global.h @@ -45,7 +45,9 @@ namespace Dolphin { /** * Attaches URLs to an existing Dolphin instance if possible. - * Returns true if URLs were successfully attached + * If @p preferredService is a valid dbus service, it will be tried first. + * @p preferredService needs to support the org.kde.dolphin.MainWindow dbus interface with the /dolphin/Dolphin_1 path. + * Returns true if the URLs were successfully attached. */ bool attachToExistingInstance(const QList& inputUrls, bool openFiles, bool splitView, const QString& preferredService = QString()); diff --git a/src/main.cpp b/src/main.cpp index 2b2674f9d9..5932df5ce0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -143,6 +143,7 @@ extern "C" Q_DECL_EXPORT int kdemain(int argc, char **argv) if (parser.isSet(QStringLiteral("daemon"))) { KDBusService dolphinDBusService; DBusInterface interface; + interface.setAsDaemon(); return app.exec(); } From 869b8d7e303b318d7370309d6caa82a0ba8056bf Mon Sep 17 00:00:00 2001 From: Elvis Angelaccio Date: Mon, 23 Dec 2019 11:06:54 +0100 Subject: [PATCH 4/4] Switch to generated MainWindow dbus interface Summary: This allows compile-time checks for the main window dbus methods. Test Plan: Same test plan as in D21691, D21666 and D25510. Reviewers: #dolphin Subscribers: kfm-devel Tags: #dolphin Differential Revision: https://phabricator.kde.org/D26214 --- src/CMakeLists.txt | 4 ++++ src/dolphinmainwindow.cpp | 4 ++++ src/global.cpp | 40 +++++++++++++++++++++------------------ 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e8d623d2f2..02a43a2094 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -269,6 +269,10 @@ kconfig_add_kcfg_files(dolphinstatic_SRCS GENERATE_MOC qt5_add_resources(dolphinstatic_SRCS dolphin.qrc) +qt5_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/dolphinmainwindow.h org.kde.DolphinMainWindow.xml) +qt5_add_dbus_adaptor(dolphinstatic_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.DolphinMainWindow.xml dolphinmainwindow.h DolphinMainWindow) +qt5_add_dbus_interface(dolphinstatic_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.DolphinMainWindow.xml dolphinmainwindowinterface) + add_library(dolphinstatic STATIC ${dolphinstatic_SRCS}) target_include_directories(dolphinstatic SYSTEM PRIVATE ${PHONON_INCLUDES}) diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index 399901688a..f88bc3f44f 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -21,6 +21,7 @@ #include "dolphinmainwindow.h" +#include "dolphinmainwindowadaptor.h" #include "config-terminal.h" #include "global.h" #include "dolphinbookmarkhandler.h" @@ -118,6 +119,9 @@ DolphinMainWindow::DolphinMainWindow() : m_forwardAction(nullptr) { Q_INIT_RESOURCE(dolphin); + + new MainWindowAdaptor(this); + #ifndef Q_OS_WIN setWindowFlags(Qt::WindowContextHelpButtonHint); #endif diff --git a/src/global.cpp b/src/global.cpp index 34ed4e824b..5236fa4d10 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -21,14 +21,13 @@ #include "dolphin_generalsettings.h" #include "dolphindebug.h" +#include "dolphinmainwindowinterface.h" #include #include #include #include -#include -#include QList Dolphin::validateUris(const QStringList& uriList) { @@ -72,18 +71,19 @@ void Dolphin::openNewWindow(const QList &urls, QWidget *window, const Open bool Dolphin::attachToExistingInstance(const QList& inputUrls, bool openFiles, bool splitView, const QString& preferredService) { + bool attached = false; + // TODO: once Wayland clients can raise or activate themselves remove check from conditional if (KWindowSystem::isPlatformWayland() || inputUrls.isEmpty() || !GeneralSettings::openExternallyCalledFolderInNewTab()) { return false; } - QVector, QStringList>> dolphinInterfaces; + QVector, QStringList>> dolphinInterfaces; if (!preferredService.isEmpty()) { - QSharedPointer preferredInterface( - new QDBusInterface(preferredService, - QStringLiteral("/dolphin/Dolphin_1"), - QStringLiteral("org.kde.dolphin.MainWindow")) - ); + QSharedPointer preferredInterface( + new OrgKdeDolphinMainWindowInterface(preferredService, + QStringLiteral("/dolphin/Dolphin_1"), + QDBusConnection::sessionBus())); if (preferredInterface->isValid() && !preferredInterface->lastError().isValid()) { dolphinInterfaces.append(qMakePair(preferredInterface, QStringList())); } @@ -98,11 +98,10 @@ bool Dolphin::attachToExistingInstance(const QList& inputUrls, bool openFi for (const QString& service : dbusServices) { if (service.startsWith(pattern) && !service.endsWith(myPid)) { // Check if instance can handle our URLs - QSharedPointer interface( - new QDBusInterface(service, - QStringLiteral("/dolphin/Dolphin_1"), - QStringLiteral("org.kde.dolphin.MainWindow")) - ); + QSharedPointer interface( + new OrgKdeDolphinMainWindowInterface(service, + QStringLiteral("/dolphin/Dolphin_1"), + QDBusConnection::sessionBus())); if (interface->isValid() && !interface->lastError().isValid()) { dolphinInterfaces.append(qMakePair(interface, QStringList())); } @@ -120,8 +119,9 @@ bool Dolphin::attachToExistingInstance(const QList& inputUrls, bool openFi for (const QString& url : urls) { bool urlFound = false; for (auto& interface: dolphinInterfaces) { - QDBusReply isUrlOpenReply = interface.first->call(QStringLiteral("isUrlOpen"), url); - if (isUrlOpenReply.isValid() && isUrlOpenReply.value()) { + auto isUrlOpenReply = interface.first->isUrlOpen(url); + isUrlOpenReply.waitForFinished(); + if (!isUrlOpenReply.isError() && isUrlOpenReply.value()) { interface.second.append(url); urlFound = true; break; @@ -135,9 +135,13 @@ bool Dolphin::attachToExistingInstance(const QList& inputUrls, bool openFi for (const auto& interface: dolphinInterfaces) { if (!interface.second.isEmpty()) { - interface.first->call(openFiles ? QStringLiteral("openFiles") : QStringLiteral("openDirectories"), interface.second, splitView); - interface.first->call(QStringLiteral("activateWindow")); + auto reply = openFiles ? interface.first->openFiles(interface.second, splitView) : interface.first->openDirectories(interface.second, splitView); + reply.waitForFinished(); + if (!reply.isError()) { + interface.first->activateWindow(); + attached = true; + } } } - return true; + return attached; }