1 /*
2     This file is part of the KDE libraries
3     SPDX-FileCopyrightText: 2013 Dawit Alemayehu <adawit@kde.org>
4 
5     SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7 
8 #include "../pathhelpers_p.h"
9 #include "clipboardupdater_p.h"
10 #include "copyjob.h"
11 #include "deletejob.h"
12 #include "jobclasses.h"
13 #include <KUrlMimeData>
14 
15 #include <QClipboard>
16 #include <QGuiApplication>
17 #include <QMimeData>
18 
19 using namespace KIO;
20 
overwriteUrlsInClipboard(KJob * job)21 static void overwriteUrlsInClipboard(KJob *job)
22 {
23     CopyJob *copyJob = qobject_cast<CopyJob *>(job);
24     FileCopyJob *fileCopyJob = qobject_cast<FileCopyJob *>(job);
25 
26     if (!copyJob && !fileCopyJob) {
27         return;
28     }
29 
30     QList<QUrl> newUrls;
31 
32     if (copyJob) {
33         const auto srcUrls = copyJob->srcUrls();
34         newUrls.reserve(srcUrls.size());
35         for (const QUrl &url : srcUrls) {
36             QUrl dUrl = copyJob->destUrl().adjusted(QUrl::StripTrailingSlash);
37             dUrl.setPath(concatPaths(dUrl.path(), url.fileName()));
38             newUrls.append(dUrl);
39         }
40     } else if (fileCopyJob) {
41         newUrls << fileCopyJob->destUrl();
42     }
43 
44     QMimeData *mime = new QMimeData();
45     mime->setUrls(newUrls);
46     QGuiApplication::clipboard()->setMimeData(mime);
47 }
48 
updateUrlsInClipboard(KJob * job)49 static void updateUrlsInClipboard(KJob *job)
50 {
51     CopyJob *copyJob = qobject_cast<CopyJob *>(job);
52     FileCopyJob *fileCopyJob = qobject_cast<FileCopyJob *>(job);
53 
54     if (!copyJob && !fileCopyJob) {
55         return;
56     }
57 
58     QClipboard *clipboard = QGuiApplication::clipboard();
59     auto mimeData = clipboard->mimeData();
60     if (!mimeData) {
61         return;
62     }
63 
64     QList<QUrl> clipboardUrls = KUrlMimeData::urlsFromMimeData(mimeData);
65     bool update = false;
66 
67     if (copyJob) {
68         const QList<QUrl> urls = copyJob->srcUrls();
69         for (const QUrl &url : urls) {
70             const int index = clipboardUrls.indexOf(url);
71             if (index > -1) {
72                 QUrl dUrl = copyJob->destUrl().adjusted(QUrl::StripTrailingSlash);
73                 dUrl.setPath(concatPaths(dUrl.path(), url.fileName()));
74                 clipboardUrls.replace(index, dUrl);
75                 update = true;
76             }
77         }
78     } else if (fileCopyJob) {
79         const int index = clipboardUrls.indexOf(fileCopyJob->srcUrl());
80         if (index > -1) {
81             clipboardUrls.replace(index, fileCopyJob->destUrl());
82             update = true;
83         }
84     }
85 
86     if (update) {
87         QMimeData *mime = new QMimeData();
88         mime->setUrls(clipboardUrls);
89         clipboard->setMimeData(mime);
90     }
91 }
92 
removeUrlsFromClipboard(KJob * job)93 static void removeUrlsFromClipboard(KJob *job)
94 {
95     SimpleJob *simpleJob = qobject_cast<SimpleJob *>(job);
96     DeleteJob *deleteJob = qobject_cast<DeleteJob *>(job);
97 
98     if (!simpleJob && !deleteJob) {
99         return;
100     }
101 
102     QList<QUrl> deletedUrls;
103     if (simpleJob) {
104         deletedUrls << simpleJob->url();
105     } else if (deleteJob) {
106         deletedUrls << deleteJob->urls();
107     }
108 
109     if (deletedUrls.isEmpty()) {
110         return;
111     }
112 
113     QClipboard *clipboard = QGuiApplication::clipboard();
114     auto mimeData = clipboard->mimeData();
115     if (!mimeData) {
116         return;
117     }
118 
119     QList<QUrl> clipboardUrls = KUrlMimeData::urlsFromMimeData(mimeData);
120     quint32 removedCount = 0;
121 
122     for (const QUrl &url : std::as_const(deletedUrls)) {
123         removedCount += clipboardUrls.removeAll(url);
124     }
125 
126     if (removedCount > 0) {
127         QMimeData *mime = new QMimeData();
128         if (!clipboardUrls.isEmpty()) {
129             mime->setUrls(clipboardUrls);
130         }
131         clipboard->setMimeData(mime);
132     }
133 }
134 
slotResult(KJob * job)135 void ClipboardUpdater::slotResult(KJob *job)
136 {
137     if (job->error()) {
138         return;
139     }
140 
141     switch (m_mode) {
142     case JobUiDelegateExtension::UpdateContent:
143         updateUrlsInClipboard(job);
144         break;
145     case JobUiDelegateExtension::OverwriteContent:
146         overwriteUrlsInClipboard(job);
147         break;
148     case JobUiDelegateExtension::RemoveContent:
149         removeUrlsFromClipboard(job);
150         break;
151     }
152 }
153 
setMode(JobUiDelegateExtension::ClipboardUpdaterMode mode)154 void ClipboardUpdater::setMode(JobUiDelegateExtension::ClipboardUpdaterMode mode)
155 {
156     m_mode = mode;
157 }
158 
update(const QUrl & srcUrl,const QUrl & destUrl)159 void ClipboardUpdater::update(const QUrl &srcUrl, const QUrl &destUrl)
160 {
161     QClipboard *clipboard = QGuiApplication::clipboard();
162     auto mimeData = clipboard->mimeData();
163     if (mimeData && mimeData->hasUrls()) {
164         QList<QUrl> clipboardUrls = KUrlMimeData::urlsFromMimeData(clipboard->mimeData());
165         const int index = clipboardUrls.indexOf(srcUrl);
166         if (index > -1) {
167             clipboardUrls.replace(index, destUrl);
168             QMimeData *mime = new QMimeData();
169             mime->setUrls(clipboardUrls);
170             clipboard->setMimeData(mime);
171         }
172     }
173 }
174 
ClipboardUpdater(Job * job,JobUiDelegateExtension::ClipboardUpdaterMode mode)175 ClipboardUpdater::ClipboardUpdater(Job *job, JobUiDelegateExtension::ClipboardUpdaterMode mode)
176     : QObject(job)
177     , m_mode(mode)
178 {
179     Q_ASSERT(job);
180     connect(job, &KJob::result, this, &ClipboardUpdater::slotResult);
181 }
182