KIO

dropjob.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2014 David Faure <faure@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "dropjob.h"
9
10#include "job_p.h"
11#include "jobuidelegate.h"
12#include "jobuidelegateextension.h"
13#include "kio_widgets_debug.h"
14#include "pastejob.h"
15#include "pastejob_p.h"
16
17#include <KConfigGroup>
18#include <KCoreDirLister>
19#include <KDesktopFile>
20#include <KFileItem>
21#include <KFileItemListProperties>
22#include <KIO/ApplicationLauncherJob>
23#include <KIO/CopyJob>
24#include <KIO/DndPopupMenuPlugin>
25#include <KIO/FileUndoManager>
26#include <KJobWidgets>
27#include <KJobWindows>
28#include <KLocalizedString>
29#include <KPluginFactory>
30#include <KPluginMetaData>
31#include <KProtocolManager>
32#include <KService>
33#include <KUrlMimeData>
34
35#ifdef WITH_QTDBUS
36#include <QDBusConnection>
37#include <QDBusPendingCall>
38#endif
39
40#include <QDropEvent>
41#include <QFileInfo>
42#include <QMenu>
43#include <QMimeData>
44#include <QProcess>
45#include <QTimer>
46#include <QWindow>
47
48using namespace KIO;
49
50Q_DECLARE_METATYPE(Qt::DropAction)
51
52namespace KIO
53{
54class DropMenu;
55}
56
57class KIO::DropMenu : public QMenu
58{
60public:
61 explicit DropMenu(QWidget *parent = nullptr);
62 ~DropMenu() override;
63
64 void addCancelAction();
65 void addExtraActions(const QList<QAction *> &appActions, const QList<QAction *> &pluginActions);
66
67private:
68 QList<QAction *> m_appActions;
69 QList<QAction *> m_pluginActions;
70 QAction *m_lastSeparator;
71 QAction *m_extraActionsSeparator;
72 QAction *m_cancelAction;
73};
74
75static const QString s_applicationSlashXDashKDEDashArkDashDnDExtractDashService = //
76 QStringLiteral("application/x-kde-ark-dndextract-service");
77static const QString s_applicationSlashXDashKDEDashArkDashDnDExtractDashPath = //
78 QStringLiteral("application/x-kde-ark-dndextract-path");
79
80class KIO::DropJobPrivate : public KIO::JobPrivate
81{
82public:
83 DropJobPrivate(const QDropEvent *dropEvent, const QUrl &destUrl, DropJobFlags dropjobFlags, JobFlags flags)
84 : JobPrivate()
85 , m_mimeData(dropEvent->mimeData()) // Extract everything from the dropevent, since it will be deleted before the job starts
86 , m_urls(KUrlMimeData::urlsFromMimeData(m_mimeData, KUrlMimeData::PreferLocalUrls, &m_metaData))
87 , m_dropAction(dropEvent->dropAction())
88 , m_relativePos(dropEvent->position().toPoint())
89 , m_keyboardModifiers(dropEvent->modifiers())
90 , m_hasArkFormat(m_mimeData->hasFormat(s_applicationSlashXDashKDEDashArkDashDnDExtractDashService)
91 && m_mimeData->hasFormat(s_applicationSlashXDashKDEDashArkDashDnDExtractDashPath))
92 , m_destUrl(destUrl)
93 , m_destItem(KCoreDirLister::cachedItemForUrl(destUrl))
94 , m_flags(flags)
95 , m_dropjobFlags(dropjobFlags)
96 , m_triggered(false)
97 {
98 // Check for the drop of a bookmark -> we want a Link action
99 if (m_mimeData->hasFormat(QStringLiteral("application/x-xbel"))) {
101 m_dropAction = Qt::LinkAction;
102 }
103 if (m_destItem.isNull() && m_destUrl.isLocalFile()) {
104 m_destItem = KFileItem(m_destUrl);
105 }
106
107 if (m_hasArkFormat) {
108 m_remoteArkDBusClient = QString::fromUtf8(m_mimeData->data(s_applicationSlashXDashKDEDashArkDashDnDExtractDashService));
109 m_remoteArkDBusPath = QString::fromUtf8(m_mimeData->data(s_applicationSlashXDashKDEDashArkDashDnDExtractDashPath));
110 }
111
112 if (!(m_flags & KIO::NoPrivilegeExecution)) {
113 m_privilegeExecutionEnabled = true;
114 switch (m_dropAction) {
115 case Qt::CopyAction:
116 m_operationType = Copy;
117 break;
118 case Qt::MoveAction:
119 m_operationType = Move;
120 break;
121 case Qt::LinkAction:
122 m_operationType = Symlink;
123 break;
124 default:
125 m_operationType = Other;
126 break;
127 }
128 }
129 }
130
131 bool destIsDirectory() const
132 {
133 if (!m_destItem.isNull()) {
134 return m_destItem.isDir();
135 }
136 // We support local dir, remote dir, local desktop file, local executable.
137 // So for remote URLs, we just assume they point to a directory, the user will get an error from KIO::copy if not.
138 return true;
139 }
140 void handleCopyToDirectory();
141 void slotDropActionDetermined(int error);
142 void handleDropToDesktopFile();
143 void handleDropToExecutable();
144 void fillPopupMenu(KIO::DropMenu *popup);
145 void addPluginActions(KIO::DropMenu *popup, const KFileItemListProperties &itemProps);
146 void doCopyToDirectory();
147
148 QWindow *transientParent();
149
150 QPointer<const QMimeData> m_mimeData;
151 const QList<QUrl> m_urls;
152 QMap<QString, QString> m_metaData;
153 Qt::DropAction m_dropAction;
154 QPoint m_relativePos;
155 Qt::KeyboardModifiers m_keyboardModifiers;
156 bool m_hasArkFormat;
157 QString m_remoteArkDBusClient;
158 QString m_remoteArkDBusPath;
159 QUrl m_destUrl;
160 KFileItem m_destItem; // null for remote URLs not found in the dirlister cache
161 const JobFlags m_flags;
162 const DropJobFlags m_dropjobFlags;
163 QList<QAction *> m_appActions;
164 QList<QAction *> m_pluginActions;
165 bool m_triggered; // Tracks whether an action has been triggered in the popup menu.
166 QSet<KIO::DropMenu *> m_menus;
167
168 Q_DECLARE_PUBLIC(DropJob)
169
170 void slotStart();
171 void slotTriggered(QAction *);
172 void slotAboutToHide();
173
174 static inline DropJob *newJob(const QDropEvent *dropEvent, const QUrl &destUrl, DropJobFlags dropjobFlags, JobFlags flags)
175 {
176 DropJob *job = new DropJob(*new DropJobPrivate(dropEvent, destUrl, dropjobFlags, flags));
178 // Note: never KIO::getJobTracker()->registerJob here.
179 // We don't want a progress dialog during the copy/move/link popup, it would in fact close
180 // the popup
181 return job;
182 }
183};
184
185DropMenu::DropMenu(QWidget *parent)
186 : QMenu(parent)
187 , m_extraActionsSeparator(nullptr)
188{
189 m_cancelAction = new QAction(i18n("C&ancel") + QLatin1Char('\t') + QKeySequence(Qt::Key_Escape).toString(QKeySequence::NativeText), this);
190 m_cancelAction->setIcon(QIcon::fromTheme(QStringLiteral("process-stop")));
191
192 m_lastSeparator = new QAction(this);
193 m_lastSeparator->setSeparator(true);
194}
195
196DropMenu::~DropMenu()
197{
198}
199
200void DropMenu::addExtraActions(const QList<QAction *> &appActions, const QList<QAction *> &pluginActions)
201{
202 removeAction(m_lastSeparator);
203 removeAction(m_cancelAction);
204
205 removeAction(m_extraActionsSeparator);
206 for (QAction *action : std::as_const(m_appActions)) {
207 removeAction(action);
208 }
209 for (QAction *action : std::as_const(m_pluginActions)) {
210 removeAction(action);
211 }
212
213 m_appActions = appActions;
214 m_pluginActions = pluginActions;
215
216 if (!m_appActions.isEmpty() || !m_pluginActions.isEmpty()) {
217 QAction *firstExtraAction = m_appActions.value(0, m_pluginActions.value(0, nullptr));
218 if (firstExtraAction && !firstExtraAction->isSeparator()) {
219 if (!m_extraActionsSeparator) {
220 m_extraActionsSeparator = new QAction(this);
221 m_extraActionsSeparator->setSeparator(true);
222 }
223 addAction(m_extraActionsSeparator);
224 }
225 addActions(appActions);
226 addActions(pluginActions);
227 }
228
229 addAction(m_lastSeparator);
230 addAction(m_cancelAction);
231}
232
233DropJob::DropJob(DropJobPrivate &dd)
234 : Job(dd)
235{
236 Q_D(DropJob);
237
238 QTimer::singleShot(0, this, [d]() {
239 d->slotStart();
240 });
241}
242
243DropJob::~DropJob()
244{
245}
246
247void DropJobPrivate::slotStart()
248{
249 Q_Q(DropJob);
250
251#ifdef WITH_QTDBUS
252 if (m_hasArkFormat) {
253 QDBusMessage message = QDBusMessage::createMethodCall(m_remoteArkDBusClient,
254 m_remoteArkDBusPath,
255 QStringLiteral("org.kde.ark.DndExtract"),
256 QStringLiteral("extractSelectedFilesTo"));
258 const auto pending = QDBusConnection::sessionBus().asyncCall(message);
259 auto watcher = std::make_shared<QDBusPendingCallWatcher>(pending);
260 QObject::connect(watcher.get(), &QDBusPendingCallWatcher::finished, q, [this, watcher] {
261 Q_Q(DropJob);
262
263 if (watcher->isError()) {
264 q->setError(KIO::ERR_UNKNOWN);
265 }
266 q->emitResult();
267 });
268
269 return;
270 }
271#endif
272
273 if (!m_urls.isEmpty()) {
274 if (destIsDirectory()) {
275 handleCopyToDirectory();
276 } else { // local file
277 const QString destFile = m_destUrl.toLocalFile();
278 if (KDesktopFile::isDesktopFile(destFile)) {
279 handleDropToDesktopFile();
280 } else if (QFileInfo(destFile).isExecutable()) {
281 handleDropToExecutable();
282 } else {
283 // should not happen, if KDirModel::flags is correct
284 q->setError(KIO::ERR_ACCESS_DENIED);
285 q->emitResult();
286 }
287 }
288 } else if (m_mimeData) {
289 // Dropping raw data
290 KIO::PasteJob *job = KIO::PasteJobPrivate::newJob(m_mimeData, m_destUrl, KIO::HideProgressInfo, false /*not clipboard*/);
292 q->addSubjob(job);
293 }
294}
295
296void DropJobPrivate::fillPopupMenu(KIO::DropMenu *popup)
297{
298 Q_Q(DropJob);
299
300 // Check what the source can do
301 // TODO: Determining the MIME type of the source URLs is difficult for remote URLs,
302 // we would need to KIO::stat each URL in turn, asynchronously....
303 KFileItemList fileItems;
304 fileItems.reserve(m_urls.size());
305 for (const QUrl &url : m_urls) {
306 fileItems.append(KFileItem(url));
307 }
308 const bool allSourcesAreHttpUrls = std::ranges::all_of(m_urls, [](const auto &url) {
309 return url.scheme().startsWith(QStringLiteral("http"), Qt::CaseInsensitive);
310 });
311 const KFileItemListProperties itemProps(fileItems);
312
313 Q_EMIT q->popupMenuAboutToShow(itemProps);
314
315 const bool sReading = itemProps.supportsReading();
316 // For http URLs, even though technically the protocol supports deleting,
317 // this never makes sense for a drag operation.
318 const bool sDeleting = allSourcesAreHttpUrls ? false : itemProps.supportsDeleting();
319 const bool sMoving = itemProps.supportsMoving();
320
321 const int separatorLength = QCoreApplication::translate("QShortcut", "+").size();
323 seq.chop(separatorLength); // chop superfluous '+'
324 QAction *popupMoveAction = new QAction(i18n("&Move Here") + QLatin1Char('\t') + seq, popup);
325 popupMoveAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-move"), QIcon::fromTheme(QStringLiteral("go-jump"))));
326 popupMoveAction->setData(QVariant::fromValue(Qt::MoveAction));
328 seq.chop(separatorLength);
329
330 const QString copyActionName = allSourcesAreHttpUrls ? i18nc("@action:inmenu Download contents of URL here", "&Download Here") : i18n("&Copy Here");
331 const QIcon copyActionIcon = QIcon::fromTheme(allSourcesAreHttpUrls ? QStringLiteral("download") : QStringLiteral("edit-copy"));
332 QAction *popupCopyAction = new QAction(copyActionName + QLatin1Char('\t') + seq, popup);
333 popupCopyAction->setIcon(copyActionIcon);
334 popupCopyAction->setData(QVariant::fromValue(Qt::CopyAction));
336 seq.chop(separatorLength);
337 QAction *popupLinkAction = new QAction(i18n("&Link Here") + QLatin1Char('\t') + seq, popup);
338 popupLinkAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-link")));
339 popupLinkAction->setData(QVariant::fromValue(Qt::LinkAction));
340
341 if (sMoving || (sReading && sDeleting)) {
342 const bool equalDestination = std::all_of(m_urls.cbegin(), m_urls.cend(), [this](const QUrl &src) {
343 return m_destUrl.matches(src.adjusted(QUrl::RemoveFilename), QUrl::StripTrailingSlash);
344 });
345
346 if (!equalDestination) {
347 popup->addAction(popupMoveAction);
348 }
349 }
350
351 if (sReading) {
352 popup->addAction(popupCopyAction);
353 }
354
355 popup->addAction(popupLinkAction);
356
357 addPluginActions(popup, itemProps);
358}
359
360void DropJobPrivate::addPluginActions(KIO::DropMenu *popup, const KFileItemListProperties &itemProps)
361{
362 const QList<KPluginMetaData> plugin_offers = KPluginMetaData::findPlugins(QStringLiteral("kf6/kio_dnd"));
363 for (const KPluginMetaData &data : plugin_offers) {
364 if (auto plugin = KPluginFactory::instantiatePlugin<KIO::DndPopupMenuPlugin>(data).plugin) {
365 const auto actions = plugin->setup(itemProps, m_destUrl);
366 for (auto action : actions) {
367 action->setParent(popup);
368 }
369 m_pluginActions += actions;
370 }
371 }
372
373 popup->addExtraActions(m_appActions, m_pluginActions);
374}
375
377{
378 Q_D(DropJob);
379
380 d->m_appActions = actions;
381
382 for (KIO::DropMenu *menu : std::as_const(d->m_menus)) {
383 menu->addExtraActions(d->m_appActions, d->m_pluginActions);
384 }
385}
386
387void DropJob::showMenu(const QPoint &p, QAction *atAction)
388{
389 Q_D(DropJob);
390
391 if (!(d->m_dropjobFlags & KIO::ShowMenuManually)) {
392 return;
393 }
394
395 for (KIO::DropMenu *menu : std::as_const(d->m_menus)) {
396 if (QWindow *transientParent = d->transientParent()) {
397 if (menu->winId()) {
398 menu->windowHandle()->setTransientParent(transientParent);
399 }
400 }
401 menu->popup(p, atAction);
402 }
403}
404
405void DropJobPrivate::slotTriggered(QAction *action)
406{
407 Q_Q(DropJob);
408 if (m_appActions.contains(action) || m_pluginActions.contains(action)) {
409 q->emitResult();
410 return;
411 }
412 const QVariant data = action->data();
413 if (!data.canConvert<Qt::DropAction>()) {
414 q->setError(KIO::ERR_USER_CANCELED);
415 q->emitResult();
416 return;
417 }
418 m_dropAction = data.value<Qt::DropAction>();
419 doCopyToDirectory();
420}
421
422void DropJobPrivate::slotAboutToHide()
423{
424 Q_Q(DropJob);
425 // QMenu emits aboutToHide before triggered.
426 // So we need to give the menu time in case it needs to emit triggered.
427 // If it does, the cleanup will be done by slotTriggered.
428 QTimer::singleShot(0, q, [=, this]() {
429 if (!m_triggered) {
430 q->setError(KIO::ERR_USER_CANCELED);
431 q->emitResult();
432 }
433 });
434}
435
436void DropJobPrivate::handleCopyToDirectory()
437{
438 Q_Q(DropJob);
439
440 // Process m_dropAction as set by Qt at the time of the drop event
441 if (!KProtocolManager::supportsWriting(m_destUrl)) {
442 slotDropActionDetermined(KIO::ERR_CANNOT_WRITE);
443 return;
444 }
445
446 if (!m_destItem.isNull() && !m_destItem.isWritable() && (m_flags & KIO::NoPrivilegeExecution)) {
447 slotDropActionDetermined(KIO::ERR_WRITE_ACCESS_DENIED);
448 return;
449 }
450
451 bool allItemsAreFromTrash = true;
452 bool containsTrashRoot = false;
453 for (const QUrl &url : m_urls) {
454 const bool local = url.isLocalFile();
455 if (!local /*optimization*/ && url.scheme() == QLatin1String("trash")) {
456 if (url.path().isEmpty() || url.path() == QLatin1String("/")) {
457 containsTrashRoot = true;
458 }
459 } else {
460 allItemsAreFromTrash = false;
461 }
462 if (url.matches(m_destUrl, QUrl::StripTrailingSlash)) {
463 slotDropActionDetermined(KIO::ERR_DROP_ON_ITSELF);
464 return;
465 }
466 }
467
468 const bool trashing = m_destUrl.scheme() == QLatin1String("trash");
469 if (trashing) {
470 if (allItemsAreFromTrash) {
471 qCDebug(KIO_WIDGETS) << "Dropping items from trash to trash";
472 slotDropActionDetermined(KIO::ERR_DROP_ON_ITSELF);
473 return;
474 }
475 m_dropAction = Qt::MoveAction;
476
477 auto *askUserInterface = KIO::delegateExtension<AskUserActionInterface *>(q);
478
479 // No UI Delegate set for this job, or a delegate that doesn't implement
480 // AskUserActionInterface, then just proceed with the job without asking.
481 // This is useful for non-interactive usage, (which doesn't actually apply
482 // here as a DropJob is always interactive), but this is useful for unittests,
483 // which are typically non-interactive.
484 if (!askUserInterface) {
485 slotDropActionDetermined(KJob::NoError);
486 return;
487 }
488
489 QObject::connect(askUserInterface, &KIO::AskUserActionInterface::askUserDeleteResult, q, [this](bool allowDelete) {
490 if (allowDelete) {
491 slotDropActionDetermined(KJob::NoError);
492 } else {
493 slotDropActionDetermined(KIO::ERR_USER_CANCELED);
494 }
495 });
496
498 return;
499 }
500
501 // If we can't determine the action below, we use ERR::UNKNOWN as we need to ask
502 // the user via a popup menu.
503 int err = KIO::ERR_UNKNOWN;
504 const bool implicitCopy = m_destUrl.scheme() == QLatin1String("stash");
505 if (implicitCopy) {
506 m_dropAction = Qt::CopyAction;
507 err = KJob::NoError; // Ok
508 } else if (containsTrashRoot) {
509 // Dropping a link to the trash: don't move the full contents, just make a link (#319660)
510 m_dropAction = Qt::LinkAction;
511 err = KJob::NoError; // Ok
512 } else if (allItemsAreFromTrash) {
513 // No point in asking copy/move/link when using dragging from the trash, just move the file out.
514 m_dropAction = Qt::MoveAction;
515 err = KJob::NoError; // Ok
516 } else if (m_keyboardModifiers & (Qt::ControlModifier | Qt::ShiftModifier | Qt::AltModifier)) {
517 // Qt determined m_dropAction from the modifiers already
518 err = KJob::NoError; // Ok
519 }
520 slotDropActionDetermined(err);
521}
522
523QWindow *DropJobPrivate::transientParent()
524{
525 Q_Q(DropJob);
526
527 if (QWidget *widget = KJobWidgets::window(q)) {
528 QWidget *window = widget->window();
529 Q_ASSERT(window);
530 return window->windowHandle();
531 }
532
533 if (QWindow *window = KJobWindows::window(q)) {
534 return window;
535 }
536
537 return nullptr;
538}
539
540void DropJobPrivate::slotDropActionDetermined(int error)
541{
542 Q_Q(DropJob);
543
544 if (error == KJob::NoError) {
545 doCopyToDirectory();
546 return;
547 }
548
549 // There was an error, handle it
550 if (error == KIO::ERR_UNKNOWN) {
551 KIO::DropMenu *menu = new KIO::DropMenu();
553
554 // If the user clicks outside the menu, it will be destroyed without emitting the triggered signal.
555 QObject::connect(menu, &QMenu::aboutToHide, q, [this]() {
556 slotAboutToHide();
557 });
558
559 fillPopupMenu(menu);
560 QObject::connect(menu, &QMenu::triggered, q, [this](QAction *action) {
561 m_triggered = true;
562 slotTriggered(action);
563 });
564
565 if (!(m_dropjobFlags & KIO::ShowMenuManually)) {
566 if (QWindow *parent = transientParent()) {
567 if (menu->winId()) {
568 menu->windowHandle()->setTransientParent(parent);
569 }
570 }
571 auto *window = KJobWidgets::window(q);
572 menu->popup(window ? window->mapToGlobal(m_relativePos) : QCursor::pos());
573 }
574 m_menus.insert(menu);
575 QObject::connect(menu, &QObject::destroyed, q, [this, menu]() {
576 m_menus.remove(menu);
577 });
578 } else {
579 q->setError(error);
580 q->emitResult();
581 }
582}
583
584void DropJobPrivate::doCopyToDirectory()
585{
586 Q_Q(DropJob);
587 KIO::CopyJob *job = nullptr;
588 switch (m_dropAction) {
589 case Qt::MoveAction:
590 job = KIO::move(m_urls, m_destUrl, m_flags);
591 KIO::FileUndoManager::self()->recordJob(m_destUrl.scheme() == QLatin1String("trash") ? KIO::FileUndoManager::Trash : KIO::FileUndoManager::Move,
592 m_urls,
593 m_destUrl,
594 job);
595 break;
596 case Qt::CopyAction:
597 job = KIO::copy(m_urls, m_destUrl, m_flags);
599 break;
600 case Qt::LinkAction:
601 job = KIO::link(m_urls, m_destUrl, m_flags);
603 break;
604 default:
605 qCWarning(KIO_WIDGETS) << "Unknown drop action" << int(m_dropAction);
606 q->setError(KIO::ERR_UNSUPPORTED_ACTION);
607 q->emitResult();
608 return;
609 }
610 Q_ASSERT(job);
611 job->setParentJob(q);
612 job->setMetaData(m_metaData);
613 QObject::connect(job, &KIO::CopyJob::copyingDone, q, [q](KIO::Job *, const QUrl &, const QUrl &to) {
614 Q_EMIT q->itemCreated(to);
615 });
616 QObject::connect(job, &KIO::CopyJob::copyingLinkDone, q, [q](KIO::Job *, const QUrl &, const QString &, const QUrl &to) {
617 Q_EMIT q->itemCreated(to);
618 });
619 q->addSubjob(job);
620
621 Q_EMIT q->copyJobStarted(job);
622}
623
624void DropJobPrivate::handleDropToDesktopFile()
625{
626 Q_Q(DropJob);
627 const QString urlKey = QStringLiteral("URL");
628 const QString destFile = m_destUrl.toLocalFile();
629 const KDesktopFile desktopFile(destFile);
630 const KConfigGroup desktopGroup = desktopFile.desktopGroup();
631 if (desktopFile.hasApplicationType()) {
632 // Drop to application -> start app with urls as argument
633 KService::Ptr service(new KService(destFile));
634 // Can't use setParentJob() because ApplicationLauncherJob isn't a KIO::Job,
635 // instead pass q as parent so that KIO::delegateExtension() can find a delegate
637 job->setUrls(m_urls);
638 QObject::connect(job, &KJob::result, q, [=]() {
639 if (job->error()) {
640 q->setError(KIO::ERR_CANNOT_LAUNCH_PROCESS);
641 q->setErrorText(destFile);
642 }
643 q->emitResult();
644 });
645 job->start();
646 } else if (desktopFile.hasLinkType() && desktopGroup.hasKey(urlKey)) {
647 // Drop to link -> adjust destination directory
648 m_destUrl = QUrl::fromUserInput(desktopGroup.readPathEntry(urlKey, QString()));
649 handleCopyToDirectory();
650 } else {
651 if (desktopFile.hasDeviceType()) {
652 qCWarning(KIO_WIDGETS) << "Not re-implemented; please email kde-frameworks-devel@kde.org if you need this.";
653 // take code from libkonq's old konq_operations.cpp
654 // for now, fallback
655 }
656 // Some other kind of .desktop file (service, servicetype...)
657 q->setError(KIO::ERR_UNSUPPORTED_ACTION);
658 q->emitResult();
659 }
660}
661
662void DropJobPrivate::handleDropToExecutable()
663{
664 Q_Q(DropJob);
665 // Launch executable for each of the files
666 QStringList args;
667 args.reserve(m_urls.size());
668 for (const QUrl &url : std::as_const(m_urls)) {
669 args << url.toLocalFile(); // assume local files
670 }
671 QProcess::startDetached(m_destUrl.toLocalFile(), args);
672 q->emitResult();
673}
674
675void DropJob::slotResult(KJob *job)
676{
677 if (job->error()) {
678 KIO::Job::slotResult(job); // will set the error and emit result(this)
679 return;
680 }
681 removeSubjob(job);
682 emitResult();
683}
684
685DropJob *KIO::drop(const QDropEvent *dropEvent, const QUrl &destUrl, JobFlags flags)
686{
687 return DropJobPrivate::newJob(dropEvent, destUrl, KIO::DropJobDefaultFlags, flags);
688}
689
690DropJob *KIO::drop(const QDropEvent *dropEvent, const QUrl &destUrl, DropJobFlags dropjobFlags, JobFlags flags)
691{
692 return DropJobPrivate::newJob(dropEvent, destUrl, dropjobFlags, flags);
693}
694
695#include "dropjob.moc"
696#include "moc_dropjob.cpp"
virtual void slotResult(KJob *job)
bool hasKey(const char *key) const
QString readPathEntry(const char *key, const QString &aDefault) const
Helper class for the kiojob used to list and update a directory.
static bool isDesktopFile(const QString &path)
Provides information about the common properties of a group of KFileItem objects.
List of KFileItems, which adds a few helper methods to QList<KFileItem>.
Definition kfileitem.h:632
A KFileItem is a generic class to handle a file, local or remote.
Definition kfileitem.h:36
bool isNull() const
Return true if default-constructed.
ApplicationLauncherJob runs an application and watches it while running.
void start() override
Starts the job.
void setUrls(const QList< QUrl > &urls)
Specifies the URLs to be passed to the application.
void askUserDeleteResult(bool allowDelete, const QList< QUrl > &urls, KIO::AskUserActionInterface::DeletionType deletionType, QWidget *parent)
Implementations of this interface must emit this signal when the dialog invoked by askUserDelete() fi...
@ Trash
Delete the files/directories directly, i.e. without moving them to Trash.
@ DefaultConfirmation
Do not ask if the user has previously set the "Do not ask again" checkbox (which is is shown in the m...
CopyJob is used to move, copy or symlink files and directories.
void copyingLinkDone(KIO::Job *job, const QUrl &from, const QString &target, const QUrl &to)
The job is copying or moving a symbolic link, that points to target.
void copyingDone(KIO::Job *job, const QUrl &from, const QUrl &to, const QDateTime &mtime, bool directory, bool renamed)
The job emits this signal when copying or moving a file or directory successfully finished.
A KIO job that handles dropping into a file-manager-like view.
void setApplicationActions(const QList< QAction * > &actions)
Allows the application to set additional actions in the drop popup menu.
Definition dropjob.cpp:376
void itemCreated(const QUrl &url)
Signals that a file or directory was created.
void showMenu(const QPoint &p, QAction *atAction=nullptr)
Allows the application to show the menu manually.
Definition dropjob.cpp:387
void recordCopyJob(KIO::CopyJob *copyJob)
Record this CopyJob while it's happening and add a command for it so that the user can undo it.
static FileUndoManager * self()
void recordJob(CommandType op, const QList< QUrl > &src, const QUrl &dst, KIO::Job *job)
Record this job while it's happening and add a command for it so that the user can undo it.
The base class for all jobs.
void setMetaData(const KIO::MetaData &metaData)
Set meta data to be sent to the worker, replacing existing meta data.
Definition job.cpp:215
void setParentJob(Job *parentJob)
Set the parent Job.
Definition job.cpp:192
bool removeSubjob(KJob *job) override
Mark a sub job as being done.
Definition job.cpp:80
A KIO job that handles pasting the clipboard contents.
void itemCreated(const QUrl &url)
Signals that a file or directory was created.
void emitResult()
int error() const
void result(KJob *job)
void setUiDelegate(KJobUiDelegate *delegate)
static QList< KPluginMetaData > findPlugins(const QString &directory, std::function< bool(const KPluginMetaData &)> filter={}, KPluginMetaDataOptions options={})
static bool supportsWriting(const QUrl &url)
Returns whether the protocol can store data to URLs.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
A namespace for KIO globals.
KIOCORE_EXPORT CopyJob * move(const QUrl &src, const QUrl &dest, JobFlags flags=DefaultFlags)
Moves a file or directory src to the given destination dest.
Definition copyjob.cpp:2657
KIOCORE_EXPORT CopyJob * link(const QUrl &src, const QUrl &destDir, JobFlags flags=DefaultFlags)
Create a link.
Definition copyjob.cpp:2691
@ ShowMenuManually
show the menu manually with DropJob::showMenu
Definition dropjob.h:30
T delegateExtension(KJob *job)
Returns the child of the job's uiDelegate() that implements the given extension, or nullptr if none w...
KIOCORE_EXPORT CopyJob * copy(const QUrl &src, const QUrl &dest, JobFlags flags=DefaultFlags)
Copy a file or directory src into the destination dest, which can be a file (including the final file...
Definition copyjob.cpp:2635
KIOCORE_EXPORT KJobUiDelegate * createDefaultJobUiDelegate()
Convenience method: use default factory, if there's one, to create a delegate and return it.
KIOWIDGETS_EXPORT DropJob * drop(const QDropEvent *dropEvent, const QUrl &destUrl, JobFlags flags=DefaultFlags)
Drops the clipboard contents.
Definition dropjob.cpp:685
@ HideProgressInfo
Hide progress information dialog, i.e. don't show a GUI.
Definition job_base.h:251
@ NoPrivilegeExecution
When set, notifies the worker that application/job does not want privilege execution.
Definition job_base.h:276
@ ERR_DROP_ON_ITSELF
from KIO::DropJob,
Definition global.h:194
QWidget * window(QObject *job)
KGUIADDONS_EXPORT QWindow * window(QObject *job)
KCOREADDONS_EXPORT QList< QUrl > urlsFromMimeData(const QMimeData *mimeData, DecodeOptions decodeOptions=PreferKdeUrls, MetaDataMap *metaData=nullptr)
QVariant data() const const
void setIcon(const QIcon &icon)
bool isSeparator() const const
void setData(const QVariant &data)
void setSeparator(bool b)
QString translate(const char *context, const char *sourceText, const char *disambiguation, int n)
QPoint pos()
QDBusPendingCall asyncCall(const QDBusMessage &message, int timeout) const const
QDBusConnection sessionBus()
QDBusMessage createMethodCall(const QString &service, const QString &path, const QString &interface, const QString &method)
void setArguments(const QList< QVariant > &arguments)
void finished(QDBusPendingCallWatcher *self)
QIcon fromTheme(const QString &name)
QString toString(SequenceFormat format) const const
void append(QList< T > &&value)
bool contains(const AT &value) const const
bool isEmpty() const const
void reserve(qsizetype size)
qsizetype size() const const
T value(qsizetype i) const const
QAction * addAction(const QIcon &icon, const QString &text, Functor functor, const QKeySequence &shortcut)
void aboutToHide()
void popup(const QPoint &p, QAction *atAction)
void triggered(QAction *action)
Q_OBJECTQ_OBJECT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
void destroyed(QObject *obj)
QObject * parent() const const
bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid)
iterator insert(const T &value)
bool remove(const T &value)
void chop(qsizetype n)
QString fromUtf8(QByteArrayView str)
qsizetype size() const const
CaseInsensitive
DropAction
Key_Escape
typedef KeyboardModifiers
PreferLocalFile
QUrl fromUserInput(const QString &userInput, const QString &workingDirectory, UserInputResolutionOptions options)
bool isLocalFile() const const
QString scheme() const const
QString toDisplayString(FormattingOptions options) const const
QString toLocalFile() const const
bool canConvert() const const
QVariant fromValue(T &&value)
T value() const const
void addActions(const QList< QAction * > &actions)
void removeAction(QAction *action)
WId winId() const const
QWindow * windowHandle() const const
QPoint mapToGlobal(const QPoint &pos) const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 16:58:45 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.