From 158d12ac37d27023cabf86d165630ec0c9309d4b Mon Sep 17 00:00:00 2001 From: Nathaniel Graham Date: Fri, 20 Dec 2019 10:07:25 -0700 Subject: [PATCH] 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