KIO

jobuidelegate.cpp
1 /*
2  This file is part of the KDE libraries
3  SPDX-FileCopyrightText: 2000 Stephan Kulow <[email protected]>
4  SPDX-FileCopyrightText: 2000 David Faure <[email protected]>
5  SPDX-FileCopyrightText: 2006 Kevin Ottens <[email protected]>
6  SPDX-FileCopyrightText: 2013 Dawit Alemayehu <[email protected]>
7 
8  SPDX-License-Identifier: LGPL-2.0-or-later
9 */
10 
11 #include "jobuidelegate.h"
12 #include <kio/jobuidelegatefactory.h>
13 #include "kio_widgets_debug.h"
14 #include "kiogui_export.h"
15 #include "widgetsuntrustedprogramhandler.h"
16 #include "widgetsopenwithhandler.h"
17 #include "widgetsopenorexecutefilehandler.h"
18 #include "widgetsaskuseractionhandler.h"
19 
20 #include <KConfigGroup>
21 #include <KJob>
22 #include <KJobWidgets>
23 #include <KLocalizedString>
24 #include <KMessageBox>
25 #include <KSharedConfig>
26 #include <ksslinfodialog.h>
27 #include <clipboardupdater_p.h>
28 
29 #include <QDBusInterface>
30 #include <QGuiApplication>
31 #include <QPointer>
32 #include <QWidget>
33 #include <QIcon>
34 #include <QRegularExpression>
35 #include <QUrl>
36 
37 #include "kio/scheduler.h"
38 
39 class KIO::JobUiDelegatePrivate
40 {
41 public:
42  JobUiDelegatePrivate(KIO::JobUiDelegate *q) {
43  // Create extension objects. See KIO::delegateExtension<T>().
44  new WidgetsUntrustedProgramHandler(q);
45  new WidgetsOpenWithHandler(q);
46  new WidgetsOpenOrExecuteFileHandler(q);
47  new WidgetsAskUserActionHandler(q);
48  }
49 };
50 
52  : d(new JobUiDelegatePrivate(this))
53 {
54 }
55 
57 
58 /*
59  Returns the top most window associated with widget.
60 
61  Unlike QWidget::window(), this function does its best to find and return the
62  main application window associated with the given widget.
63 
64  If widget itself is a dialog or its parent is a dialog, and that dialog has a
65  parent widget then this function will iterate through all those widgets to
66  find the top most window, which most of the time is the main window of the
67  application. By contrast, QWidget::window() would simply return the first
68  file dialog it encountered since it is the "next ancestor widget that has (or
69  could have) a window-system frame".
70 */
71 static QWidget *topLevelWindow(QWidget *widget)
72 {
73  QWidget *w = widget;
74  while (w && w->parentWidget()) {
75  w = w->parentWidget();
76  }
77  return (w ? w->window() : nullptr);
78 }
79 
80 class JobUiDelegateStatic : public QObject
81 {
82  Q_OBJECT
83 public:
84  void registerWindow(QWidget *wid)
85  {
86  if (!wid) {
87  return;
88  }
89 
90  QWidget *window = topLevelWindow(wid);
91  QObject *obj = static_cast<QObject *>(window);
92  if (!m_windowList.contains(obj)) {
93  // We must store the window Id because by the time
94  // the destroyed signal is emitted we can no longer
95  // access QWidget::winId() (already destructed)
96  WId windowId = window->winId();
97  m_windowList.insert(obj, windowId);
98  connect(window, &QObject::destroyed,
99  this, &JobUiDelegateStatic::slotUnregisterWindow);
100  QDBusInterface(QStringLiteral("org.kde.kded5"), QStringLiteral("/kded"), QStringLiteral("org.kde.kded5")).
101  call(QDBus::NoBlock, QStringLiteral("registerWindowId"), qlonglong(windowId));
102  }
103  }
104 public Q_SLOTS:
105  void slotUnregisterWindow(QObject *obj)
106  {
107  if (!obj) {
108  return;
109  }
110 
111  QMap<QObject *, WId>::Iterator it = m_windowList.find(obj);
112  if (it == m_windowList.end()) {
113  return;
114  }
115  WId windowId = it.value();
117  this, &JobUiDelegateStatic::slotUnregisterWindow);
118  m_windowList.erase(it);
119  QDBusInterface(QStringLiteral("org.kde.kded5"), QStringLiteral("/kded"), QStringLiteral("org.kde.kded5")).
120  call(QDBus::NoBlock, QStringLiteral("unregisterWindowId"), qlonglong(windowId));
121  }
122 private:
123  QMap<QObject *, WId> m_windowList;
124 };
125 
126 Q_GLOBAL_STATIC(JobUiDelegateStatic, s_static)
127 
129  : KDialogJobUiDelegate(flags, window), d(new JobUiDelegatePrivate(this))
130 {
131  s_static()->registerWindow(window);
132 }
133 
135 {
137  s_static()->registerWindow(window);
138 }
139 
141 {
142  s_static()->slotUnregisterWindow(window);
143 }
144 
146  const QString &caption,
147  const QUrl &src,
148  const QUrl &dest,
150  QString &newDest,
151  KIO::filesize_t sizeSrc,
152  KIO::filesize_t sizeDest,
153  const QDateTime &ctimeSrc,
154  const QDateTime &ctimeDest,
155  const QDateTime &mtimeSrc,
156  const QDateTime &mtimeDest)
157 {
158  //qDebug() << "job=" << job;
159  // We now do it in process, so that opening the rename dialog
160  // doesn't start uiserver for nothing if progressId=0 (e.g. F2 in konq)
161  KIO::RenameDialog dlg(KJobWidgets::window(job), caption, src, dest, options,
162  sizeSrc, sizeDest,
163  ctimeSrc, ctimeDest, mtimeSrc,
164  mtimeDest);
166  connect(job, &KJob::finished, &dlg, &QDialog::reject); // #192976
167  KIO::RenameDialog_Result res = static_cast<RenameDialog_Result>(dlg.exec());
168  if (res == Result_AutoRename) {
169  newDest = dlg.autoDestUrl().path();
170  } else {
171  newDest = dlg.newDestUrl().path();
172  }
173  return res;
174 }
175 
177  KIO::SkipDialog_Options options,
178  const QString &error_text)
179 {
180  KIO::SkipDialog dlg(KJobWidgets::window(job), options, error_text);
182  connect(job, &KJob::finished, &dlg, &QDialog::reject); // #192976
183  return static_cast<KIO::SkipDialog_Result>(dlg.exec());
184 }
185 
187  DeletionType deletionType,
188  ConfirmationType confirmationType)
189 {
190  QString keyName;
191  bool ask = (confirmationType == ForceConfirmation);
192  if (!ask) {
193  KSharedConfigPtr kioConfig = KSharedConfig::openConfig(QStringLiteral("kiorc"), KConfig::NoGlobals);
194 
195  // The default value for confirmations is true for delete and false
196  // for trash. If you change this, please also update:
197  // dolphin/src/settings/general/confirmationssettingspage.cpp
198  bool defaultValue = true;
199 
200  switch (deletionType) {
201  case Delete:
202  keyName = QStringLiteral("ConfirmDelete");
203  break;
204  case Trash:
205  keyName = QStringLiteral("ConfirmTrash");
206  defaultValue = false;
207  break;
208  case EmptyTrash:
209  keyName = QStringLiteral("ConfirmEmptyTrash");
210  break;
211  }
212 
213  ask = kioConfig->group("Confirmations").readEntry(keyName, defaultValue);
214  }
215  if (ask) {
216  QStringList prettyList;
217  prettyList.reserve(urls.size());
218  for (const QUrl &url : urls) {
219  if (url.scheme() == QLatin1String("trash")) {
220  QString path = url.path();
221  // HACK (#98983): remove "0-foo". Note that it works better than
222  // displaying KFileItem::name(), for files under a subdir.
223  path.remove(QRegularExpression(QStringLiteral("^/[0-9]*-")));
224  prettyList.append(path);
225  } else {
226  prettyList.append(url.toDisplayString(QUrl::PreferLocalFile));
227  }
228  }
229 
230  int result;
231  QWidget *widget = window();
233  switch (deletionType) {
234  case Delete:
235  if (prettyList.count() == 1) {
237  widget,
238  xi18nc("@info", "Do you really want to permanently delete this item?<nl/><filename>%1</filename><nl/><nl/><emphasis strong='true'>This action cannot be undone.</emphasis>", prettyList.first()),
239  i18n("Delete Permanently"),
242  keyName, options);
243  } else {
245  widget,
246  xi18ncp("@info", "Do you really want to permanently delete this item?<nl/><nl/><emphasis strong='true'>This action cannot be undone.</emphasis>", "Do you really want to permanently delete these %1 items?<nl/><nl/><emphasis strong='true'>This action cannot be undone.</emphasis>", prettyList.count()),
247  prettyList,
248  i18n("Delete Permanently"),
251  keyName, options);
252  }
253  break;
254  case EmptyTrash:
256  widget,
257  xi18nc("@info", "Do you want to permanently delete all items from the Trash?<nl/><nl/><emphasis strong='true'>This action cannot be undone.</emphasis>"),
258  i18n("Delete Permanently"),
259  KGuiItem(i18nc("@action:button", "Empty Trash"),
260  QIcon::fromTheme(QStringLiteral("user-trash"))),
262  keyName, options);
263  break;
264  case Trash:
265  default:
266  if (prettyList.count() == 1) {
268  widget,
269  xi18nc("@info", "Do you really want to move this item to the Trash?<nl/><filename>%1</filename>", prettyList.first()),
270  i18n("Move to Trash"),
271  KGuiItem(i18n("Move to Trash"), QStringLiteral("user-trash")),
273  keyName, options);
274  } else {
276  widget,
277  i18np("Do you really want to move this item to the Trash?", "Do you really want to move these %1 items to the Trash?", prettyList.count()),
278  prettyList,
279  i18n("Move to Trash"),
280  KGuiItem(i18n("Move to Trash"), QStringLiteral("user-trash")),
282  keyName, options);
283  }
284  }
285  if (!keyName.isEmpty()) {
286  // Check kmessagebox setting... erase & copy to konquerorrc.
287  KSharedConfig::Ptr config = KSharedConfig::openConfig();
288  KConfigGroup notificationGroup(config, "Notification Messages");
289  if (!notificationGroup.readEntry(keyName, true)) {
290  notificationGroup.writeEntry(keyName, true);
291  notificationGroup.sync();
292 
293  KSharedConfigPtr kioConfig = KSharedConfig::openConfig(QStringLiteral("kiorc"), KConfig::NoGlobals);
294  kioConfig->group("Confirmations").writeEntry(keyName, false);
295  }
296  }
297  return (result == KMessageBox::Continue);
298  }
299  return true;
300 }
301 
303  const QString &text, const QString &caption,
304  const QString &buttonYes, const QString &buttonNo,
305  const QString &iconYes, const QString &iconNo,
306  const QString &dontAskAgainName,
307  const KIO::MetaData &metaData)
308 {
309  int result = -1;
310 
311  //qDebug() << type << text << "caption=" << caption;
312 
313  KConfig config(QStringLiteral("kioslaverc"));
315 
316  const KGuiItem buttonYesGui(buttonYes, iconYes);
317  const KGuiItem buttonNoGui(buttonNo, iconNo);
319 
320  switch (type) {
321  case QuestionYesNo:
323  window(), text, caption, buttonYesGui,
324  buttonNoGui, dontAskAgainName, options);
325  break;
326  case WarningYesNo:
327  result = KMessageBox::warningYesNo(
328  window(), text, caption, buttonYesGui,
329  buttonNoGui, dontAskAgainName,
330  options | KMessageBox::Dangerous);
331  break;
332  case WarningYesNoCancel:
334  window(), text, caption, buttonYesGui, buttonNoGui,
335  KStandardGuiItem::cancel(), dontAskAgainName, options);
336  break;
337  case WarningContinueCancel:
339  window(), text, caption, buttonYesGui,
340  KStandardGuiItem::cancel(), dontAskAgainName, options);
341  break;
342  case Information:
343  KMessageBox::information(window(), text, caption, dontAskAgainName, options);
344  result = 1; // whatever
345  break;
346  case SSLMessageBox: {
348  //### this is boilerplate code and appears in khtml_part.cpp almost unchanged!
349  const QStringList sl = metaData.value(QStringLiteral("ssl_peer_chain")).split(QLatin1Char('\x01'), Qt::SkipEmptyParts);
350  QList<QSslCertificate> certChain;
351  bool decodedOk = true;
352  for (const QString &s : sl) {
353  certChain.append(QSslCertificate(s.toLatin1())); //or is it toLocal8Bit or whatever?
354  if (certChain.last().isNull()) {
355  decodedOk = false;
356  break;
357  }
358  }
359 
360  if (decodedOk) {
361  result = 1; // whatever
362  kid->setSslInfo(certChain,
363  metaData.value(QStringLiteral("ssl_peer_ip")),
364  text, // the URL
365  metaData.value(QStringLiteral("ssl_protocol_version")),
366  metaData.value(QStringLiteral("ssl_cipher")),
367  metaData.value(QStringLiteral("ssl_cipher_used_bits")).toInt(),
368  metaData.value(QStringLiteral("ssl_cipher_bits")).toInt(),
369  KSslInfoDialog::certificateErrorsFromString(metaData.value(QStringLiteral("ssl_cert_errors"))));
370  kid->exec();
371  } else {
372  result = -1;
374  i18n("The peer SSL certificate chain appears to be corrupt."),
375  i18n("SSL"), QString(), options);
376  }
377  // KSslInfoDialog deletes itself (Qt::WA_DeleteOnClose).
378  delete kid;
379  break;
380  }
381  case WarningContinueCancelDetailed: {
382  const QString details = metaData.value(QStringLiteral("privilege_conf_details"));
385  dontAskAgainName, options | KMessageBox::Dangerous, details);
386  break;
387  }
388  default:
389  qCWarning(KIO_WIDGETS) << "Unknown type" << type;
390  result = 0;
391  break;
392  }
394  return result;
395 }
396 
397 KIO::ClipboardUpdater *KIO::JobUiDelegate::createClipboardUpdater(Job *job, ClipboardUpdaterMode mode)
398 {
399  if (qobject_cast<QGuiApplication *>(qApp)) {
400  return new KIO::ClipboardUpdater(job, mode);
401  }
402  return nullptr;
403 }
404 
406 {
407  if (qobject_cast<QGuiApplication *>(qApp)) {
408  KIO::ClipboardUpdater::update(src, dest);
409  }
410 }
411 
412 class KIOWidgetJobUiDelegateFactory : public KIO::JobUiDelegateFactory
413 {
414 public:
415  KJobUiDelegate *createDelegate() const override
416  {
417  return new KIO::JobUiDelegate;
418  }
419 };
420 
421 Q_GLOBAL_STATIC(KIOWidgetJobUiDelegateFactory, globalUiDelegateFactory)
422 Q_GLOBAL_STATIC(KIO::JobUiDelegate, globalUiDelegate)
423 
424 // Simply linking to this library, creates a GUI job delegate and delegate extension for all KIO jobs
425 static void registerJobUiDelegate()
426 {
427  KIO::setDefaultJobUiDelegateFactory(globalUiDelegateFactory());
428  KIO::setDefaultJobUiDelegateExtension(globalUiDelegate());
429 }
430 
431 Q_CONSTRUCTOR_FUNCTION(registerJobUiDelegate)
432 
433 #include "jobuidelegate.moc"
PreferLocalFile
bool sync() override
bool askDeleteConfirmation(const QList< QUrl > &urls, DeletionType deletionType, ConfirmationType confirmationType) override
Ask for confirmation before deleting/trashing urls.
qulonglong filesize_t
64-bit file size
Definition: global.h:40
virtual void reject()
DeletionType
The type of deletion: real deletion, moving the files to the trash or emptying the trash Used by askD...
KJOBWIDGETS_EXPORT QWidget * window(KJob *job)
bool isNull() const const
KJob * job() const
QWidget * window() const const
void setWindow(QWidget *window) override
Associate this job with a window given by window.
void setWindowModality(Qt::WindowModality windowModality)
QString xi18nc(const char *context, const char *text, const TYPE &arg...)
A namespace for KIO globals.
void reserve(int alloc)
virtual int exec()
void writeEntry(const QString &key, const QVariant &value, WriteConfigFlags pFlags=Normal)
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
SkipDialog_Result askSkip(KJob *job, KIO::SkipDialog_Options options, const QString &error_text) override
QString & remove(int position, int n)
KGuiItem cont()
KIOCORE_EXPORT void setDefaultJobUiDelegateFactory(JobUiDelegateFactory *factory)
Internal.
KGuiItem cancel()
QString xi18ncp(const char *context, const char *singular, const char *plural, const TYPE &arg...)
KIOCORE_EXPORT void setDefaultJobUiDelegateExtension(JobUiDelegateExtension *extension)
Internal.
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
MetaData is a simple map of key/value strings.
Definition: metadata.h:23
void setDontShowAgainConfig(KConfig *cfg)
int size() const const
KGuiItem del()
RenameDialog_Result askFileRename(KJob *job, const QString &caption, const QUrl &src, const QUrl &dest, KIO::RenameDialog_Options options, QString &newDest, KIO::filesize_t sizeSrc=KIO::filesize_t(-1), KIO::filesize_t sizeDest=KIO::filesize_t(-1), const QDateTime &ctimeSrc=QDateTime(), const QDateTime &ctimeDest=QDateTime(), const QDateTime &mtimeSrc=QDateTime(), const QDateTime &mtimeDest=QDateTime()) override
ButtonCode warningContinueCancelDetailed(QWidget *parent, const QString &text, const QString &caption=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify, const QString &details=QString())
int count(const T &value) const const
KDE SSL Information Dialog.
void append(const T &value)
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &caption=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
Q_OBJECTQ_OBJECT
void finished(KJob *job)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
A UI delegate tuned to be used with KIO Jobs.
Definition: jobuidelegate.h:33
bool isEmpty() const const
static QList< QList< QSslError::SslError > > certificateErrorsFromString(const QString &errorsString)
Converts certificate errors as provided in the "ssl_cert_errors" meta data to a list of QSslError::Ss...
ConfirmationType
ForceConfirmation: always ask the user for confirmation DefaultConfirmation: don&#39;t ask the user if he...
QString path(QUrl::ComponentFormattingOptions options) const const
JobUiDelegate()
Constructs a new KIO Job UI delegate.
WId winId() const const
T & first()
QMap::iterator end()
int requestMessageBox(MessageBoxType type, const QString &text, const QString &caption, const QString &buttonYes, const QString &buttonNo, const QString &iconYes=QString(), const QString &iconNo=QString(), const QString &dontAskAgainName=QString(), const KIO::MetaData &metaData=KIO::MetaData()) override
This function allows for the delegation user prompts from the ioslaves.
ClipboardUpdater * createClipboardUpdater(Job *job, ClipboardUpdaterMode mode) override
Creates a clipboard updater.
ButtonCode questionYesNo(QWidget *parent, const QString &text, const QString &caption=QString(), const KGuiItem &buttonYes=KStandardGuiItem::yes(), const KGuiItem &buttonNo=KStandardGuiItem::no(), const QString &dontAskAgainName=QString(), Options options=Notify)
SkipEmptyParts
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
const Key key(const T &value, const Key &defaultKey) const const
QString i18n(const char *text, const TYPE &arg...)
virtual ~JobUiDelegate()
Destroys the KIO Job UI delegate.
void updateUrlInClipboard(const QUrl &src, const QUrl &dest) override
Update URL in clipboard, if present.
QWidget * parentWidget() const const
ButtonCode warningContinueCancelList(QWidget *parent, const QString &text, const QStringList &strlist, const QString &caption=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
T & last()
QUrl autoDestUrl() const
RenameDialog_Result
The result of a rename or skip dialog.
ButtonCode warningYesNoCancel(QWidget *parent, const QString &text, const QString &caption=QString(), const KGuiItem &buttonYes=KStandardGuiItem::yes(), const KGuiItem &buttonNo=KStandardGuiItem::no(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
The base class for all jobs.
Definition: job_base.h:45
virtual void setWindow(QWidget *window)
QIcon fromTheme(const QString &name)
The dialog shown when a CopyJob realizes that a destination file already exists, and wants to offer t...
Definition: renamedialog.h:38
void information(QWidget *parent, const QString &text, const QString &caption=QString(), const QString &dontShowAgainName=QString(), Options options=Notify)
WindowModal
Q_SLOTSQ_SLOTS
static void unregisterWindow(QWidget *window)
Unregister the given window from kded.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
T readEntry(const QString &key, const T &aDefault) const
QMap::iterator find(const Key &key)
void destroyed(QObject *obj)
QWidget * window() const
ButtonCode warningYesNo(QWidget *parent, const QString &text, const QString &caption=QString(), const KGuiItem &buttonYes=KStandardGuiItem::yes(), const KGuiItem &buttonNo=KStandardGuiItem::no(), const QString &dontAskAgainName=QString(), Options options=Options(Notify|Dangerous))
const T value(const Key &key, const T &defaultValue) const const
A factory for creating job ui delegates.
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Wed Jan 27 2021 23:00:04 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.