diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -1331,6 +1331,7 @@ QAction* deleteAction = col->action(KStandardAction::name(KStandardAction::DeleteFile)); QAction* cutAction = col->action(KStandardAction::name(KStandardAction::Cut)); QAction* deleteWithTrashShortcut = col->action(QStringLiteral("delete_shortcut")); // see DolphinViewActionHandler + QAction* duplicateAction = col->action(QStringLiteral("duplicate")); // see DolphinViewActionHandler KFileItemListProperties capabilities(list); const bool enableMoveToTrash = capabilities.isLocal() && capabilities.supportsMoving(); @@ -1340,6 +1341,7 @@ deleteAction->setEnabled(capabilities.supportsDeleting()); deleteWithTrashShortcut->setEnabled(capabilities.supportsDeleting() && !enableMoveToTrash); cutAction->setEnabled(capabilities.supportsMoving()); + duplicateAction->setEnabled(capabilities.supportsWriting()); } } diff --git a/src/dolphinui.rc b/src/dolphinui.rc --- a/src/dolphinui.rc +++ b/src/dolphinui.rc @@ -1,14 +1,15 @@ - + - + + @@ -70,6 +71,7 @@ + @@ -80,6 +82,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 @@ -704,6 +704,80 @@ } } +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 (KFileItem item : itemList) { + 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 = ""; + } else { + suffixString = "." + suffix; + } + + if (originalPath.endsWith(i18n(" copy"))) { + duplicatePath = originalPath + QString(i18n(" 1")) + suffixString; + } else { + duplicatePath = originalPath + QString(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(); + + // This doesn't work because the new items' indices show up as -1; they don't appear to have shown up in m_model yet + KItemSet duplicatedItems; + for (QUrl url : newSelection) { + const int index = m_model->index(url); + if (index >= 0) { + duplicatedItems.insert(index); + } + } + + KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); + selectionManager->setSelectedItems(duplicatedItems); + emitSelectionChangedSignal(); + + connect(m_view, &DolphinItemListView::roleEditingFinished, + this, &DolphinView::slotRoleEditingFinished); + + if (m_selectedUrls.count() == 1) { + renameSelectedItems(); + } +} + 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 @@ -206,6 +206,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 @@ -128,6 +128,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") ); @@ -582,6 +589,12 @@ delete dialog; } +void DolphinViewActionHandler::slotDuplicate() +{ + emit actionBeingHandled(); + m_currentView->duplicateSelectedItems(); +} + void DolphinViewActionHandler::slotProperties() { KPropertiesDialog* dialog = 0;