[graphics/koko] src: Add paste action
Oliver Beard <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 6abd9f6134d95f6819bc50e4e33d4ed50b3772f2 by Oliver Beard.
Committed on 23/07/2026 at 08:00.
Pushed by olib into branch 'master'.
Add paste action
When nothing is selected, show a paste action. Since the gallery context menu selects the item, this is only shown as a page action.
Pasted items are selected, and the last pasted item is set as the current index.
In future, if we want to show actions for the current directory (by right-clicking empty area), we can have no selection and act on `FileMenuManager::rootUrl`.
M +50 -0 src/filemenumanager.cpp
M +12 -0 src/filemenumanager.h
M +3 -0 src/qml/ActionCollection.qml
M +45 -0 src/qml/gallery/GalleryPage.qml
https://invent.kde.org/graphics/koko/-/commit/6abd9f6134d95f6819bc50e4e33d4ed50b3772f2
diff --git a/src/filemenumanager.cpp b/src/filemenumanager.cpp
index 02a01f91..e3e66c10 100644
--- a/src/filemenumanager.cpp
+++ b/src/filemenumanager.cpp
@@ -40,6 +40,8 @@
#include <KIO/JobUiDelegate>
#include <KIO/JobUiDelegateFactory>
#include <KIO/OpenFileManagerWindowJob>
+#include <KIO/Paste>
+#include <KIO/PasteJob>
#include <KIO/RenameFileDialog>
#include <KIO/WidgetsAskUserActionHandler>
@@ -48,6 +50,7 @@ using namespace Qt::StringLiterals;
FileMenuManager::FileMenuManager(QObject *parent)
: QObject(parent)
{
+ connect(QApplication::clipboard(), &QClipboard::dataChanged, this, &FileMenuManager::updateActions);
}
QList<QUrl> FileMenuManager::urls() const
@@ -68,6 +71,24 @@ void FileMenuManager::setUrls(const QList<QUrl> &urls)
Q_EMIT urlsChanged();
}
+QUrl FileMenuManager::rootUrl() const
+{
+ return m_rootUrl;
+}
+
+void FileMenuManager::setRootUrl(const QUrl &url)
+{
+ if (m_rootUrl == url) {
+ return;
+ }
+
+ m_rootUrl = url;
+
+ updateActions();
+
+ Q_EMIT rootUrlChanged();
+}
+
bool FileMenuManager::enabled() const
{
return m_enabled;
@@ -111,6 +132,11 @@ bool FileMenuManager::canCopyPath() const
return m_canCopyPath;
}
+bool FileMenuManager::canPaste() const
+{
+ return m_canPaste;
+}
+
bool FileMenuManager::canRenameFile() const
{
return m_canRenameFile;
@@ -347,6 +373,30 @@ void FileMenuManager::updateActions()
}
Q_EMIT canCopyPathChanged();
+ // Paste action
+ m_canPaste = false;
+ disconnectStandardAction(KStandardActions::Paste);
+ if (m_enabled && !hasFile && KFileItem(m_rootUrl).isWritable()) {
+ m_canPaste = true;
+ auto pasteLambda = [this] {
+ if (!m_enabled) {
+ return;
+ }
+
+ auto pastedUrls = std::make_shared<QList<QUrl>>();
+
+ auto pasteJob = KIO::paste(QApplication::clipboard()->mimeData(), m_rootUrl);
+ connect(pasteJob, &KIO::PasteJob::itemCreated, this, [pastedUrls](const QUrl &url) {
+ *pastedUrls << url;
+ });
+ connect(pasteJob, &KJob::finished, this, [this, pastedUrls]() {
+ Q_EMIT this->pastedUrls(*pastedUrls);
+ });
+ };
+ connectStandardAction(KStandardActions::Paste, pasteLambda);
+ }
+ Q_EMIT canPasteChanged();
+
// Rename File action
m_canRenameFile = false;
disconnectStandardAction(KStandardActions::RenameFile);
diff --git a/src/filemenumanager.h b/src/filemenumanager.h
index bae9962a..98ea6e41 100644
--- a/src/filemenumanager.h
+++ b/src/filemenumanager.h
@@ -20,6 +20,7 @@ class FileMenuManager : public QObject
Q_OBJECT
Q_PROPERTY(QList<QUrl> urls READ urls WRITE setUrls NOTIFY urlsChanged FINAL)
+ Q_PROPERTY(QUrl rootUrl READ rootUrl WRITE setRootUrl NOTIFY rootUrlChanged FINAL)
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged FINAL)
Q_PROPERTY(QQuickWindow *window MEMBER m_window FINAL)
@@ -28,6 +29,7 @@ class FileMenuManager : public QObject
Q_PROPERTY(bool canOpenWith READ canOpenWith NOTIFY canOpenWithChanged FINAL)
Q_PROPERTY(bool canCopy READ canCopy NOTIFY canCopyChanged FINAL)
Q_PROPERTY(bool canCopyPath READ canCopyPath NOTIFY canCopyPathChanged FINAL)
+ Q_PROPERTY(bool canPaste READ canPaste NOTIFY canPasteChanged FINAL)
Q_PROPERTY(bool canRenameFile READ canRenameFile NOTIFY canRenameFileChanged FINAL)
Q_PROPERTY(bool canMoveToTrash READ canMoveToTrash NOTIFY canMoveToTrashChanged FINAL)
Q_PROPERTY(bool canDeleteFile READ canDeleteFile NOTIFY canDeleteFileChanged FINAL)
@@ -42,6 +44,9 @@ public:
QList<QUrl> urls() const;
void setUrls(const QList<QUrl> &urls);
+ QUrl rootUrl() const;
+ void setRootUrl(const QUrl &url);
+
bool enabled() const;
void setEnabled(const bool enabled);
@@ -50,6 +55,7 @@ public:
bool canOpenWith() const;
bool canCopy() const;
bool canCopyPath() const;
+ bool canPaste() const;
bool canRenameFile() const;
bool canMoveToTrash() const;
bool canDeleteFile() const;
@@ -58,20 +64,25 @@ public:
Q_SIGNALS:
void urlsChanged();
+ void rootUrlChanged();
void enabledChanged();
void canSaveAsChanged();
void canOpenFolderChanged();
void canOpenWithChanged();
void canCopyChanged();
void canCopyPathChanged();
+ void canPasteChanged();
void canRenameFileChanged();
void canMoveToTrashChanged();
void canDeleteFileChanged();
void canPrintChanged();
void canPropertiesChanged();
+ void pastedUrls(QList<QUrl> urls);
+
private:
QList<QUrl> m_urls;
+ QUrl m_rootUrl;
bool m_enabled = false;
QQuickWindow *m_window = nullptr;
@@ -80,6 +91,7 @@ private:
bool m_canOpenWith = false;
bool m_canCopy = false;
bool m_canCopyPath = false;
+ bool m_canPaste = false;
bool m_canRenameFile = false;
bool m_canMoveToTrash = false;
bool m_canDeleteFile = false;
diff --git a/src/qml/ActionCollection.qml b/src/qml/ActionCollection.qml
index ef2c5661..20ec3a45 100644
--- a/src/qml/ActionCollection.qml
+++ b/src/qml/ActionCollection.qml
@@ -121,6 +121,9 @@ AC.ActionCollectionManager {
text: i18nc("@action:inmenu", "Copy Location")
icon.name: "edit-copy-path"
}
+ AC.StandardActionData {
+ standardAction: AC.StandardActionData.Paste
+ }
AC.StandardActionData {
standardAction: AC.StandardActionData.RenameFile
}
diff --git a/src/qml/gallery/GalleryPage.qml b/src/qml/gallery/GalleryPage.qml
index 565a54d9..b26e347b 100644
--- a/src/qml/gallery/GalleryPage.qml
+++ b/src/qml/gallery/GalleryPage.qml
@@ -362,8 +362,46 @@ Kirigami.ScrollablePage {
Koko.FileMenuManager {
id: fileMenuManager
urls: selectionModel.selectedIndexes.map(index => selectionModel.model.data(index, AbstractGalleryModel.UrlRole))
+ rootUrl: page.galleryModel.path
enabled: page.visible && page.enabled
window: page.Window.window
+
+ // Handle selection of pasted items:
+
+ property url lastPastedUrl
+ property var pastedUrls: new Set()
+
+ onPastedUrls: (urls) => {
+ urls.forEach(url => fileMenuManager.pastedUrls.add(url));
+ fileMenuManager.lastPastedUrl = urls[urls.length - 1];
+ selectionModel.clearSelection();
+
+ // If it turns out we get told about pasted urls after the model includes them:
+ // onRowsInserted(undefined, 0, gridView.model.rowCount() - 1);
+ }
+
+ function onRowsInserted(parent, first, last) {
+ if (pastedUrls.size === 0) {
+ return;
+ }
+
+ for (let row = first; row <= last; ++row) {
+ const index = gridView.model.index(row, 0);
+ const url = gridView.model.data(index, AbstractGalleryModel.UrlRole);
+
+ if (fileMenuManager.pastedUrls.has(url)) {
+ fileMenuManager.pastedUrls.delete(url);
+ selectionModel.select(index, ItemSelectionModel.Select);
+
+ if (url === fileMenuManager.lastPastedUrl) {
+ gridView.currentIndex = row;
+ fileMenuManager.lastPastedUrl = undefined;
+ }
+ }
+ }
+ }
+
+ Component.onCompleted: gridView.model.rowsInserted.connect(onRowsInserted)
}
readonly property list<Kirigami.Action> fileMenuActions: [
@@ -402,6 +440,13 @@ Kirigami.ScrollablePage {
AC.ActionCollection.action: "CopyPath"
AC.ActionCollection.collection: "org.kde.koko.file"
},
+ Kirigami.Action {
+ displayHint: Kirigami.DisplayHint.AlwaysHide
+ enabled: fileMenuManager.enabled && fileMenuManager.canPaste && !page.showingCollections
+ visible: enabled
+ AC.ActionCollection.action: AC.StandardActionData.Paste
+ AC.ActionCollection.collection: "org.kde.koko.file"
+ },
Kirigami.Action {
displayHint: Kirigami.DisplayHint.AlwaysHide
enabled: fileMenuManager.enabled && fileMenuManager.canRenameFile && !page.showingCollections