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 KFileItemListProperties itemProps(fileItems);
309
310 Q_EMIT q->popupMenuAboutToShow(itemProps);
311
312 const bool sReading = itemProps.supportsReading();
313 const bool sDeleting = itemProps.supportsDeleting();
314 const bool sMoving = itemProps.supportsMoving();
315
316 const int separatorLength = QCoreApplication::translate("QShortcut", "+").size();
318 seq.chop(separatorLength); // chop superfluous '+'
319 QAction *popupMoveAction = new QAction(i18n("&Move Here") + QLatin1Char('\t') + seq, popup);
320 popupMoveAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-move"), QIcon::fromTheme(QStringLiteral("go-jump"))));
321 popupMoveAction->setData(QVariant::fromValue(Qt::MoveAction));
323 seq.chop(separatorLength);
324 QAction *popupCopyAction = new QAction(i18n("&Copy Here") + QLatin1Char('\t') + seq, popup);
325 popupCopyAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
326 popupCopyAction->setData(QVariant::fromValue(Qt::CopyAction));
328 seq.chop(separatorLength);
329 QAction *popupLinkAction = new QAction(i18n("&Link Here") + QLatin1Char('\t') + seq, popup);
330 popupLinkAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-link")));
331 popupLinkAction->setData(QVariant::fromValue(Qt::LinkAction));
332
333 if (sMoving || (sReading && sDeleting)) {
334 const bool equalDestination = std::all_of(m_urls.cbegin(), m_urls.cend(), [this](const QUrl &src) {
335 return m_destUrl.matches(src.adjusted(QUrl::RemoveFilename), QUrl::StripTrailingSlash);
336 });
337
338 if (!equalDestination) {
339 popup->addAction(popupMoveAction);
340 }
341 }
342
343 if (sReading) {
344 popup->addAction(popupCopyAction);
345 }
346
347 popup->addAction(popupLinkAction);
348
349 addPluginActions(popup, itemProps);
350}
351
352void DropJobPrivate::addPluginActions(KIO::DropMenu *popup, const KFileItemListProperties &itemProps)
353{
354 const QList<KPluginMetaData> plugin_offers = KPluginMetaData::findPlugins(QStringLiteral("kf6/kio_dnd"));
355 for (const KPluginMetaData &data : plugin_offers) {
356 if (auto plugin = KPluginFactory::instantiatePlugin<KIO::DndPopupMenuPlugin>(data).plugin) {
357 const auto actions = plugin->setup(itemProps, m_destUrl);
358 for (auto action : actions) {
359 action->setParent(popup);
360 }
361 m_pluginActions += actions;
362 }
363 }
364
365 popup->addExtraActions(m_appActions, m_pluginActions);
366}
367
369{
370 Q_D(DropJob);
371
372 d->m_appActions = actions;
373
374 for (KIO::DropMenu *menu : std::as_const(d->m_menus)) {
375 menu->addExtraActions(d->m_appActions, d->m_pluginActions);
376 }
377}
378
379void DropJob::showMenu(const QPoint &p, QAction *atAction)
380{
381 Q_D(DropJob);
382
383 if (!(d->m_dropjobFlags & KIO::ShowMenuManually)) {
384 return;
385 }
386
387 for (KIO::DropMenu *menu : std::as_const(d->m_menus)) {
388 if (QWindow *transientParent = d->transientParent()) {
389 if (menu->winId()) {
390 menu->windowHandle()->setTransientParent(transientParent);
391 }
392 }
393 menu->popup(p, atAction);
394 }
395}
396
397void DropJobPrivate::slotTriggered(QAction *action)
398{
399 Q_Q(DropJob);
400 if (m_appActions.contains(action) || m_pluginActions.contains(action)) {
401 q->emitResult();
402 return;
403 }
404 const QVariant data = action->data();
405 if (!data.canConvert<Qt::DropAction>()) {
406 q->setError(KIO::ERR_USER_CANCELED);
407 q->emitResult();
408 return;
409 }
410 m_dropAction = data.value<Qt::DropAction>();
411 doCopyToDirectory();
412}
413
414void DropJobPrivate::slotAboutToHide()
415{
416 Q_Q(DropJob);
417 // QMenu emits aboutToHide before triggered.
418 // So we need to give the menu time in case it needs to emit triggered.
419 // If it does, the cleanup will be done by slotTriggered.
420 QTimer::singleShot(0, q, [=, this]() {
421 if (!m_triggered) {
422 q->setError(KIO::ERR_USER_CANCELED);
423 q->emitResult();
424 }
425 });
426}
427
428void DropJobPrivate::handleCopyToDirectory()
429{
430 Q_Q(DropJob);
431
432 // Process m_dropAction as set by Qt at the time of the drop event
433 if (!KProtocolManager::supportsWriting(m_destUrl)) {
434 slotDropActionDetermined(KIO::ERR_CANNOT_WRITE);
435 return;
436 }
437
438 if (!m_destItem.isNull() && !m_destItem.isWritable() && (m_flags & KIO::NoPrivilegeExecution)) {
439 slotDropActionDetermined(KIO::ERR_WRITE_ACCESS_DENIED);
440 return;
441 }
442
443 bool allItemsAreFromTrash = true;
444 bool containsTrashRoot = false;
445 for (const QUrl &url : m_urls) {
446 const bool local = url.isLocalFile();
447 if (!local /*optimization*/ && url.scheme() == QLatin1String("trash")) {
448 if (url.path().isEmpty() || url.path() == QLatin1String("/")) {
449 containsTrashRoot = true;
450 }
451 } else {
452 allItemsAreFromTrash = false;
453 }
454 if (url.matches(m_destUrl, QUrl::StripTrailingSlash)) {
455 slotDropActionDetermined(KIO::ERR_DROP_ON_ITSELF);
456 return;
457 }
458 }
459
460 const bool trashing = m_destUrl.scheme() == QLatin1String("trash");
461 if (trashing) {
462 if (allItemsAreFromTrash) {
463 qCDebug(KIO_WIDGETS) << "Dropping items from trash to trash";
464 slotDropActionDetermined(KIO::ERR_DROP_ON_ITSELF);
465 return;
466 }
467 m_dropAction = Qt::MoveAction;
468
469 auto *askUserInterface = KIO::delegateExtension<AskUserActionInterface *>(q);
470
471 // No UI Delegate set for this job, or a delegate that doesn't implement
472 // AskUserActionInterface, then just proceed with the job without asking.
473 // This is useful for non-interactive usage, (which doesn't actually apply
474 // here as a DropJob is always interactive), but this is useful for unittests,
475 // which are typically non-interactive.
476 if (!askUserInterface) {
477 slotDropActionDetermined(KJob::NoError);
478 return;
479 }
480
481 QObject::connect(askUserInterface, &KIO::AskUserActionInterface::askUserDeleteResult, q, [this](bool allowDelete) {
482 if (allowDelete) {
483 slotDropActionDetermined(KJob::NoError);
484 } else {
485 slotDropActionDetermined(KIO::ERR_USER_CANCELED);
486 }
487 });
488
490 return;
491 }
492
493 // If we can't determine the action below, we use ERR::UNKNOWN as we need to ask
494 // the user via a popup menu.
495 int err = KIO::ERR_UNKNOWN;
496 const bool implicitCopy = m_destUrl.scheme() == QLatin1String("stash");
497 if (implicitCopy) {
498 m_dropAction = Qt::CopyAction;
499 err = KJob::NoError; // Ok
500 } else if (containsTrashRoot) {
501 // Dropping a link to the trash: don't move the full contents, just make a link (#319660)
502 m_dropAction = Qt::LinkAction;
503 err = KJob::NoError; // Ok
504 } else if (allItemsAreFromTrash) {
505 // No point in asking copy/move/link when using dragging from the trash, just move the file out.
506 m_dropAction = Qt::MoveAction;
507 err = KJob::NoError; // Ok
508 } else if (m_keyboardModifiers & (Qt::ControlModifier | Qt::ShiftModifier | Qt::AltModifier)) {
509 // Qt determined m_dropAction from the modifiers already
510 err = KJob::NoError; // Ok
511 }
512 slotDropActionDetermined(err);
513}
514
515QWindow *DropJobPrivate::transientParent()
516{
517 Q_Q(DropJob);
518
519 if (QWidget *widget = KJobWidgets::window(q)) {
520 QWidget *window = widget->window();
521 Q_ASSERT(window);
522 return window->windowHandle();
523 }
524
525 if (QWindow *window = KJobWindows::window(q)) {
526 return window;
527 }
528
529 return nullptr;
530}
531
532void DropJobPrivate::slotDropActionDetermined(int error)
533{
534 Q_Q(DropJob);
535
536 if (error == KJob::NoError) {
537 doCopyToDirectory();
538 return;
539 }
540
541 // There was an error, handle it
542 if (error == KIO::ERR_UNKNOWN) {
543 KIO::DropMenu *menu = new KIO::DropMenu();
545
546 // If the user clicks outside the menu, it will be destroyed without emitting the triggered signal.
547 QObject::connect(menu, &QMenu::aboutToHide, q, [this]() {
548 slotAboutToHide();
549 });
550
551 fillPopupMenu(menu);
552 QObject::connect(menu, &QMenu::triggered, q, [this](QAction *action) {
553 m_triggered = true;
554 slotTriggered(action);
555 });
556
557 if (!(m_dropjobFlags & KIO::ShowMenuManually)) {
558 if (QWindow *parent = transientParent()) {
559 if (menu->winId()) {
560 menu->windowHandle()->setTransientParent(parent);
561 }
562 }
563 auto *window = KJobWidgets::window(q);
564 menu->popup(window ? window->mapToGlobal(m_relativePos) : QCursor::pos());
565 }
566 m_menus.insert(menu);
567 QObject::connect(menu, &QObject::destroyed, q, [this, menu]() {
568 m_menus.remove(menu);
569 });
570 } else {
571 q->setError(error);
572 q->emitResult();
573 }
574}
575
576void DropJobPrivate::doCopyToDirectory()
577{
578 Q_Q(DropJob);
579 KIO::CopyJob *job = nullptr;
580 switch (m_dropAction) {
581 case Qt::MoveAction:
582 job = KIO::move(m_urls, m_destUrl, m_flags);
583 KIO::FileUndoManager::self()->recordJob(m_destUrl.scheme() == QLatin1String("trash") ? KIO::FileUndoManager::Trash : KIO::FileUndoManager::Move,
584 m_urls,
585 m_destUrl,
586 job);
587 break;
588 case Qt::CopyAction:
589 job = KIO::copy(m_urls, m_destUrl, m_flags);
591 break;
592 case Qt::LinkAction:
593 job = KIO::link(m_urls, m_destUrl, m_flags);
595 break;
596 default:
597 qCWarning(KIO_WIDGETS) << "Unknown drop action" << int(m_dropAction);
598 q->setError(KIO::ERR_UNSUPPORTED_ACTION);
599 q->emitResult();
600 return;
601 }
602 Q_ASSERT(job);
603 job->setParentJob(q);
604 job->setMetaData(m_metaData);
605 QObject::connect(job, &KIO::CopyJob::copyingDone, q, [q](KIO::Job *, const QUrl &, const QUrl &to) {
606 Q_EMIT q->itemCreated(to);
607 });
608 QObject::connect(job, &KIO::CopyJob::copyingLinkDone, q, [q](KIO::Job *, const QUrl &, const QString &, const QUrl &to) {
609 Q_EMIT q->itemCreated(to);
610 });
611 q->addSubjob(job);
612
613 Q_EMIT q->copyJobStarted(job);
614}
615
616void DropJobPrivate::handleDropToDesktopFile()
617{
618 Q_Q(DropJob);
619 const QString urlKey = QStringLiteral("URL");
620 const QString destFile = m_destUrl.toLocalFile();
621 const KDesktopFile desktopFile(destFile);
622 const KConfigGroup desktopGroup = desktopFile.desktopGroup();
623 if (desktopFile.hasApplicationType()) {
624 // Drop to application -> start app with urls as argument
625 KService::Ptr service(new KService(destFile));
626 // Can't use setParentJob() because ApplicationLauncherJob isn't a KIO::Job,
627 // instead pass q as parent so that KIO::delegateExtension() can find a delegate
629 job->setUrls(m_urls);
630 QObject::connect(job, &KJob::result, q, [=]() {
631 if (job->error()) {
632 q->setError(KIO::ERR_CANNOT_LAUNCH_PROCESS);
633 q->setErrorText(destFile);
634 }
635 q->emitResult();
636 });
637 job->start();
638 } else if (desktopFile.hasLinkType() && desktopGroup.hasKey(urlKey)) {
639 // Drop to link -> adjust destination directory
640 m_destUrl = QUrl::fromUserInput(desktopGroup.readPathEntry(urlKey, QString()));
641 handleCopyToDirectory();
642 } else {
643 if (desktopFile.hasDeviceType()) {
644 qCWarning(KIO_WIDGETS) << "Not re-implemented; please email kde-frameworks-devel@kde.org if you need this.";
645 // take code from libkonq's old konq_operations.cpp
646 // for now, fallback
647 }
648 // Some other kind of .desktop file (service, servicetype...)
649 q->setError(KIO::ERR_UNSUPPORTED_ACTION);
650 q->emitResult();
651 }
652}
653
654void DropJobPrivate::handleDropToExecutable()
655{
656 Q_Q(DropJob);
657 // Launch executable for each of the files
658 QStringList args;
659 args.reserve(m_urls.size());
660 for (const QUrl &url : std::as_const(m_urls)) {
661 args << url.toLocalFile(); // assume local files
662 }
663 QProcess::startDetached(m_destUrl.toLocalFile(), args);
664 q->emitResult();
665}
666
667void DropJob::slotResult(KJob *job)
668{
669 if (job->error()) {
670 KIO::Job::slotResult(job); // will set the error and emit result(this)
671 return;
672 }
673 removeSubjob(job);
674 emitResult();
675}
676
677DropJob *KIO::drop(const QDropEvent *dropEvent, const QUrl &destUrl, JobFlags flags)
678{
679 return DropJobPrivate::newJob(dropEvent, destUrl, KIO::DropJobDefaultFlags, flags);
680}
681
682DropJob *KIO::drop(const QDropEvent *dropEvent, const QUrl &destUrl, DropJobFlags dropjobFlags, JobFlags flags)
683{
684 return DropJobPrivate::newJob(dropEvent, destUrl, dropjobFlags, flags);
685}
686
687#include "dropjob.moc"
688#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:630
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:368
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:379
FileUndoManager: makes it possible to undo kio jobs.
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 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:2626
KIOCORE_EXPORT CopyJob * link(const QUrl &src, const QUrl &destDir, JobFlags flags=DefaultFlags)
Create a link.
Definition copyjob.cpp:2660
@ 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:2604
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:677
@ 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)
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)
const_iterator cbegin() const const
const_iterator cend() const const
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
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)
QPoint mapToGlobal(const QPoint &pos) const const
void removeAction(QAction *action)
WId winId() const const
QWidget * window() const const
QWindow * windowHandle() const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:54:08 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.