diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -1394,6 +1394,7 @@ 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 KFileItemListProperties capabilities(list); const bool enableMoveToTrash = capabilities.isLocal() && capabilities.supportsMoving(); @@ -1404,6 +1405,7 @@ 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 --- a/src/dolphinui.rc +++ b/src/dolphinui.rc @@ -1,5 +1,5 @@ - + @@ -9,6 +9,7 @@ + @@ -72,6 +73,7 @@ + @@ -82,6 +84,7 @@ + diff --git a/src/views/dolphinview.h b/src/views/dolphinview.h --- a/src/views/dolphinview.h +++ b/src/views/dolphinview.h @@ -363,6 +363,13 @@ void pasteIntoFolder(); /** + * Creates duplicates of selected items, appending "copy" + * to the end. If only one file is selected, also initiates + * a rename operation on it. + */ + void duplicateSelectedItems(); + + /** * Handles a drop of @p dropEvent onto widget @p dropWidget and destination @p destUrl */ void dropUrls(const QUrl &destUrl, QDropEvent *dropEvent, QWidget *dropWidget); diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -703,6 +703,63 @@ } } +void DolphinView::duplicateSelectedItems() +{ + const KFileItemList itemList = selectedItems(); + if (itemList.isEmpty()) { + return; + } + + QUrl originalURL; + QFileInfo originalFile; + QString originalPath; + QString suffix; + QString suffixString; + QUrl duplicateURL; + QString duplicatePath; + QList newSelection; + + // If multiple items are selected, duplicate them all + for (const auto &item) { + originalURL = item.url(); + duplicateURL = originalURL; + originalFile = QFileInfo(originalURL.path()); + + // We extract the file and suffix separately so we can append "copy" + // to the end of the file name, but before the suffix, if there is one + originalPath = originalFile.path() + "/" + originalFile.baseName(); + suffix = originalFile.completeSuffix(); + + // Don't add an unnecessary dot for directories or files with no suffix + if (suffix.isEmpty()) { + suffixString.clear(); + } else { + suffixString = "." + suffix; + } + + if (originalPath.endsWith(i18n(" copy"))) { + duplicatePath = originalPath + i18n(" 1") + suffixString; + } else { + duplicatePath = originalPath + i18n(" copy") + suffixString; + } + duplicateURL.setPath(duplicatePath); + + KIO::CopyJob* job = KIO::copyAs(originalURL, duplicateURL, KIO::HideProgressInfo); + KJobWidgets::setWindow(job, this); + + if (job) { + newSelection << duplicateURL; + KIO::FileUndoManager::self()->recordCopyJob(job); + } + } + + forceUrlsSelection(newSelection.first(), newSelection); + emitSelectionChangedSignal(); + + connect(m_view, &DolphinItemListView::roleEditingFinished, + this, &DolphinView::slotRoleEditingFinished); +} + void DolphinView::stopLoading() { m_model->cancelDirectoryLoading(); diff --git a/src/views/dolphinviewactionhandler.h b/src/views/dolphinviewactionhandler.h --- a/src/views/dolphinviewactionhandler.h +++ b/src/views/dolphinviewactionhandler.h @@ -207,6 +207,11 @@ 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 * active view. The properties dialog shows information diff --git a/src/views/dolphinviewactionhandler.cpp b/src/views/dolphinviewactionhandler.cpp --- a/src/views/dolphinviewactionhandler.cpp +++ b/src/views/dolphinviewactionhandler.cpp @@ -126,6 +126,13 @@ deleteWithTrashShortcut->setEnabled(false); connect(deleteWithTrashShortcut, &QAction::triggered, this, &DolphinViewActionHandler::slotDeleteItems); + QAction* duplicateAction = m_actionCollection->addAction(QStringLiteral("duplicate")); + duplicateAction->setText(i18nc("@action:inmenu File", "Duplicate")); + 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") ); @@ -580,6 +587,12 @@ delete dialog; } +void DolphinViewActionHandler::slotDuplicate() +{ + emit actionBeingHandled(); + m_currentView->duplicateSelectedItems(); +} + void DolphinViewActionHandler::slotProperties() { KPropertiesDialog* dialog = nullptr;