KIO

kpropertiesdialog.cpp
1 /*
2  This file is part of the KDE project
3  SPDX-FileCopyrightText: 1998, 1999 Torben Weis <[email protected]>
4  SPDX-FileCopyrightText: 1999, 2000 Preston Brown <[email protected]>
5  SPDX-FileCopyrightText: 2000 Simon Hausmann <[email protected]>
6  SPDX-FileCopyrightText: 2000 David Faure <[email protected]>
7  SPDX-FileCopyrightText: 2003 Waldo Bastian <[email protected]>
8 
9  SPDX-License-Identifier: LGPL-2.0-or-later
10 */
11 
12 /*
13  * kpropertiesdialog.cpp
14  * View/Edit Properties of files, locally or remotely
15  *
16  * some FilePermissionsPropsPlugin-changes by
17  * Henner Zeller <[email protected]>
18  * some layout management by
19  * Bertrand Leconte <[email protected]>
20  * the rest of the layout management, bug fixes, adaptation to libkio,
21  * template feature by
22  * David Faure <[email protected]>
23  * More layout, cleanups, and fixes by
24  * Preston Brown <[email protected]>
25  * Plugin capability, cleanups and port to KDialog by
26  * Simon Hausmann <[email protected]>
27  * KDesktopPropsPlugin by
28  * Waldo Bastian <[email protected]>
29  */
30 
31 #include "kpropertiesdialog.h"
32 #include "kpropertiesdialog_p.h"
33 #include "kio_widgets_debug.h"
34 #include "../pathhelpers_p.h"
35 
36 #include <config-kiowidgets.h>
37 
38 #include <qplatformdefs.h>
39 #include <algorithm>
40 #include <functional>
41 
42 #include <QCheckBox>
43 #include <QClipboard>
44 #include <QDebug>
45 #include <QDBusConnection>
46 #include <QDBusInterface>
47 #include <QDBusReply>
48 #include <QDir>
49 #include <QDialogButtonBox>
50 #include <QFile>
51 #include <QFileDialog>
52 #include <QFileInfo>
53 #include <QFileSystemWatcher>
54 #include <QFutureWatcher>
55 #include <QLabel>
56 #include <QLayout>
57 #include <QLocale>
58 #include <QMimeDatabase>
59 #include <QProgressBar>
60 #include <QPushButton>
61 #include <QRegularExpression>
62 #include <QStandardPaths>
63 #include <QStyle>
64 #include <QUrl>
65 #include <QVector>
66 #include <QtConcurrent>
67 
68 #if HAVE_POSIX_ACL
69 extern "C" {
70 # include <errno.h>
71 # include <sys/xattr.h>
72 }
73 #endif
74 
75 #include <KAuthorized>
76 #include <KColorScheme>
77 #include <KDialogJobUiDelegate>
78 #include <kdirnotify.h>
79 #include <kdiskfreespaceinfo.h>
80 #include <KDesktopFile>
81 #include <KPluginMetaData>
82 #include <KIconButton>
83 #include <kurlrequester.h>
84 #include <KLocalizedString>
85 #include <KJobUiDelegate>
86 #include <kio/global.h>
87 #include <kio/job.h>
88 #include <kio/copyjob.h>
89 #include <kio/chmodjob.h>
90 #include <kio/directorysizejob.h>
91 #include <KIO/FileSystemFreeSpaceJob>
92 #include <KIO/OpenFileManagerWindowJob>
93 #include <kio/renamedialog.h>
94 #include <kio/jobuidelegate.h>
95 #include <KJobWidgets>
96 #include <kmountpoint.h>
97 #include <KMessageBox>
98 #include <KMessageWidget>
99 #include <KService>
100 #include <KSharedConfig>
101 #include <KComboBox>
102 #include <KCompletion>
103 #include <KLineEdit>
104 #include <KSeparator>
105 #include <KSqueezedTextLabel>
106 #include <KMimeTypeTrader>
107 #include <KIO/ApplicationLauncherJob>
108 #include <kio/desktopexecparser.h>
109 #include <kacl.h>
110 #include <KConfigGroup>
111 #include <KMimeTypeEditor>
112 #include <KShell>
113 #include <KCapacityBar>
114 #include <kfileitemlistproperties.h>
115 #include <KWindowConfig>
116 #include <kioglobal_p.h>
117 #include <kprotocolinfo.h>
118 
119 #include "ui_checksumswidget.h"
120 #include "ui_kpropertiesdesktopbase.h"
121 #include "ui_kpropertiesdesktopadvbase.h"
122 #if HAVE_POSIX_ACL
123 #include "kacleditwidget.h"
124 #endif
125 
126 #include <kbuildsycocaprogressdialog.h>
127 #include <KMimeTypeChooser>
128 
129 #ifdef Q_OS_WIN
130 #include <qt_windows.h>
131 #include <shellapi.h>
132 #include <process.h>
133 #ifdef __GNUC__
134 # warning TODO: port completely to win32
135 #endif
136 #endif
137 
138 using namespace KDEPrivate;
139 
140 static QString nameFromFileName(QString nameStr)
141 {
142  if (nameStr.endsWith(QLatin1String(".desktop"))) {
143  nameStr.chop(8);
144  }
145  if (nameStr.endsWith(QLatin1String(".kdelnk"))) {
146  nameStr.chop(7);
147  }
148  // Make it human-readable (%2F => '/', ...)
149  nameStr = KIO::decodeFileName(nameStr);
150  return nameStr;
151 }
152 
153 const mode_t KFilePermissionsPropsPlugin::fperm[3][4] = {
154  {S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID},
155  {S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID},
156  {S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX}
157 };
158 
159 class Q_DECL_HIDDEN KPropertiesDialog::KPropertiesDialogPrivate
160 {
161 public:
162  explicit KPropertiesDialogPrivate(KPropertiesDialog *qq)
163  : q(qq)
164  , m_aborted(false)
165  {
166  }
167  ~KPropertiesDialogPrivate()
168  {
169  }
170 
174  void init();
178  void insertPages();
179 
180  KPropertiesDialog * const q;
181  bool m_aborted;
182  KPageWidgetItem *fileSharePageItem = nullptr;
186  QUrl m_singleUrl;
190  KFileItemList m_items;
194  QString m_defaultName;
195  QUrl m_currentDir;
200 };
201 
203  QWidget *parent)
204  : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
205 {
206  setWindowTitle(i18n("Properties for %1", KIO::decodeFileName(item.name())));
207 
208  Q_ASSERT(!item.isNull());
209  d->m_items.append(item);
210 
211  d->m_singleUrl = item.url();
212  Q_ASSERT(!d->m_singleUrl.isEmpty());
213 
214  d->init();
215 }
216 
218  QWidget *parent)
219  : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
220 {
221  setWindowTitle(i18n("Properties for %1", title));
222 
223  d->init();
224 }
225 
227  QWidget *parent)
228  : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
229 {
230  if (_items.count() > 1) {
231  setWindowTitle(i18np("Properties for 1 item", "Properties for %1 Selected Items", _items.count()));
232  } else {
233  setWindowTitle(i18n("Properties for %1", KIO::decodeFileName(_items.first().name())));
234  }
235 
236  Q_ASSERT(!_items.isEmpty());
237  d->m_singleUrl = _items.first().url();
238  Q_ASSERT(!d->m_singleUrl.isEmpty());
239 
240  d->m_items = _items;
241 
242  d->init();
243 }
244 
246  QWidget *parent)
247  : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
248 {
249  setWindowTitle(i18n("Properties for %1", KIO::decodeFileName(_url.fileName())));
250 
251  d->m_singleUrl = _url;
252 
253  KIO::StatJob *job = KIO::stat(_url);
254  KJobWidgets::setWindow(job, parent);
255  job->exec();
256  KIO::UDSEntry entry = job->statResult();
257 
258  d->m_items.append(KFileItem(entry, _url));
259  d->init();
260 }
261 
263  QWidget* parent)
264  : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
265 {
266  if (urls.count() > 1) {
267  setWindowTitle(i18np("Properties for 1 item", "Properties for %1 Selected Items", urls.count()));
268  } else {
269  setWindowTitle(i18n("Properties for %1", KIO::decodeFileName(urls.first().fileName())));
270  }
271 
272  Q_ASSERT(!urls.isEmpty());
273  d->m_singleUrl = urls.first();
274  Q_ASSERT(!d->m_singleUrl.isEmpty());
275 
276  d->m_items.reserve(urls.size());
277  for (const QUrl& url : urls) {
278  KIO::StatJob *job = KIO::stat(url);
279  KJobWidgets::setWindow(job, parent);
280  job->exec();
281  KIO::UDSEntry entry = job->statResult();
282 
283  d->m_items.append(KFileItem(entry, url));
284  }
285 
286  d->init();
287 }
288 
289 KPropertiesDialog::KPropertiesDialog(const QUrl &_tempUrl, const QUrl &_currentDir,
290  const QString &_defaultName,
291  QWidget *parent)
292  : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
293 {
294  setWindowTitle(i18n("Properties for %1", KIO::decodeFileName(_tempUrl.fileName())));
295 
296  d->m_singleUrl = _tempUrl;
297  d->m_defaultName = _defaultName;
298  d->m_currentDir = _currentDir;
299  Q_ASSERT(!d->m_singleUrl.isEmpty());
300 
301  // Create the KFileItem for the _template_ file, in order to read from it.
302  d->m_items.append(KFileItem(d->m_singleUrl));
303  d->init();
304 }
305 
306 #ifdef Q_OS_WIN
307 bool showWin32FilePropertyDialog(const QString &fileName)
308 {
309  QString path_ = QDir::toNativeSeparators(QFileInfo(fileName).absoluteFilePath());
310 
311 #ifndef _WIN32_WCE
312  SHELLEXECUTEINFOW execInfo;
313 #else
314  SHELLEXECUTEINFO execInfo;
315 #endif
316  memset(&execInfo, 0, sizeof(execInfo));
317  execInfo.cbSize = sizeof(execInfo);
318 #ifndef _WIN32_WCE
319  execInfo.fMask = SEE_MASK_INVOKEIDLIST | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI;
320 #else
321  execInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI;
322 #endif
323  const QString verb(QLatin1String("properties"));
324  execInfo.lpVerb = (LPCWSTR)verb.utf16();
325  execInfo.lpFile = (LPCWSTR)path_.utf16();
326 #ifndef _WIN32_WCE
327  return ShellExecuteExW(&execInfo);
328 #else
329  return ShellExecuteEx(&execInfo);
330  //There is no native file property dialog in wince
331  // return false;
332 #endif
333 }
334 #endif
335 
337  bool modal)
338 {
339  // TODO: do we really want to show the win32 property dialog?
340  // This means we lose metainfo, support for .desktop files, etc. (DF)
341 #ifdef Q_OS_WIN
342  QString localPath = item.localPath();
343  if (!localPath.isEmpty()) {
344  return showWin32FilePropertyDialog(localPath);
345  }
346 #endif
347  KPropertiesDialog *dlg = new KPropertiesDialog(item, parent);
348  if (modal) {
349  dlg->exec();
350  } else {
351  dlg->show();
352  }
353 
354  return true;
355 }
356 
358  bool modal)
359 {
360 #ifdef Q_OS_WIN
361  if (_url.isLocalFile()) {
362  return showWin32FilePropertyDialog(_url.toLocalFile());
363  }
364 #endif
365  KPropertiesDialog *dlg = new KPropertiesDialog(_url, parent);
366  if (modal) {
367  dlg->exec();
368  } else {
369  dlg->show();
370  }
371 
372  return true;
373 }
374 
376  bool modal)
377 {
378  if (_items.count() == 1) {
379  const KFileItem &item = _items.first();
380  if (item.entry().count() == 0 && item.localPath().isEmpty()) // this remote item wasn't listed by a slave
381  // Let's stat to get more info on the file
382  {
384  } else {
385  return KPropertiesDialog::showDialog(_items.first(), parent, modal);
386  }
387  }
388  KPropertiesDialog *dlg = new KPropertiesDialog(_items, parent);
389  if (modal) {
390  dlg->exec();
391  } else {
392  dlg->show();
393  }
394  return true;
395 }
396 
398  bool modal)
399 {
400  KPropertiesDialog *dlg = new KPropertiesDialog(urls, parent);
401  if (modal) {
402  dlg->exec();
403  } else {
404  dlg->show();
405  }
406  return true;
407 }
408 
409 void KPropertiesDialog::KPropertiesDialogPrivate::init()
410 {
411  q->setFaceType(KPageDialog::Tabbed);
412 
413  insertPages();
414 
415  KConfigGroup group(KSharedConfig::openConfig(), "KPropertiesDialog");
416  KWindowConfig::restoreWindowSize(q->windowHandle(), group);
417 }
418 
420 {
421  if (d->fileSharePageItem) {
422  setCurrentPage(d->fileSharePageItem);
423  }
424 }
425 
427 {
428  d->fileSharePageItem = addPage(page, i18nc("@title:tab", "Share"));
429 }
430 
432 {
433  for (KPropertiesDialogPlugin *it : qAsConst(d->m_pageList)) {
434  if (auto *filePropsPlugin = qobject_cast<KFilePropsPlugin *>(it)) {
435  filePropsPlugin->setFileNameReadOnly(ro);
436  } else if (auto *urlPropsPlugin = qobject_cast<KUrlPropsPlugin *>(it)) {
437  urlPropsPlugin->setFileNameReadOnly(ro);
438  }
439  }
440 }
441 
443 {
444  qDeleteAll(d->m_pageList);
445  delete d;
446 
447  KConfigGroup group(KSharedConfig::openConfig(), "KPropertiesDialog");
449 }
450 
452 {
454  plugin, QOverload<>::of(&KPropertiesDialogPlugin::setDirty));
455 
456  d->m_pageList.append(plugin);
457 }
458 
460 {
461  return d->m_singleUrl;
462 }
463 
465 {
466  return d->m_items.first();
467 }
468 
470 {
471  return d->m_items;
472 }
473 
475 {
476  return d->m_currentDir;
477 }
478 
480 {
481  return d->m_defaultName;
482 }
483 
485 {
486  // TODO: cache the result of those calls. Currently we parse .desktop files far too many times
487  return KFilePropsPlugin::supports(_items) ||
488  KFilePermissionsPropsPlugin::supports(_items) ||
489  KDesktopPropsPlugin::supports(_items) ||
490  KUrlPropsPlugin::supports(_items) ||
491  KDevicePropsPlugin::supports(_items) /* ||
492  KPreviewPropsPlugin::supports( _items )*/;
493 }
494 
496 {
497  accept();
498 }
499 
501 {
503  d->m_aborted = false;
504 
505  KFilePropsPlugin *filePropsPlugin = qobject_cast<KFilePropsPlugin *>(d->m_pageList.first());
506 
507  // If any page is dirty, then set the main one (KFilePropsPlugin) as
508  // dirty too. This is what makes it possible to save changes to a global
509  // desktop file into a local one. In other cases, it doesn't hurt.
510  for (pageListIt = d->m_pageList.constBegin(); pageListIt != d->m_pageList.constEnd(); ++pageListIt) {
511  if ((*pageListIt)->isDirty() && filePropsPlugin) {
512  filePropsPlugin->setDirty();
513  break;
514  }
515  }
516 
517  // Apply the changes in the _normal_ order of the tabs now
518  // This is because in case of renaming a file, KFilePropsPlugin will call
519  // KPropertiesDialog::rename, so other tab will be ok with whatever order
520  // BUT for file copied from templates, we need to do the renaming first !
521  for (pageListIt = d->m_pageList.constBegin(); pageListIt != d->m_pageList.constEnd() && !d->m_aborted; ++pageListIt) {
522  if ((*pageListIt)->isDirty()) {
523  // qDebug() << "applying changes for " << (*pageListIt)->metaObject()->className();
524  (*pageListIt)->applyChanges();
525  // applyChanges may change d->m_aborted.
526  } else {
527  // qDebug() << "skipping page " << (*pageListIt)->metaObject()->className();
528  }
529  }
530 
531  if (!d->m_aborted && filePropsPlugin) {
532  filePropsPlugin->postApplyChanges();
533  }
534 
535  if (!d->m_aborted) {
536  emit applied();
537  emit propertiesClosed();
538  deleteLater(); // somewhat like Qt::WA_DeleteOnClose would do.
540  } // else, keep dialog open for user to fix the problem.
541 }
542 
544 {
545  reject();
546 }
547 
549 {
550  emit canceled();
551  emit propertiesClosed();
552 
553  deleteLater();
555 }
556 
557 void KPropertiesDialog::KPropertiesDialogPrivate::insertPages()
558 {
559  if (m_items.isEmpty()) {
560  return;
561  }
562 
563  if (KFilePropsPlugin::supports(m_items)) {
565  q->insertPlugin(p);
566  }
567 
568  if (KFilePermissionsPropsPlugin::supports(m_items)) {
570  q->insertPlugin(p);
571  }
572 
573  if (KChecksumsPlugin::supports(m_items)) {
575  q->insertPlugin(p);
576  }
577 
578  if (KDesktopPropsPlugin::supports(m_items)) {
580  q->insertPlugin(p);
581  }
582 
583  if (KUrlPropsPlugin::supports(m_items)) {
585  q->insertPlugin(p);
586  }
587 
588  if (KDevicePropsPlugin::supports(m_items)) {
590  q->insertPlugin(p);
591  }
592 
593 // if ( KPreviewPropsPlugin::supports( m_items ) ) {
594 // KPropertiesDialogPlugin *p = new KPreviewPropsPlugin(q);
595 // q->insertPlugin(p);
596 // }
597 
598  //plugins
599 
600  if (m_items.count() != 1) {
601  return;
602  }
603 
604  const KFileItem item = m_items.first();
605  const QString mimetype = item.mimetype();
606 
607  if (mimetype.isEmpty()) {
608  return;
609  }
610 
611  QString query = QStringLiteral(
612  "(((not exist [X-KDE-Protocol]) and (not exist [X-KDE-Protocols])) or ([X-KDE-Protocol] == '%1') or ('%1' in [X-KDE-Protocols]))"
613  ).arg(item.url().scheme());
614 
615  // qDebug() << "trader query: " << query;
616 
617  QStringList addedPlugins;
618  const auto jsonPlugins = KPluginLoader::findPlugins(QStringLiteral("kf5/propertiesdialog"));
619  for (const auto &jsonMetadata : jsonPlugins) {
620  KPluginFactory *factory = KPluginLoader(jsonMetadata.fileName()).factory();
621  if (!factory) {
622  continue;
623  }
625  if (plugin) {
626  q->insertPlugin(plugin);
627  addedPlugins.append(jsonMetadata.pluginId());
628  }
629  }
630 
631  // TODO KF6 - Remove once we drop loading of C++ plugins without JSON metadata.
632  const KService::List offers = KMimeTypeTrader::self()->query(mimetype, QStringLiteral("KPropertiesDialog/Plugin"), query);
633  for (const KService::Ptr &ptr : offers) {
634  if (addedPlugins.contains(ptr->desktopEntryName())) {
635  continue;
636  }
637  qCWarning(KIO_WIDGETS) << "Plugin" << ptr->desktopEntryName() << "is using the deprecated loading style. Please port it to JSON loading.";
638  KPropertiesDialogPlugin *plugin = ptr->createInstance<KPropertiesDialogPlugin>(q);
639  if (!plugin) {
640  continue;
641  }
642  plugin->setObjectName(ptr->name());
643 
644  q->insertPlugin(plugin);
645  addedPlugins.append(ptr->desktopEntryName());
646  }
647 }
648 
649 void KPropertiesDialog::updateUrl(const QUrl &_newUrl)
650 {
651  Q_ASSERT(d->m_items.count() == 1);
652  // qDebug() << "KPropertiesDialog::updateUrl (pre)" << _newUrl;
653  QUrl newUrl = _newUrl;
654  emit saveAs(d->m_singleUrl, newUrl);
655  // qDebug() << "KPropertiesDialog::updateUrl (post)" << newUrl;
656 
657  d->m_singleUrl = newUrl;
658  d->m_items.first().setUrl(newUrl);
659  Q_ASSERT(!d->m_singleUrl.isEmpty());
660  // If we have an Desktop page, set it dirty, so that a full file is saved locally
661  // Same for a URL page (because of the Name= hack)
662  for (KPropertiesDialogPlugin *it : qAsConst(d->m_pageList)) {
663  if (qobject_cast<KUrlPropsPlugin *>(it) ||
665  //qDebug() << "Setting page dirty";
666  it->setDirty();
667  break;
668  }
669  }
670 }
671 
673 {
674  Q_ASSERT(d->m_items.count() == 1);
675  // qDebug() << "KPropertiesDialog::rename " << _name;
676  QUrl newUrl;
677  // if we're creating from a template : use currentdir
678  if (!d->m_currentDir.isEmpty()) {
679  newUrl = d->m_currentDir;
680  newUrl.setPath(concatPaths(newUrl.path(), _name));
681  } else {
682  // It's a directory, so strip the trailing slash first
683  newUrl = d->m_singleUrl.adjusted(QUrl::StripTrailingSlash);
684  // Now change the filename
685  newUrl = newUrl.adjusted(QUrl::RemoveFilename); // keep trailing slash
686  newUrl.setPath(concatPaths(newUrl.path(), _name));
687  }
688  updateUrl(newUrl);
689 }
690 
692 {
693  d->m_aborted = true;
694 }
695 
696 class Q_DECL_HIDDEN KPropertiesDialogPlugin::KPropertiesDialogPluginPrivate
697 {
698 public:
699  KPropertiesDialogPluginPrivate()
700  {
701  }
702  ~KPropertiesDialogPluginPrivate()
703  {
704  }
705 
706  bool m_bDirty;
707  int fontHeight;
708 };
709 
711  : QObject(_props), d(new KPropertiesDialogPluginPrivate)
712 {
713  properties = _props;
714  d->fontHeight = 2 * properties->fontMetrics().height();
715  d->m_bDirty = false;
716 }
717 
718 KPropertiesDialogPlugin::~KPropertiesDialogPlugin()
719 {
720  delete d;
721 }
722 
723 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(4, 1)
725 {
726  return _item.isDesktopFile();
727 }
728 #endif
729 
730 void KPropertiesDialogPlugin::setDirty(bool b)
731 {
732  d->m_bDirty = b;
733 }
734 
735 void KPropertiesDialogPlugin::setDirty()
736 {
737  d->m_bDirty = true;
738 }
739 
740 bool KPropertiesDialogPlugin::isDirty() const
741 {
742  return d->m_bDirty;
743 }
744 
746 {
747  qCWarning(KIO_WIDGETS) << "applyChanges() not implemented in page !";
748 }
749 
751 {
752  return d->fontHeight;
753 }
754 
756 
757 class KFilePropsPlugin::KFilePropsPluginPrivate
758 {
759 public:
760  KFilePropsPluginPrivate()
761  {
762  dirSizeJob = nullptr;
763  dirSizeUpdateTimer = nullptr;
764  m_lined = nullptr;
765  m_capacityBar = nullptr;
766  m_linkTargetLineEdit = nullptr;
767  }
768  ~KFilePropsPluginPrivate()
769  {
770  if (dirSizeJob) {
771  dirSizeJob->kill();
772  }
773  }
774 
775  KIO::DirectorySizeJob *dirSizeJob;
776  QTimer *dirSizeUpdateTimer;
777  QFrame *m_frame;
778  bool bMultiple;
779  bool bIconChanged;
780  bool bKDesktopMode;
781  bool bDesktopFile;
782  KCapacityBar *m_capacityBar;
783  QString mimeType;
784  QString oldFileName;
785  KLineEdit *m_lined;
786  QLabel *m_fileNameLabel = nullptr;
787  QGridLayout *m_grid = nullptr;
788 
789  QWidget *iconArea;
790 
791  QLabel *m_sizeLabel;
792 
793  QPushButton *m_sizeDetermineButton;
794  QPushButton *m_sizeStopButton;
795  QPushButton *m_sizeDetailsButton;
796 
797  KLineEdit *m_linkTargetLineEdit;
798 
799  QString m_sRelativePath;
800  bool m_bFromTemplate;
801 
805  QString oldName;
806 };
807 
808 KFilePropsPlugin::KFilePropsPlugin(KPropertiesDialog *_props)
809  : KPropertiesDialogPlugin(_props), d(new KFilePropsPluginPrivate)
810 {
811  d->bMultiple = (properties->items().count() > 1);
812  d->bIconChanged = false;
813  d->bDesktopFile = KDesktopPropsPlugin::supports(properties->items());
814  // qDebug() << "KFilePropsPlugin::KFilePropsPlugin bMultiple=" << d->bMultiple;
815 
816  // We set this data from the first item, and we'll
817  // check that the other items match against it, resetting when not.
818  bool isLocal;
819  const KFileItem item = properties->item();
820  QUrl url = item.mostLocalUrl(&isLocal);
821  bool isReallyLocal = item.url().isLocalFile();
822  bool bDesktopFile = item.isDesktopFile();
823  mode_t mode = item.mode();
824  bool hasDirs = item.isDir() && !item.isLink();
825  bool hasRoot = url.path() == QLatin1String("/");
826  QString iconStr = item.iconName();
828  QString protocol = properties->url().scheme();
829  d->bKDesktopMode = protocol == QLatin1String("desktop") ||
830  properties->currentDir().scheme() == QLatin1String("desktop");
831  QString mimeComment = item.mimeComment();
832  d->mimeType = item.mimetype();
833  KIO::filesize_t totalSize = item.size();
834  QString magicMimeComment;
835  QMimeDatabase db;
836  if (isLocal) {
838  if (magicMimeType.isValid() && !magicMimeType.isDefault()) {
839  magicMimeComment = magicMimeType.comment();
840  }
841  }
842 #ifdef Q_OS_WIN
843  if (isReallyLocal) {
844  directory = QDir::toNativeSeparators(directory.mid(1));
845  }
846 #endif
847 
848  // Those things only apply to 'single file' mode
849  QString filename;
850  bool isTrash = false;
851  d->m_bFromTemplate = false;
852 
853  // And those only to 'multiple' mode
854  uint iDirCount = hasDirs ? 1 : 0;
855  uint iFileCount = 1 - iDirCount;
856 
857  d->m_frame = new QFrame();
858  properties->addPage(d->m_frame, i18nc("@title:tab File properties", "&General"));
859 
860  QVBoxLayout *vbl = new QVBoxLayout(d->m_frame);
861  vbl->setContentsMargins(0, 0, 0, 0);
862  vbl->setObjectName(QStringLiteral("vbl"));
863  QGridLayout *grid = new QGridLayout(); // unknown rows
864  d->m_grid = grid;
865  grid->setColumnStretch(0, 0);
866  grid->setColumnStretch(1, 0);
867  grid->setColumnStretch(2, 1);
868  const int horizontalSpacing = d->m_frame->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
869  grid->addItem(new QSpacerItem(horizontalSpacing, 0), 0, 1);
870  vbl->addLayout(grid);
871  int curRow = 0;
872 
873  if (!d->bMultiple) {
874  QString path;
875  if (!d->m_bFromTemplate) {
876  isTrash = (properties->url().scheme() == QLatin1String("trash"));
877  // Extract the full name, but without file: for local files
879  } else {
880  path = concatPaths(properties->currentDir().path(), properties->defaultName());
882  }
883 
884  if (d->bDesktopFile) {
885  determineRelativePath(path);
886  }
887 
888  // Extract the file name only
889  filename = properties->defaultName();
890  if (filename.isEmpty()) { // no template
891  const QFileInfo finfo(item.name()); // this gives support for UDS_NAME, e.g. for kio_trash or kio_system
892  filename = finfo.fileName(); // Make sure only the file's name is displayed (#160964).
893  } else {
894  d->m_bFromTemplate = true;
895  setDirty(); // to enforce that the copy happens
896  }
897  d->oldFileName = filename;
898 
899  // Make it human-readable
900  filename = nameFromFileName(filename);
901 
902  if (d->bKDesktopMode && d->bDesktopFile) {
903  KDesktopFile config(url.toLocalFile());
904  if (config.desktopGroup().hasKey("Name")) {
905  filename = config.readName();
906  }
907  }
908 
909  d->oldName = filename;
910  } else {
911  // Multiple items: see what they have in common
912  const KFileItemList items = properties->items();
913  KFileItemList::const_iterator kit = items.begin();
914  const KFileItemList::const_iterator kend = items.end();
915  for (++kit /*no need to check the first one again*/; kit != kend; ++kit) {
916  const QUrl url = (*kit).url();
917  // qDebug() << "KFilePropsPlugin::KFilePropsPlugin " << url.toDisplayString();
918  // The list of things we check here should match the variables defined
919  // at the beginning of this method.
920  if (url.isLocalFile() != isLocal) {
921  isLocal = false; // not all local
922  }
923  if (bDesktopFile && (*kit).isDesktopFile() != bDesktopFile) {
924  bDesktopFile = false; // not all desktop files
925  }
926  if ((*kit).mode() != mode) {
927  mode = (mode_t)0;
928  }
929  if (KIO::iconNameForUrl(url) != iconStr) {
930  iconStr = QStringLiteral("document-multiple");
931  }
932  if (url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path() != directory) {
933  directory.clear();
934  }
935  if (url.scheme() != protocol) {
936  protocol.clear();
937  }
938  if (!mimeComment.isNull() && (*kit).mimeComment() != mimeComment) {
939  mimeComment.clear();
940  }
941  if (isLocal && !magicMimeComment.isNull()) {
943  if (magicMimeType.isValid() && magicMimeType.comment() != magicMimeComment) {
944  magicMimeComment.clear();
945  }
946  }
947 
948  if (isLocal && url.path() == QLatin1String("/")) {
949  hasRoot = true;
950  }
951  if ((*kit).isDir() && !(*kit).isLink()) {
952  iDirCount++;
953  hasDirs = true;
954  } else {
955  iFileCount++;
956  totalSize += (*kit).size();
957  }
958  }
959  }
960 
961  if (!isReallyLocal && !protocol.isEmpty()) {
962  directory += QLatin1String(" (") + protocol + QLatin1Char(')');
963  }
964 
965  if (!isTrash && (bDesktopFile || ((mode & QT_STAT_MASK) == QT_STAT_DIR))
966  && !d->bMultiple // not implemented for multiple
967  && enableIconButton()) { // #56857
968  KIconButton *iconButton = new KIconButton(d->m_frame);
969  int bsize = 66 + 2 * iconButton->style()->pixelMetric(QStyle::PM_ButtonMargin);
970  iconButton->setFixedSize(bsize, bsize);
971  iconButton->setIconSize(48);
972  iconButton->setStrictIconSize(false);
973  if (bDesktopFile && isLocal) {
974  const KDesktopFile config(url.toLocalFile());
975  if (config.hasDeviceType()) {
977  } else {
979  }
980  } else {
982  }
983  iconButton->setIcon(iconStr);
984  d->iconArea = iconButton;
985  connect(iconButton, &KIconButton::iconChanged,
986  this, &KFilePropsPlugin::slotIconChanged);
987  } else {
988  QLabel *iconLabel = new QLabel(d->m_frame);
989  iconLabel->setAlignment(Qt::AlignCenter);
990  int bsize = 66 + 2 * iconLabel->style()->pixelMetric(QStyle::PM_ButtonMargin);
991  iconLabel->setFixedSize(bsize, bsize);
992  iconLabel->setPixmap(QIcon::fromTheme(iconStr).pixmap(48));
993  d->iconArea = iconLabel;
994  }
995  grid->addWidget(d->iconArea, curRow, 0, Qt::AlignCenter);
996 
997  KFileItemListProperties itemList(KFileItemList() << item);
998  if (d->bMultiple || isTrash || hasRoot || !(d->m_bFromTemplate || itemList.supportsMoving())) {
999  setFileNameReadOnly(true);
1000  if (d->bMultiple) {
1001  d->m_fileNameLabel->setText(KIO::itemsSummaryString(iFileCount + iDirCount, iFileCount, iDirCount, 0, false));
1002  }
1003 
1004  } else {
1005  d->m_lined = new KLineEdit(d->m_frame);
1006  d->m_lined->setObjectName(QStringLiteral("KFilePropsPlugin::nameLineEdit"));
1007  d->m_lined->setText(filename);
1008  d->m_lined->setFocus();
1009 
1010  // Enhanced rename: Don't highlight the file extension.
1011  QString extension = db.suffixForFileName(filename);
1012  if (!extension.isEmpty()) {
1013  d->m_lined->setSelection(0, filename.length() - extension.length() - 1);
1014  } else {
1015  int lastDot = filename.lastIndexOf(QLatin1Char('.'));
1016  if (lastDot > 0) {
1017  d->m_lined->setSelection(0, lastDot);
1018  }
1019  }
1020 
1021  connect(d->m_lined, &QLineEdit::textChanged,
1022  this, &KFilePropsPlugin::nameFileChanged);
1023  grid->addWidget(d->m_lined, curRow, 2);
1024  }
1025  ++curRow;
1026 
1027  KSeparator *sep = new KSeparator(Qt::Horizontal, d->m_frame);
1028  grid->addWidget(sep, curRow, 0, 1, 3);
1029  ++curRow;
1030 
1031  QLabel *l;
1032  if (!mimeComment.isEmpty() && !isTrash) {
1033  l = new QLabel(i18n("Type:"), d->m_frame);
1034  grid->addWidget(l, curRow, 0, Qt::AlignRight | Qt::AlignTop);
1035 
1036  QFrame *box = new QFrame(d->m_frame);
1037  QVBoxLayout *boxLayout = new QVBoxLayout(box);
1038  boxLayout->setSpacing(2); // without that spacing the button literally “sticks” to the label ;)
1039  boxLayout->setContentsMargins(0, 0, 0, 0);
1040 
1041  l = new QLabel(mimeComment, box);
1043  grid->addWidget(box, curRow++, 2);
1044 
1045  QPushButton *button = new QPushButton(box);
1046  button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); // Minimum still makes the button grow to the entire layout width
1047  button->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
1048 
1049  boxLayout->addWidget(l);
1050  boxLayout->addWidget(button);
1051 
1052  if (d->mimeType == QLatin1String("application/octet-stream")) {
1053  button->setText(i18n("Create New File Type"));
1054  } else {
1055  button->setText(i18n("File Type Options"));
1056  }
1057 
1058  connect(button, &QAbstractButton::clicked, this, &KFilePropsPlugin::slotEditFileType);
1059 
1060  if (!KAuthorized::authorizeAction(QStringLiteral("editfiletype"))) {
1061  button->hide();
1062  }
1063  }
1064 
1065  if (!magicMimeComment.isEmpty() && magicMimeComment != mimeComment) {
1066  l = new QLabel(i18n("Contents:"), d->m_frame);
1067  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1068 
1069  l = new QLabel(magicMimeComment, d->m_frame);
1070  grid->addWidget(l, curRow++, 2);
1071  }
1072 
1073  if (!directory.isEmpty()) {
1074  l = new QLabel(i18n("Location:"), d->m_frame);
1075  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1076 
1077  l = new KSqueezedTextLabel(directory, d->m_frame);
1078  // force the layout direction to be always LTR
1080  // but if we are in RTL mode, align the text to the right
1081  // otherwise the text is on the wrong side of the dialog
1084  }
1086  grid->addWidget(l, curRow++, 2);
1087  }
1088 
1089  l = new QLabel(i18n("Size:"), d->m_frame);
1090  grid->addWidget(l, curRow, 0, Qt::AlignRight | Qt::AlignTop);
1091 
1092  d->m_sizeLabel = new QLabel(d->m_frame);
1093  d->m_sizeLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
1094  grid->addWidget(d->m_sizeLabel, curRow++, 2);
1095 
1096  if (!hasDirs) { // Only files [and symlinks]
1097  d->m_sizeLabel->setText(QStringLiteral("%1 (%2)").arg(KIO::convertSize(totalSize),
1098  QLocale().toString(totalSize)));
1099  d->m_sizeDetermineButton = nullptr;
1100  d->m_sizeStopButton = nullptr;
1101  d->m_sizeDetailsButton = nullptr;
1102  } else { // Directory
1103  QHBoxLayout *sizelay = new QHBoxLayout();
1104  grid->addLayout(sizelay, curRow++, 2);
1105 
1106  // buttons
1107  d->m_sizeDetermineButton = new QPushButton(i18n("Calculate"), d->m_frame);
1108  d->m_sizeStopButton = new QPushButton(i18n("Stop"), d->m_frame);
1109 
1110  d->m_sizeDetermineButton->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
1111  d->m_sizeStopButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-cancel")));
1112 
1113  connect(d->m_sizeDetermineButton, &QAbstractButton::clicked, this, &KFilePropsPlugin::slotSizeDetermine);
1114  connect(d->m_sizeStopButton, &QAbstractButton::clicked, this, &KFilePropsPlugin::slotSizeStop);
1115 
1116  sizelay->addWidget(d->m_sizeDetermineButton, 0);
1117  sizelay->addWidget(d->m_sizeStopButton, 0);
1118 
1119  if (KService::serviceByDesktopName(QStringLiteral("org.kde.filelight"))) {
1120  d->m_sizeDetailsButton = new QPushButton(i18n("Explore in Filelight"), d->m_frame);
1121  d->m_sizeDetailsButton->setIcon(QIcon::fromTheme(QStringLiteral("filelight")));
1122  connect(d->m_sizeDetailsButton, &QPushButton::clicked, this, &KFilePropsPlugin::slotSizeDetails);
1123  sizelay->addWidget(d->m_sizeDetailsButton, 0);
1124  }
1125 
1126  sizelay->addStretch(10); // so that the buttons don't grow horizontally
1127 
1128  // auto-launch for local dirs only, and not for '/'
1129  if (isLocal && !hasRoot) {
1130  d->m_sizeDetermineButton->setText(i18n("Refresh"));
1131  slotSizeDetermine();
1132  } else {
1133  d->m_sizeStopButton->setEnabled(false);
1134  }
1135  }
1136 
1137  if (!d->bMultiple && item.isLink()) {
1138  l = new QLabel(i18n("Points to:"), d->m_frame);
1139  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1140 
1141  d->m_linkTargetLineEdit = new KLineEdit(item.linkDest(), d->m_frame);
1142  connect(d->m_linkTargetLineEdit, &QLineEdit::textChanged,
1143  this, QOverload<>::of(&KFilePropsPlugin::setDirty));
1144 
1145  QPushButton *goThereButton = new QPushButton(d->m_frame);
1146  goThereButton->setIcon(QIcon::fromTheme(QStringLiteral("go-jump")));
1147 
1148  QHBoxLayout *row = new QHBoxLayout();
1149  row->setContentsMargins(0, 0, 0, 0);
1150  row->addWidget(d->m_linkTargetLineEdit);
1151  row->addWidget(goThereButton);
1152  grid->addLayout(row, curRow++, 2);
1153 
1154  KMessageWidget* messageWidget = new KMessageWidget(d->m_frame);
1155  messageWidget->setWordWrap(true);
1156  messageWidget->setMessageType(KMessageWidget::Error);
1157  messageWidget->setText(i18n("Invalid link target"));
1158  messageWidget->hide();
1159  grid->addWidget(messageWidget, curRow++, 0, 1, -1);
1160 
1161  connect(goThereButton, &QPushButton::clicked,
1162  this, [=]() {
1163  const QUrl targetLocation = QUrl::fromLocalFile(d->m_linkTargetLineEdit->text());
1164  KIO::StatJob *statJob = KIO::stat(targetLocation, KIO::HideProgressInfo);
1165  bool ok = statJob->exec();
1166  if (ok) {
1167  KIO::highlightInFileManager({targetLocation});
1168  _props->close();
1169  return;
1170  }
1171  // Show error message if the link destination doesn't exist
1172  messageWidget->animatedShow();
1173  });
1174  }
1175 
1176  if (!d->bMultiple) { // Dates and extra fields for multiple don't make much sense...
1177  QLocale locale;
1178  const auto extraFields = KProtocolInfo::extraFields(url);
1179  for (int i = 0; i < extraFields.count(); ++i) {
1180  const auto &field = extraFields.at(i);
1181 
1182  QString label = item.entry().stringValue(KIO::UDSEntry::UDS_EXTRA + i);
1183  if (field.type == KProtocolInfo::ExtraField::Invalid || label.isEmpty()) {
1184  continue;
1185  }
1186 
1187  if (field.type == KProtocolInfo::ExtraField::DateTime) {
1188  const QDateTime date = QDateTime::fromString(label, Qt::ISODate);
1189  if (!date.isValid()) {
1190  continue;
1191  }
1192 
1193  label = locale.toString(date, QLocale::LongFormat);
1194  }
1195 
1196  l = new QLabel(i18n("%1:", field.name), d->m_frame);
1197  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1198 
1199  l = new KSqueezedTextLabel(label, d->m_frame);
1202  } else {
1204  }
1205  grid->addWidget(l, curRow++, 2);
1207  }
1208 
1209  QDateTime dt = item.time(KFileItem::CreationTime);
1210  if (!dt.isNull()) {
1211  l = new QLabel(i18n("Created:"), d->m_frame);
1212  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1213 
1214  l = new QLabel(locale.toString(dt, QLocale::LongFormat), d->m_frame);
1215  grid->addWidget(l, curRow++, 2);
1217  }
1218 
1219  dt = item.time(KFileItem::ModificationTime);
1220  if (!dt.isNull()) {
1221  l = new QLabel(i18n("Modified:"), d->m_frame);
1222  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1223 
1224  l = new QLabel(locale.toString(dt, QLocale::LongFormat), d->m_frame);
1226  grid->addWidget(l, curRow++, 2);
1227  }
1228 
1229  dt = item.time(KFileItem::AccessTime);
1230  if (!dt.isNull()) {
1231  l = new QLabel(i18n("Accessed:"), d->m_frame);
1232  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1233 
1234  l = new QLabel(locale.toString(dt, QLocale::LongFormat), d->m_frame);
1236  grid->addWidget(l, curRow++, 2);
1237  }
1238  }
1239 
1240  if (hasDirs) { // only for directories
1241  sep = new KSeparator(Qt::Horizontal, d->m_frame);
1242  grid->addWidget(sep, curRow, 0, 1, 3);
1243  ++curRow;
1244 
1245  if (isLocal) {
1247 
1248  if (mp) {
1249  l = new QLabel(i18n("File System:"), d->m_frame);
1250  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1251 
1252  l = new QLabel(d->m_frame);
1253  grid->addWidget(l, curRow++, 2);
1255  l->setText(mp->mountType());
1256 
1257  l = new QLabel(i18n("Mounted on:"), d->m_frame);
1258  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1259 
1260  l = new KSqueezedTextLabel(mp->mountPoint(), d->m_frame);
1262  grid->addWidget(l, curRow++, 2);
1263 
1264  l = new QLabel(i18n("Mounted from:"), d->m_frame);
1265  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1266 
1267  l = new QLabel(mp->mountedFrom(), d->m_frame);
1268  grid->addWidget(l, curRow++, 2);
1270  }
1271  }
1272 
1273  l = new QLabel(i18nc("Amount of used and available space on this device or partition", "Free space:"), d->m_frame);
1274  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1275 
1276  d->m_capacityBar = new KCapacityBar(KCapacityBar::DrawTextOutline, d->m_frame);
1277  d->m_capacityBar->setText(i18nc("@info:status", "Unknown size"));
1278  grid->addWidget(d->m_capacityBar, curRow++, 2);
1279 
1281  connect(job, &KIO::FileSystemFreeSpaceJob::result, this, &KFilePropsPlugin::slotFreeSpaceResult);
1282  }
1283 
1284  vbl->addStretch(1);
1285 }
1286 
1287 bool KFilePropsPlugin::enableIconButton() const
1288 {
1289  const KFileItem item = properties->item();
1290  // If the current item is a directory, check if it's writable,
1291  // so we can create/update a .directory
1292  // Current item is a file, same thing: check if it is writable
1293  if (item.isWritable()) {
1294  // exclude remote dirs as changing the icon has no effect (bug 205954)
1295  if (item.isLocalFile() || item.url().scheme() == QLatin1String("desktop")) {
1296  return true;
1297  }
1298  }
1299 
1300  return false;
1301 }
1302 
1303 // QString KFilePropsPlugin::tabName () const
1304 // {
1305 // return i18n ("&General");
1306 // }
1307 
1308 void KFilePropsPlugin::setFileNameReadOnly(bool ro)
1309 {
1310  Q_ASSERT(ro); // false isn't supported
1311  if (ro && !d->m_fileNameLabel) {
1312  Q_ASSERT(!d->m_bFromTemplate);
1313  delete d->m_lined;
1314  d->m_lined = nullptr;
1315  d->m_fileNameLabel = new QLabel(d->m_frame);
1316  d->m_fileNameLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
1317  d->m_fileNameLabel->setText(d->oldName); // will get overwritten if d->bMultiple
1318  d->m_grid->addWidget(d->m_fileNameLabel, 0, 2);
1319  }
1320 }
1321 
1322 void KFilePropsPlugin::slotEditFileType()
1323 {
1324  QString mime;
1325  if (d->mimeType == QLatin1String("application/octet-stream")) {
1326  const int pos = d->oldFileName.lastIndexOf(QLatin1Char('.'));
1327  if (pos != -1) {
1328  mime = QLatin1Char('*') + d->oldFileName.midRef(pos);
1329  } else {
1330  mime = QStringLiteral("*");
1331  }
1332  } else {
1333  mime = d->mimeType;
1334  }
1336 }
1337 
1338 void KFilePropsPlugin::slotIconChanged()
1339 {
1340  d->bIconChanged = true;
1341  emit changed();
1342 }
1343 
1344 void KFilePropsPlugin::nameFileChanged(const QString &text)
1345 {
1347  emit changed();
1348 }
1349 
1350 static QString relativeAppsLocation(const QString &file)
1351 {
1352  const QString canonical = QFileInfo(file).canonicalFilePath();
1354  for (const QString &base : dirs) {
1355  QDir base_dir = QDir(base);
1356  if (base_dir.exists() && canonical.startsWith(base_dir.canonicalPath())) {
1357  return canonical.mid(base.length() + 1);
1358  }
1359  }
1360  return QString(); // return empty if the file is not in apps
1361 }
1362 
1363 void KFilePropsPlugin::determineRelativePath(const QString &path)
1364 {
1365  // now let's make it relative
1366  d->m_sRelativePath = relativeAppsLocation(path);
1367 }
1368 
1369 void KFilePropsPlugin::slotFreeSpaceResult(KIO::Job *job, KIO::filesize_t size, KIO::filesize_t available)
1370 {
1371  if (!job->error()) {
1372  const quint64 used = size - available;
1373  const int percentUsed = qRound(100.0 * qreal(used) / qreal(size));
1374 
1375  d->m_capacityBar->setText(
1376  i18nc("Available space out of total partition size (percent used)", "%1 free of %2 (%3% used)",
1377  KIO::convertSize(available),
1378  KIO::convertSize(size),
1379  percentUsed));
1380 
1381  d->m_capacityBar->setValue(percentUsed);
1382  } else {
1383  d->m_capacityBar->setText(i18nc("@info:status", "Unknown size"));
1384  d->m_capacityBar->setValue(0);
1385  }
1386 }
1387 
1388 void KFilePropsPlugin::slotDirSizeUpdate()
1389 {
1390  KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
1391  KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles();
1392  KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs();
1393  d->m_sizeLabel->setText(
1394  i18n("Calculating... %1 (%2)\n%3, %4",
1395  KIO::convertSize(totalSize),
1396  QLocale().toString(totalSize),
1397  i18np("1 file", "%1 files", totalFiles),
1398  i18np("1 sub-folder", "%1 sub-folders", totalSubdirs)));
1399 }
1400 
1401 void KFilePropsPlugin::slotDirSizeFinished(KJob *job)
1402 {
1403  if (job->error()) {
1404  d->m_sizeLabel->setText(job->errorString());
1405  } else {
1406  KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
1407  KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles();
1408  KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs();
1409  d->m_sizeLabel->setText(QStringLiteral("%1 (%2)\n%3, %4")
1410  .arg(KIO::convertSize(totalSize),
1411  QLocale().toString(totalSize),
1412  i18np("1 file", "%1 files", totalFiles),
1413  i18np("1 sub-folder", "%1 sub-folders", totalSubdirs)));
1414  }
1415  d->m_sizeStopButton->setEnabled(false);
1416  // just in case you change something and try again :)
1417  d->m_sizeDetermineButton->setText(i18n("Refresh"));
1418  d->m_sizeDetermineButton->setEnabled(true);
1419  d->dirSizeJob = nullptr;
1420  delete d->dirSizeUpdateTimer;
1421  d->dirSizeUpdateTimer = nullptr;
1422 }
1423 
1424 void KFilePropsPlugin::slotSizeDetermine()
1425 {
1426  d->m_sizeLabel->setText(i18n("Calculating..."));
1427  // qDebug() << "properties->item()=" << properties->item() << "URL=" << properties->item().url();
1428 
1429  d->dirSizeJob = KIO::directorySize(properties->items());
1430  d->dirSizeUpdateTimer = new QTimer(this);
1431  connect(d->dirSizeUpdateTimer, &QTimer::timeout,
1432  this, &KFilePropsPlugin::slotDirSizeUpdate);
1433  d->dirSizeUpdateTimer->start(500);
1434  connect(d->dirSizeJob, &KJob::result,
1435  this, &KFilePropsPlugin::slotDirSizeFinished);
1436  d->m_sizeStopButton->setEnabled(true);
1437  d->m_sizeDetermineButton->setEnabled(false);
1438 
1439  // also update the "Free disk space" display
1440  if (d->m_capacityBar) {
1441  const KFileItem item = properties->item();
1443  connect(job, &KIO::FileSystemFreeSpaceJob::result, this, &KFilePropsPlugin::slotFreeSpaceResult);
1444  }
1445 }
1446 
1447 void KFilePropsPlugin::slotSizeStop()
1448 {
1449  if (d->dirSizeJob) {
1450  KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
1451  d->m_sizeLabel->setText(i18n("At least %1",
1452  KIO::convertSize(totalSize)));
1453  d->dirSizeJob->kill();
1454  d->dirSizeJob = nullptr;
1455  }
1456  if (d->dirSizeUpdateTimer) {
1457  d->dirSizeUpdateTimer->stop();
1458  }
1459 
1460  d->m_sizeStopButton->setEnabled(false);
1461  d->m_sizeDetermineButton->setEnabled(true);
1462 }
1463 
1464 void KFilePropsPlugin::slotSizeDetails()
1465 {
1466  // Open the current folder in filelight
1467  KService::Ptr service = KService::serviceByDesktopName(QStringLiteral("org.kde.filelight"));
1468  if (service) {
1469  auto *job = new KIO::ApplicationLauncherJob(service);
1470  job->setUrls({ properties->url() });
1472  job->start();
1473  }
1474 }
1475 
1476 KFilePropsPlugin::~KFilePropsPlugin()
1477 {
1478  delete d;
1479 }
1480 
1481 bool KFilePropsPlugin::supports(const KFileItemList & /*_items*/)
1482 {
1483  return true;
1484 }
1485 
1486 void KFilePropsPlugin::applyChanges()
1487 {
1488  if (d->dirSizeJob) {
1489  slotSizeStop();
1490  }
1491 
1492  // qDebug() << "KFilePropsPlugin::applyChanges";
1493 
1494  if (d->m_lined) {
1495  QString n = d->m_lined->text();
1496  // Remove trailing spaces (#4345)
1497  while (! n.isEmpty() && n[n.length() - 1].isSpace()) {
1498  n.chop(1);
1499  }
1500  if (n.isEmpty()) {
1501  KMessageBox::sorry(properties, i18n("The new file name is empty."));
1503  return;
1504  }
1505 
1506  // Do we need to rename the file ?
1507  // qDebug() << "oldname = " << d->oldName;
1508  // qDebug() << "newname = " << n;
1509  if (d->oldName != n || d->m_bFromTemplate) { // true for any from-template file
1510  KIO::CopyJob *job = nullptr;
1511  QUrl oldurl = properties->url();
1512 
1513  QString newFileName = KIO::encodeFileName(n);
1514  if (d->bDesktopFile && !newFileName.endsWith(QLatin1String(".desktop")) &&
1515  !newFileName.endsWith(QLatin1String(".kdelnk"))) {
1516  newFileName += QLatin1String(".desktop");
1517  }
1518 
1519  // Tell properties. Warning, this changes the result of properties->url() !
1520  properties->rename(newFileName);
1521 
1522  // Update also relative path (for apps)
1523  if (!d->m_sRelativePath.isEmpty()) {
1524  determineRelativePath(properties->url().toLocalFile());
1525  }
1526 
1527  // qDebug() << "New URL = " << properties->url();
1528  // qDebug() << "old = " << oldurl.url();
1529 
1530  // Don't remove the template !!
1531  if (!d->m_bFromTemplate) { // (normal renaming)
1532  job = KIO::moveAs(oldurl, properties->url());
1533  } else { // Copying a template
1534  job = KIO::copyAs(oldurl, properties->url());
1535  }
1536 
1537  connect(job, &KJob::result,
1538  this, &KFilePropsPlugin::slotCopyFinished);
1540  this, &KFilePropsPlugin::slotFileRenamed);
1541  // wait for job
1542  QEventLoop eventLoop;
1543  connect(this, &KFilePropsPlugin::leaveModality,
1544  &eventLoop, &QEventLoop::quit);
1546  return;
1547  }
1549  // Update also relative path (for apps)
1550  if (!d->m_sRelativePath.isEmpty()) {
1551  determineRelativePath(properties->url().toLocalFile());
1552  }
1553  }
1554 
1555  // No job, keep going
1556  slotCopyFinished(nullptr);
1557 }
1558 
1559 void KFilePropsPlugin::slotCopyFinished(KJob *job)
1560 {
1561  // qDebug() << "KFilePropsPlugin::slotCopyFinished";
1562  if (job) {
1563  // allow apply() to return
1564  emit leaveModality();
1565  if (job->error()) {
1566  job->uiDelegate()->showErrorMessage();
1567  // Didn't work. Revert the URL to the old one
1568  properties->updateUrl(static_cast<KIO::CopyJob *>(job)->srcUrls().constFirst());
1569  properties->abortApplying(); // Don't apply the changes to the wrong file !
1570  return;
1571  }
1572  }
1573 
1574  Q_ASSERT(!properties->item().isNull());
1575  Q_ASSERT(!properties->item().url().isEmpty());
1576 
1577  // Save the file locally
1578  if (d->bDesktopFile && !d->m_sRelativePath.isEmpty()) {
1579  // qDebug() << "KFilePropsPlugin::slotCopyFinished " << d->m_sRelativePath;
1581  const QUrl newURL = QUrl::fromLocalFile(newPath);
1582  // qDebug() << "KFilePropsPlugin::slotCopyFinished path=" << newURL;
1583  properties->updateUrl(newURL);
1584  }
1585 
1586  if (d->bKDesktopMode && d->bDesktopFile) {
1587  // Renamed? Update Name field
1588  // Note: The desktop ioslave does this as well, but not when
1589  // the file is copied from a template.
1590  if (d->m_bFromTemplate) {
1591  KIO::StatJob *job = KIO::stat(properties->url());
1592  job->exec();
1593  KIO::UDSEntry entry = job->statResult();
1594 
1595  KFileItem item(entry, properties->url());
1596  KDesktopFile config(item.localPath());
1597  KConfigGroup cg = config.desktopGroup();
1598  QString nameStr = nameFromFileName(properties->url().fileName());
1599  cg.writeEntry("Name", nameStr);
1600  cg.writeEntry("Name", nameStr, KConfigGroup::Persistent | KConfigGroup::Localized);
1601  }
1602  }
1603 
1604  if (d->m_linkTargetLineEdit && !d->bMultiple) {
1605  const KFileItem item = properties->item();
1606  const QString newTarget = d->m_linkTargetLineEdit->text();
1607  if (newTarget != item.linkDest()) {
1608  // qDebug() << "Updating target of symlink to" << newTarget;
1609  KIO::Job *job = KIO::symlink(newTarget, item.url(), KIO::Overwrite);
1611  job->exec();
1612  }
1613  }
1614 
1615  // "Link to Application" templates need to be made executable
1616  // Instead of matching against a filename we check if the destination
1617  // is an Application now.
1618  if (d->m_bFromTemplate) {
1619  // destination is not necessarily local, use the src template
1620  KDesktopFile templateResult(static_cast<KIO::CopyJob *>(job)->srcUrls().constFirst().toLocalFile());
1621  if (templateResult.hasApplicationType()) {
1622  // We can either stat the file and add the +x bit or use the larger chmod() job
1623  // with a umask designed to only touch u+x. This is only one KIO job, so let's
1624  // do that.
1625 
1626  KFileItem appLink(properties->item());
1627  KFileItemList fileItemList;
1628  fileItemList << appLink;
1629 
1630  // first 0100 adds u+x, second 0100 only allows chmod to change u+x
1631  KIO::Job *chmodJob = KIO::chmod(fileItemList, 0100, 0100, QString(), QString(), KIO::HideProgressInfo);
1632  chmodJob->exec();
1633  }
1634  }
1635 }
1636 
1637 void KFilePropsPlugin::applyIconChanges()
1638 {
1639  KIconButton *iconButton = qobject_cast<KIconButton *>(d->iconArea);
1640  if (!iconButton || !d->bIconChanged) {
1641  return;
1642  }
1643  // handle icon changes - only local files (or pseudo-local) for now
1644  // TODO: Use KTempFile and KIO::file_copy with overwrite = true
1645  QUrl url = properties->url();
1646  KIO::StatJob *job = KIO::mostLocalUrl(url);
1648  job->exec();
1649  url = job->mostLocalUrl();
1650 
1651  if (url.isLocalFile()) {
1652  QString path;
1653 
1654  if ((properties->item().mode() & QT_STAT_MASK) == QT_STAT_DIR) {
1655  path = url.toLocalFile() + QLatin1String("/.directory");
1656  // don't call updateUrl because the other tabs (i.e. permissions)
1657  // apply to the directory, not the .directory file.
1658  } else {
1659  path = url.toLocalFile();
1660  }
1661 
1662  // Get the default image
1663  QMimeDatabase db;
1665  // Is it another one than the default ?
1666  QString sIcon;
1667  if (str != iconButton->icon()) {
1668  sIcon = iconButton->icon();
1669  }
1670  // (otherwise write empty value)
1671 
1672  // qDebug() << "**" << path << "**";
1673 
1674  // If default icon and no .directory file -> don't create one
1675  if (!sIcon.isEmpty() || QFile::exists(path)) {
1676  KDesktopFile cfg(path);
1677  // qDebug() << "sIcon = " << (sIcon);
1678  // qDebug() << "str = " << (str);
1679  cfg.desktopGroup().writeEntry("Icon", sIcon);
1680  cfg.sync();
1681 
1682  cfg.reparseConfiguration();
1683  if (cfg.desktopGroup().readEntry("Icon") != sIcon) {
1684  KMessageBox::sorry(nullptr, i18n("<qt>Could not save properties. You do not "
1685  "have sufficient access to write to <b>%1</b>.</qt>", path));
1686  }
1687  }
1688  }
1689 }
1690 
1691 void KFilePropsPlugin::slotFileRenamed(KIO::Job *, const QUrl &, const QUrl &newUrl)
1692 {
1693  // This is called in case of an existing local file during the copy/move operation,
1694  // if the user chooses Rename.
1695  properties->updateUrl(newUrl);
1696 }
1697 
1698 void KFilePropsPlugin::postApplyChanges()
1699 {
1700  // Save the icon only after applying the permissions changes (#46192)
1701  applyIconChanges();
1702 
1703  const KFileItemList items = properties->items();
1704  const QList<QUrl> lst = items.urlList();
1705  org::kde::KDirNotify::emitFilesChanged(QList<QUrl>(lst));
1706 }
1707 
1708 class KFilePermissionsPropsPlugin::KFilePermissionsPropsPluginPrivate
1709 {
1710 public:
1711  KFilePermissionsPropsPluginPrivate()
1712  {
1713  }
1714  ~KFilePermissionsPropsPluginPrivate()
1715  {
1716  }
1717 
1718  QFrame *m_frame;
1719  QCheckBox *cbRecursive;
1720  QLabel *explanationLabel;
1721  KComboBox *ownerPermCombo, *groupPermCombo, *othersPermCombo;
1722  QCheckBox *extraCheckbox;
1723  mode_t partialPermissions;
1724  KFilePermissionsPropsPlugin::PermissionsMode pmode;
1725  bool canChangePermissions;
1726  bool isIrregular;
1727  bool hasExtendedACL;
1728  KACL extendedACL;
1729  KACL defaultACL;
1730  bool fileSystemSupportsACLs;
1731 
1732  KComboBox *grpCombo;
1733 
1734  KLineEdit *usrEdit;
1735  KLineEdit *grpEdit;
1736 
1737  // Old permissions
1738  mode_t permissions;
1739  // Old group
1740  QString strGroup;
1741  // Old owner
1742  QString strOwner;
1743 };
1744 
1745 #define UniOwner (S_IRUSR|S_IWUSR|S_IXUSR)
1746 #define UniGroup (S_IRGRP|S_IWGRP|S_IXGRP)
1747 #define UniOthers (S_IROTH|S_IWOTH|S_IXOTH)
1748 #define UniRead (S_IRUSR|S_IRGRP|S_IROTH)
1749 #define UniWrite (S_IWUSR|S_IWGRP|S_IWOTH)
1750 #define UniExec (S_IXUSR|S_IXGRP|S_IXOTH)
1751 #define UniSpecial (S_ISUID|S_ISGID|S_ISVTX)
1752 
1753 // synced with PermissionsTarget
1754 const mode_t KFilePermissionsPropsPlugin::permissionsMasks[3] = {UniOwner, UniGroup, UniOthers};
1755 const mode_t KFilePermissionsPropsPlugin::standardPermissions[4] = { 0, UniRead, UniRead | UniWrite, (mode_t) - 1 };
1756 
1757 // synced with PermissionsMode and standardPermissions
1758 const char *const KFilePermissionsPropsPlugin::permissionsTexts[4][4] = {
1759  {
1760  I18N_NOOP("No Access"),
1761  I18N_NOOP("Can Only View"),
1762  I18N_NOOP("Can View & Modify"),
1763  nullptr
1764  },
1765  {
1766  I18N_NOOP("No Access"),
1767  I18N_NOOP("Can Only View Content"),
1768  I18N_NOOP("Can View & Modify Content"),
1769  nullptr
1770  },
1771  { nullptr, nullptr, nullptr, nullptr}, // no texts for links
1772  {
1773  I18N_NOOP("No Access"),
1774  I18N_NOOP("Can Only View/Read Content"),
1775  I18N_NOOP("Can View/Read & Modify/Write"),
1776  nullptr
1777  }
1778 };
1779 
1780 KFilePermissionsPropsPlugin::KFilePermissionsPropsPlugin(KPropertiesDialog *_props)
1781  : KPropertiesDialogPlugin(_props), d(new KFilePermissionsPropsPluginPrivate)
1782 {
1783  d->cbRecursive = nullptr;
1784  d->grpCombo = nullptr; d->grpEdit = nullptr;
1785  d->usrEdit = nullptr;
1786  bool isLocal = properties->url().isLocalFile();
1787  bool isTrash = (properties->url().scheme() == QLatin1String("trash"));
1788  KUser myself(KUser::UseEffectiveUID);
1789  const bool IamRoot = myself.isSuperUser();
1790 
1791  const KFileItem item = properties->item();
1792  bool isLink = item.isLink();
1793  bool isDir = item.isDir(); // all dirs
1794  bool hasDir = item.isDir(); // at least one dir
1795  d->permissions = item.permissions(); // common permissions to all files
1796  d->partialPermissions = d->permissions; // permissions that only some files have (at first we take everything)
1797  d->isIrregular = isIrregular(d->permissions, isDir, isLink);
1798  d->strOwner = item.user();
1799  d->strGroup = item.group();
1800  d->hasExtendedACL = item.ACL().isExtended() || item.defaultACL().isValid();
1801  d->extendedACL = item.ACL();
1802  d->defaultACL = item.defaultACL();
1803  d->fileSystemSupportsACLs = false;
1804 
1805  if (properties->items().count() > 1) {
1806  // Multiple items: see what they have in common
1807  const KFileItemList items = properties->items();
1808  KFileItemList::const_iterator it = items.begin();
1809  const KFileItemList::const_iterator kend = items.end();
1810  for (++it /*no need to check the first one again*/; it != kend; ++it) {
1811  if (!d->isIrregular)
1812  d->isIrregular |= isIrregular((*it).permissions(),
1813  (*it).isDir() == isDir,
1814  (*it).isLink() == isLink);
1815  d->hasExtendedACL = d->hasExtendedACL || (*it).hasExtendedACL();
1816  if ((*it).isLink() != isLink) {
1817  isLink = false;
1818  }
1819  if ((*it).isDir() != isDir) {
1820  isDir = false;
1821  }
1822  hasDir |= (*it).isDir();
1823  if ((*it).permissions() != d->permissions) {
1824  d->permissions &= (*it).permissions();
1825  d->partialPermissions |= (*it).permissions();
1826  }
1827  if ((*it).user() != d->strOwner) {
1828  d->strOwner.clear();
1829  }
1830  if ((*it).group() != d->strGroup) {
1831  d->strGroup.clear();
1832  }
1833  }
1834  }
1835 
1836  if (isLink) {
1837  d->pmode = PermissionsOnlyLinks;
1838  } else if (isDir) {
1839  d->pmode = PermissionsOnlyDirs;
1840  } else if (hasDir) {
1841  d->pmode = PermissionsMixed;
1842  } else {
1843  d->pmode = PermissionsOnlyFiles;
1844  }
1845 
1846  // keep only what's not in the common permissions
1847  d->partialPermissions = d->partialPermissions & ~d->permissions;
1848 
1849  bool isMyFile = false;
1850 
1851  if (isLocal && !d->strOwner.isEmpty()) { // local files, and all owned by the same person
1852  if (myself.isValid()) {
1853  isMyFile = (d->strOwner == myself.loginName());
1854  } else {
1855  qCWarning(KIO_WIDGETS) << "I don't exist ?! geteuid=" << KUserId::currentEffectiveUserId().toString();
1856  }
1857  } else {
1858  //We don't know, for remote files, if they are ours or not.
1859  //So we let the user change permissions, and
1860  //KIO::chmod will tell, if he had no right to do it.
1861  isMyFile = true;
1862  }
1863 
1864  d->canChangePermissions = (isMyFile || IamRoot) && (!isLink);
1865 
1866  // create GUI
1867 
1868  d->m_frame = new QFrame();
1869  properties->addPage(d->m_frame, i18n("&Permissions"));
1870 
1871  QBoxLayout *box = new QVBoxLayout(d->m_frame);
1872  box->setContentsMargins(0, 0, 0, 0);
1873 
1874  QWidget *l;
1875  QLabel *lbl;
1876  QGroupBox *gb;
1877  QGridLayout *gl;
1878  QPushButton *pbAdvancedPerm = nullptr;
1879 
1880  /* Group: Access Permissions */
1881  gb = new QGroupBox(i18n("Access Permissions"), d->m_frame);
1882  box->addWidget(gb);
1883 
1884  gl = new QGridLayout(gb);
1885  gl->setColumnStretch(1, 1);
1886 
1887  l = d->explanationLabel = new QLabel(gb);
1888  if (isLink)
1889  d->explanationLabel->setText(i18np("This file is a link and does not have permissions.",
1890  "All files are links and do not have permissions.",
1891  properties->items().count()));
1892  else if (!d->canChangePermissions) {
1893  d->explanationLabel->setText(i18n("Only the owner can change permissions."));
1894  }
1895  gl->addWidget(l, 0, 0, 1, 2);
1896 
1897  lbl = new QLabel(i18n("O&wner:"), gb);
1898  gl->addWidget(lbl, 1, 0, Qt::AlignRight);
1899  l = d->ownerPermCombo = new KComboBox(gb);
1900  lbl->setBuddy(l);
1901  gl->addWidget(l, 1, 1);
1902  connect(d->ownerPermCombo, QOverload<int>::of(&QComboBox::activated),
1904  l->setWhatsThis(i18n("Specifies the actions that the owner is allowed to do."));
1905 
1906  lbl = new QLabel(i18n("Gro&up:"), gb);
1907  gl->addWidget(lbl, 2, 0, Qt::AlignRight);
1908  l = d->groupPermCombo = new KComboBox(gb);
1909  lbl->setBuddy(l);
1910  gl->addWidget(l, 2, 1);
1911  connect(d->groupPermCombo, QOverload<int>::of(&QComboBox::activated),
1913  l->setWhatsThis(i18n("Specifies the actions that the members of the group are allowed to do."));
1914 
1915  lbl = new QLabel(i18n("O&thers:"), gb);
1916  gl->addWidget(lbl, 3, 0, Qt::AlignRight);
1917  l = d->othersPermCombo = new KComboBox(gb);
1918  lbl->setBuddy(l);
1919  gl->addWidget(l, 3, 1);
1920  connect(d->othersPermCombo, QOverload<int>::of(&QComboBox::activated),
1922  l->setWhatsThis(i18n("Specifies the actions that all users, who are neither "
1923  "owner nor in the group, are allowed to do."));
1924 
1925  if (!isLink) {
1926  l = d->extraCheckbox = new QCheckBox(hasDir ?
1927  i18n("Only own&er can rename and delete folder content") :
1928  i18n("Is &executable"),
1929  gb);
1931  gl->addWidget(l, 4, 1);
1932  l->setWhatsThis(hasDir ? i18n("Enable this option to allow only the folder's owner to "
1933  "delete or rename the contained files and folders. Other "
1934  "users can only add new files, which requires the 'Modify "
1935  "Content' permission.")
1936  : i18n("Enable this option to mark the file as executable. This only makes "
1937  "sense for programs and scripts. It is required when you want to "
1938  "execute them."));
1939 
1941  gl->addItem(spacer, 5, 0, 1, 3);
1942 
1943  pbAdvancedPerm = new QPushButton(i18n("A&dvanced Permissions"), gb);
1944  gl->addWidget(pbAdvancedPerm, 6, 0, 1, 2, Qt::AlignRight);
1945  connect(pbAdvancedPerm, &QAbstractButton::clicked,
1946  this, &KFilePermissionsPropsPlugin::slotShowAdvancedPermissions);
1947  } else {
1948  d->extraCheckbox = nullptr;
1949  }
1950 
1951  /**** Group: Ownership ****/
1952  gb = new QGroupBox(i18n("Ownership"), d->m_frame);
1953  box->addWidget(gb);
1954 
1955  gl = new QGridLayout(gb);
1956  gl->addItem(new QSpacerItem(0, 10), 0, 0);
1957 
1958  /*** Set Owner ***/
1959  l = new QLabel(i18n("User:"), gb);
1960  gl->addWidget(l, 1, 0, Qt::AlignRight);
1961 
1962  /* GJ: Don't autocomplete more than 1000 users. This is a kind of random
1963  * value. Huge sites having 10.000+ user have a fair chance of using NIS,
1964  * (possibly) making this unacceptably slow.
1965  * OTOH, it is nice to offer this functionality for the standard user.
1966  */
1967  int maxEntries = 1000;
1968 
1969  /* File owner: For root, offer a KLineEdit with autocompletion.
1970  * For a user, who can never chown() a file, offer a QLabel.
1971  */
1972  if (IamRoot && isLocal) {
1973  d->usrEdit = new KLineEdit(gb);
1974  KCompletion *kcom = d->usrEdit->completionObject();
1976  QStringList userNames = KUser::allUserNames(maxEntries);
1977  kcom->setItems(userNames);
1978  d->usrEdit->setCompletionMode((userNames.size() < maxEntries) ? KCompletion::CompletionAuto :
1980  d->usrEdit->setText(d->strOwner);
1981  gl->addWidget(d->usrEdit, 1, 1);
1982  connect(d->usrEdit, &QLineEdit::textChanged,
1984  } else {
1985  l = new QLabel(d->strOwner, gb);
1986  gl->addWidget(l, 1, 1);
1987  }
1988 
1989  /*** Set Group ***/
1990 
1992  QStringList groupList = user.groupNames();
1993  const bool isMyGroup = groupList.contains(d->strGroup);
1994 
1995  /* add the group the file currently belongs to ..
1996  * .. if it is not there already
1997  */
1998  if (!isMyGroup) {
1999  groupList += d->strGroup;
2000  }
2001 
2002  l = new QLabel(i18n("Group:"), gb);
2003  gl->addWidget(l, 2, 0, Qt::AlignRight);
2004 
2005  /* Set group: if possible to change:
2006  * - Offer a KLineEdit for root, since he can change to any group.
2007  * - Offer a KComboBox for a normal user, since he can change to a fixed
2008  * (small) set of groups only.
2009  * If not changeable: offer a QLabel.
2010  */
2011  if (IamRoot && isLocal) {
2012  d->grpEdit = new KLineEdit(gb);
2013  KCompletion *kcom = new KCompletion;
2014  kcom->setItems(groupList);
2015  d->grpEdit->setCompletionObject(kcom, true);
2016  d->grpEdit->setAutoDeleteCompletionObject(true);
2017  d->grpEdit->setCompletionMode(KCompletion::CompletionAuto);
2018  d->grpEdit->setText(d->strGroup);
2019  gl->addWidget(d->grpEdit, 2, 1);
2020  connect(d->grpEdit, &QLineEdit::textChanged,
2022  } else if ((groupList.count() > 1) && isMyFile && isLocal) {
2023  d->grpCombo = new KComboBox(gb);
2024  d->grpCombo->setObjectName(QStringLiteral("combogrouplist"));
2025  d->grpCombo->addItems(groupList);
2026  d->grpCombo->setCurrentIndex(groupList.indexOf(d->strGroup));
2027  gl->addWidget(d->grpCombo, 2, 1);
2028  connect(d->grpCombo, QOverload<int>::of(&QComboBox::activated),
2030  } else {
2031  l = new QLabel(d->strGroup, gb);
2032  gl->addWidget(l, 2, 1);
2033  }
2034 
2035  gl->setColumnStretch(2, 10);
2036 
2037  // "Apply recursive" checkbox
2038  if (hasDir && !isLink && !isTrash) {
2039  d->cbRecursive = new QCheckBox(i18n("Apply changes to all subfolders and their contents"), d->m_frame);
2041  box->addWidget(d->cbRecursive);
2042  }
2043 
2044  updateAccessControls();
2045 
2046  if (isTrash) {
2047  //don't allow to change properties for file into trash
2048  enableAccessControls(false);
2049  if (pbAdvancedPerm) {
2050  pbAdvancedPerm->setEnabled(false);
2051  }
2052  }
2053 
2054  box->addStretch(10);
2055 }
2056 
2057 #if HAVE_POSIX_ACL
2058 static bool fileSystemSupportsACL(const QByteArray &path)
2059 {
2060  bool fileSystemSupportsACLs = false;
2061 #ifdef Q_OS_FREEBSD
2062  struct statfs buf;
2063  fileSystemSupportsACLs = (statfs(path.data(), &buf) == 0) && (buf.f_flags & MNT_ACLS);
2064 #elif defined Q_OS_MACOS
2065  fileSystemSupportsACLs =
2066  getxattr(path.data(), "system.posix_acl_access", nullptr, 0, 0, XATTR_NOFOLLOW) >= 0 || errno == ENODATA;
2067 #else
2068  fileSystemSupportsACLs =
2069  getxattr(path.data(), "system.posix_acl_access", nullptr, 0) >= 0 || errno == ENODATA;
2070 #endif
2071  return fileSystemSupportsACLs;
2072 }
2073 #endif
2074 
2075 void KFilePermissionsPropsPlugin::slotShowAdvancedPermissions()
2076 {
2077 
2078  bool isDir = (d->pmode == PermissionsOnlyDirs) || (d->pmode == PermissionsMixed);
2079  QDialog dlg(properties);
2080  dlg.setModal(true);
2081  dlg.setWindowTitle(i18n("Advanced Permissions"));
2082 
2083  QLabel *l, *cl[3];
2084  QGroupBox *gb;
2085  QGridLayout *gl;
2086 
2087  QVBoxLayout *vbox = new QVBoxLayout;
2088  dlg.setLayout(vbox);
2089  // Group: Access Permissions
2090  gb = new QGroupBox(i18n("Access Permissions"), &dlg);
2091  vbox->addWidget(gb);
2092 
2093  gl = new QGridLayout(gb);
2094  gl->addItem(new QSpacerItem(0, 10), 0, 0);
2095 
2096  QVector<QWidget *> theNotSpecials;
2097 
2098  l = new QLabel(i18n("Class"), gb);
2099  gl->addWidget(l, 1, 0);
2100  theNotSpecials.append(l);
2101 
2102  QString readWhatsThis;
2103  QString readLabel;
2104  if (isDir) {
2105  readLabel = i18n("Show\nEntries");
2106  readWhatsThis = i18n("This flag allows viewing the content of the folder.");
2107  } else {
2108  readLabel = i18n("Read");
2109  readWhatsThis = i18n("The Read flag allows viewing the content of the file.");
2110  }
2111 
2112  QString writeWhatsThis;
2113  QString writeLabel;
2114  if (isDir) {
2115  writeLabel = i18n("Write\nEntries");
2116  writeWhatsThis = i18n("This flag allows adding, renaming and deleting of files. "
2117  "Note that deleting and renaming can be limited using the Sticky flag.");
2118  } else {
2119  writeLabel = i18n("Write");
2120  writeWhatsThis = i18n("The Write flag allows modifying the content of the file.");
2121  }
2122 
2123  QString execLabel;
2124  QString execWhatsThis;
2125  if (isDir) {
2126  execLabel = i18nc("Enter folder", "Enter");
2127  execWhatsThis = i18n("Enable this flag to allow entering the folder.");
2128  } else {
2129  execLabel = i18n("Exec");
2130  execWhatsThis = i18n("Enable this flag to allow executing the file as a program.");
2131  }
2132  // GJ: Add space between normal and special modes
2133  QSize size = l->sizeHint();
2134  size.setWidth(size.width() + 15);
2135  l->setFixedSize(size);
2136  gl->addWidget(l, 1, 3);
2137 
2138  l = new QLabel(i18n("Special"), gb);
2139  gl->addWidget(l, 1, 4, 1, 1);
2140  QString specialWhatsThis;
2141  if (isDir)
2142  specialWhatsThis = i18n("Special flag. Valid for the whole folder, the exact "
2143  "meaning of the flag can be seen in the right hand column.");
2144  else
2145  specialWhatsThis = i18n("Special flag. The exact meaning of the flag can be seen "
2146  "in the right hand column.");
2147  l->setWhatsThis(specialWhatsThis);
2148 
2149  cl[0] = new QLabel(i18n("User"), gb);
2150  gl->addWidget(cl[0], 2, 0);
2151  theNotSpecials.append(cl[0]);
2152 
2153  cl[1] = new QLabel(i18n("Group"), gb);
2154  gl->addWidget(cl[1], 3, 0);
2155  theNotSpecials.append(cl[1]);
2156 
2157  cl[2] = new QLabel(i18n("Others"), gb);
2158  gl->addWidget(cl[2], 4, 0);
2159  theNotSpecials.append(cl[2]);
2160 
2161  QString setUidWhatsThis;
2162  if (isDir)
2163  setUidWhatsThis = i18n("If this flag is set, the owner of this folder will be "
2164  "the owner of all new files.");
2165  else
2166  setUidWhatsThis = i18n("If this file is an executable and the flag is set, it will "
2167  "be executed with the permissions of the owner.");
2168 
2169  QString setGidWhatsThis;
2170  if (isDir)
2171  setGidWhatsThis = i18n("If this flag is set, the group of this folder will be "
2172  "set for all new files.");
2173  else
2174  setGidWhatsThis = i18n("If this file is an executable and the flag is set, it will "
2175  "be executed with the permissions of the group.");
2176 
2177  QString stickyWhatsThis;
2178  if (isDir)
2179  stickyWhatsThis = i18n("If the Sticky flag is set on a folder, only the owner "
2180  "and root can delete or rename files. Otherwise everybody "
2181  "with write permissions can do this.");
2182  else
2183  stickyWhatsThis = i18n("The Sticky flag on a file is ignored on Linux, but may "
2184  "be used on some systems");
2185  mode_t aPermissions, aPartialPermissions;
2186  mode_t dummy1, dummy2;
2187 
2188  if (!d->isIrregular) {
2189  switch (d->pmode) {
2190  case PermissionsOnlyFiles:
2191  getPermissionMasks(aPartialPermissions,
2192  dummy1,
2193  aPermissions,
2194  dummy2);
2195  break;
2196  case PermissionsOnlyDirs:
2197  case PermissionsMixed:
2198  getPermissionMasks(dummy1,
2199  aPartialPermissions,
2200  dummy2,
2201  aPermissions);
2202  break;
2203  case PermissionsOnlyLinks:
2204  aPermissions = UniRead | UniWrite | UniExec | UniSpecial;
2205  aPartialPermissions = 0;
2206  break;
2207  }
2208  } else {
2209  aPermissions = d->permissions;
2210  aPartialPermissions = d->partialPermissions;
2211  }
2212 
2213  // Draw Checkboxes
2214  QCheckBox *cba[3][4];
2215  for (int row = 0; row < 3; ++row) {
2216  for (int col = 0; col < 4; ++col) {
2217  QCheckBox *cb = new QCheckBox(gb);
2218  if (col != 3) {
2219  theNotSpecials.append(cb);
2220  }
2221  cba[row][col] = cb;
2222  cb->setChecked(aPermissions & fperm[row][col]);
2223  if (aPartialPermissions & fperm[row][col]) {
2224  cb->setTristate();
2226  } else if (d->cbRecursive && d->cbRecursive->isChecked()) {
2227  cb->setTristate();
2228  }
2229 
2230  cb->setEnabled(d->canChangePermissions);
2231  gl->addWidget(cb, row + 2, col + 1);
2232  switch (col) {
2233  case 0:
2234  cb->setText(readLabel);
2235  cb->setWhatsThis(readWhatsThis);
2236  break;
2237  case 1:
2238  cb->setText(writeLabel);
2239  cb->setWhatsThis(writeWhatsThis);
2240  break;
2241  case 2:
2242  cb->setText(execLabel);
2243  cb->setWhatsThis(execWhatsThis);
2244  break;
2245  case 3:
2246  switch (row) {
2247  case 0:
2248  cb->setText(i18n("Set UID"));
2249  cb->setWhatsThis(setUidWhatsThis);
2250  break;
2251  case 1:
2252  cb->setText(i18n("Set GID"));
2253  cb->setWhatsThis(setGidWhatsThis);
2254  break;
2255  case 2:
2256  cb->setText(i18nc("File permission", "Sticky"));
2257  cb->setWhatsThis(stickyWhatsThis);
2258  break;
2259  }
2260  break;
2261  }
2262  }
2263  }
2264  gl->setColumnStretch(6, 10);
2265 
2266 #if HAVE_POSIX_ACL
2267  KACLEditWidget *extendedACLs = nullptr;
2268 
2269  // FIXME make it work with partial entries
2270  if (properties->items().count() == 1) {
2272  d->fileSystemSupportsACLs = fileSystemSupportsACL(path);
2273  }
2274  if (d->fileSystemSupportsACLs) {
2275  std::for_each(theNotSpecials.begin(), theNotSpecials.end(), std::mem_fun(&QWidget::hide));
2276  extendedACLs = new KACLEditWidget(&dlg);
2277  extendedACLs->setEnabled(d->canChangePermissions);
2278  vbox->addWidget(extendedACLs);
2279  if (d->extendedACL.isValid() && d->extendedACL.isExtended()) {
2280  extendedACLs->setACL(d->extendedACL);
2281  } else {
2282  extendedACLs->setACL(KACL(aPermissions));
2283  }
2284 
2285  if (d->defaultACL.isValid()) {
2286  extendedACLs->setDefaultACL(d->defaultACL);
2287  }
2288 
2289  if (properties->items().constFirst().isDir()) {
2290  extendedACLs->setAllowDefaults(true);
2291  }
2292  }
2293 #endif
2294 
2295  QDialogButtonBox *buttonBox = new QDialogButtonBox(&dlg);
2297  connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
2298  connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
2299  vbox->addWidget(buttonBox);
2300 
2301  if (dlg.exec() != QDialog::Accepted) {
2302  return;
2303  }
2304 
2305  mode_t andPermissions = mode_t(~0);
2306  mode_t orPermissions = 0;
2307  for (int row = 0; row < 3; ++row)
2308  for (int col = 0; col < 4; ++col) {
2309  switch (cba[row][col]->checkState()) {
2310  case Qt::Checked:
2311  orPermissions |= fperm[row][col];
2312  //fall through
2313  case Qt::Unchecked:
2314  andPermissions &= ~fperm[row][col];
2315  break;
2316  case Qt::PartiallyChecked:
2317  break;
2318  }
2319  }
2320 
2321  d->isIrregular = false;
2322  const KFileItemList items = properties->items();
2323  KFileItemList::const_iterator it = items.begin();
2324  const KFileItemList::const_iterator kend = items.end();
2325  for (; it != kend; ++it) {
2326  if (isIrregular(((*it).permissions() & andPermissions) | orPermissions,
2327  (*it).isDir(), (*it).isLink())) {
2328  d->isIrregular = true;
2329  break;
2330  }
2331  }
2332 
2333  d->permissions = orPermissions;
2334  d->partialPermissions = andPermissions;
2335 
2336 #if HAVE_POSIX_ACL
2337  // override with the acls, if present
2338  if (extendedACLs) {
2339  d->extendedACL = extendedACLs->getACL();
2340  d->defaultACL = extendedACLs->getDefaultACL();
2341  d->hasExtendedACL = d->extendedACL.isExtended() || d->defaultACL.isValid();
2342  d->permissions = d->extendedACL.basePermissions();
2343  d->permissions |= (andPermissions | orPermissions) & (S_ISUID | S_ISGID | S_ISVTX);
2344  }
2345 #endif
2346 
2347  updateAccessControls();
2348  emit changed();
2349 }
2350 
2351 // QString KFilePermissionsPropsPlugin::tabName () const
2352 // {
2353 // return i18n ("&Permissions");
2354 // }
2355 
2356 KFilePermissionsPropsPlugin::~KFilePermissionsPropsPlugin()
2357 {
2358  delete d;
2359 }
2360 
2361 bool KFilePermissionsPropsPlugin::supports(const KFileItemList & /*_items*/)
2362 {
2363  return true;
2364 }
2365 
2366 // sets a combo box in the Access Control frame
2367 void KFilePermissionsPropsPlugin::setComboContent(QComboBox *combo, PermissionsTarget target,
2368  mode_t permissions, mode_t partial)
2369 {
2370  combo->clear();
2371  if (d->isIrregular) { //#176876
2372  return;
2373  }
2374 
2375  if (d->pmode == PermissionsOnlyLinks) {
2376  combo->addItem(i18n("Link"));
2377  combo->setCurrentIndex(0);
2378  return;
2379  }
2380 
2381  mode_t tMask = permissionsMasks[target];
2382  int textIndex;
2383  for (textIndex = 0; standardPermissions[textIndex] != (mode_t) - 1; textIndex++) {
2384  if ((standardPermissions[textIndex]&tMask) == (permissions & tMask & (UniRead | UniWrite))) {
2385  break;
2386  }
2387  }
2388  Q_ASSERT(standardPermissions[textIndex] != (mode_t) - 1); // must not happen, would be irreglar
2389 
2390  for (int i = 0; permissionsTexts[(int)d->pmode][i]; i++) {
2391  combo->addItem(i18n(permissionsTexts[(int)d->pmode][i]));
2392  }
2393 
2394  if (partial & tMask & ~UniExec) {
2395  combo->addItem(i18n("Varying (No Change)"));
2396  combo->setCurrentIndex(3);
2397  } else {
2398  combo->setCurrentIndex(textIndex);
2399  }
2400 }
2401 
2402 // permissions are irregular if they cant be displayed in a combo box.
2403 bool KFilePermissionsPropsPlugin::isIrregular(mode_t permissions, bool isDir, bool isLink)
2404 {
2405  if (isLink) { // links are always ok
2406  return false;
2407  }
2408 
2409  mode_t p = permissions;
2410  if (p & (S_ISUID | S_ISGID)) { // setuid/setgid -> irregular
2411  return true;
2412  }
2413  if (isDir) {
2414  p &= ~S_ISVTX; // ignore sticky on dirs
2415 
2416  // check supported flag combinations
2417  mode_t p0 = p & UniOwner;
2418  if ((p0 != 0) && (p0 != (S_IRUSR | S_IXUSR)) && (p0 != UniOwner)) {
2419  return true;
2420  }
2421  p0 = p & UniGroup;
2422  if ((p0 != 0) && (p0 != (S_IRGRP | S_IXGRP)) && (p0 != UniGroup)) {
2423  return true;
2424  }
2425  p0 = p & UniOthers;
2426  if ((p0 != 0) && (p0 != (S_IROTH | S_IXOTH)) && (p0 != UniOthers)) {
2427  return true;
2428  }
2429  return false;
2430  }
2431  if (p & S_ISVTX) { // sticky on file -> irregular
2432  return true;
2433  }
2434 
2435  // check supported flag combinations
2436  mode_t p0 = p & UniOwner;
2437  bool usrXPossible = !p0; // true if this file could be an executable
2438  if (p0 & S_IXUSR) {
2439  if ((p0 == S_IXUSR) || (p0 == (S_IWUSR | S_IXUSR))) {
2440  return true;
2441  }
2442  usrXPossible = true;
2443  } else if (p0 == S_IWUSR) {
2444  return true;
2445  }
2446 
2447  p0 = p & UniGroup;
2448  bool grpXPossible = !p0; // true if this file could be an executable
2449  if (p0 & S_IXGRP) {
2450  if ((p0 == S_IXGRP) || (p0 == (S_IWGRP | S_IXGRP))) {
2451  return true;
2452  }
2453  grpXPossible = true;
2454  } else if (p0 == S_IWGRP) {
2455  return true;
2456  }
2457  if (p0 == 0) {
2458  grpXPossible = true;
2459  }
2460 
2461  p0 = p & UniOthers;
2462  bool othXPossible = !p0; // true if this file could be an executable
2463  if (p0 & S_IXOTH) {
2464  if ((p0 == S_IXOTH) || (p0 == (S_IWOTH | S_IXOTH))) {
2465  return true;
2466  }
2467  othXPossible = true;
2468  } else if (p0 == S_IWOTH) {
2469  return true;
2470  }
2471 
2472  // check that there either all targets are executable-compatible, or none
2473  return (p & UniExec) && !(usrXPossible && grpXPossible && othXPossible);
2474 }
2475 
2476 // enables/disabled the widgets in the Access Control frame
2477 void KFilePermissionsPropsPlugin::enableAccessControls(bool enable)
2478 {
2479  d->ownerPermCombo->setEnabled(enable);
2480  d->groupPermCombo->setEnabled(enable);
2481  d->othersPermCombo->setEnabled(enable);
2482  if (d->extraCheckbox) {
2483  d->extraCheckbox->setEnabled(enable);
2484  }
2485  if (d->cbRecursive) {
2486  d->cbRecursive->setEnabled(enable);
2487  }
2488 }
2489 
2490 // updates all widgets in the Access Control frame
2491 void KFilePermissionsPropsPlugin::updateAccessControls()
2492 {
2493  setComboContent(d->ownerPermCombo, PermissionsOwner,
2494  d->permissions, d->partialPermissions);
2495  setComboContent(d->groupPermCombo, PermissionsGroup,
2496  d->permissions, d->partialPermissions);
2497  setComboContent(d->othersPermCombo, PermissionsOthers,
2498  d->permissions, d->partialPermissions);
2499 
2500  switch (d->pmode) {
2501  case PermissionsOnlyLinks:
2502  enableAccessControls(false);
2503  break;
2504  case PermissionsOnlyFiles:
2505  enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
2506  if (d->canChangePermissions)
2507  d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
2508  i18np("This file uses advanced permissions",
2509  "These files use advanced permissions.",
2510  properties->items().count()) : QString());
2511  if (d->partialPermissions & UniExec) {
2512  d->extraCheckbox->setTristate();
2513  d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
2514  } else {
2515  d->extraCheckbox->setTristate(false);
2516  d->extraCheckbox->setChecked(d->permissions & UniExec);
2517  }
2518  break;
2519  case PermissionsOnlyDirs:
2520  enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
2521  // if this is a dir, and we can change permissions, don't dis-allow
2522  // recursive, we can do that for ACL setting.
2523  if (d->cbRecursive) {
2524  d->cbRecursive->setEnabled(d->canChangePermissions && !d->isIrregular);
2525  }
2526 
2527  if (d->canChangePermissions)
2528  d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
2529  i18np("This folder uses advanced permissions.",
2530  "These folders use advanced permissions.",
2531  properties->items().count()) : QString());
2532  if (d->partialPermissions & S_ISVTX) {
2533  d->extraCheckbox->setTristate();
2534  d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
2535  } else {
2536  d->extraCheckbox->setTristate(false);
2537  d->extraCheckbox->setChecked(d->permissions & S_ISVTX);
2538  }
2539  break;
2540  case PermissionsMixed:
2541  enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
2542  if (d->canChangePermissions)
2543  d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
2544  i18n("These files use advanced permissions.") : QString());
2545  if (d->partialPermissions & S_ISVTX) {
2546  d->extraCheckbox->setTristate();
2547  d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
2548  } else {
2549  d->extraCheckbox->setTristate(false);
2550  d->extraCheckbox->setChecked(d->permissions & S_ISVTX);
2551  }
2552  break;
2553  }
2554 }
2555 
2556 // gets masks for files and dirs from the Access Control frame widgets
2557 void KFilePermissionsPropsPlugin::getPermissionMasks(mode_t &andFilePermissions,
2558  mode_t &andDirPermissions,
2559  mode_t &orFilePermissions,
2560  mode_t &orDirPermissions)
2561 {
2562  andFilePermissions = mode_t(~UniSpecial);
2563  andDirPermissions = mode_t(~(S_ISUID | S_ISGID));
2564  orFilePermissions = 0;
2565  orDirPermissions = 0;
2566  if (d->isIrregular) {
2567  return;
2568  }
2569 
2570  mode_t m = standardPermissions[d->ownerPermCombo->currentIndex()];
2571  if (m != (mode_t) - 1) {
2572  orFilePermissions |= m & UniOwner;
2573  if ((m & UniOwner) &&
2574  ((d->pmode == PermissionsMixed) ||
2575  ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked)))) {
2576  andFilePermissions &= ~(S_IRUSR | S_IWUSR);
2577  } else {
2578  andFilePermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
2579  if ((m & S_IRUSR) && (d->extraCheckbox->checkState() == Qt::Checked)) {
2580  orFilePermissions |= S_IXUSR;
2581  }
2582  }
2583 
2584  orDirPermissions |= m & UniOwner;
2585  if (m & S_IRUSR) {
2586  orDirPermissions |= S_IXUSR;
2587  }
2588  andDirPermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
2589  }
2590 
2591  m = standardPermissions[d->groupPermCombo->currentIndex()];
2592  if (m != (mode_t) - 1) {
2593  orFilePermissions |= m & UniGroup;
2594  if ((m & UniGroup) &&
2595  ((d->pmode == PermissionsMixed) ||
2596  ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked)))) {
2597  andFilePermissions &= ~(S_IRGRP | S_IWGRP);
2598  } else {
2599  andFilePermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
2600  if ((m & S_IRGRP) && (d->extraCheckbox->checkState() == Qt::Checked)) {
2601  orFilePermissions |= S_IXGRP;
2602  }
2603  }
2604 
2605  orDirPermissions |= m & UniGroup;
2606  if (m & S_IRGRP) {
2607  orDirPermissions |= S_IXGRP;
2608  }
2609  andDirPermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
2610  }
2611 
2612  m = d->othersPermCombo->currentIndex() >= 0 ? standardPermissions[d->othersPermCombo->currentIndex()] : (mode_t) - 1;
2613  if (m != (mode_t) - 1) {
2614  orFilePermissions |= m & UniOthers;
2615  if ((m & UniOthers) &&
2616  ((d->pmode == PermissionsMixed) ||
2617  ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked)))) {
2618  andFilePermissions &= ~(S_IROTH | S_IWOTH);
2619  } else {
2620  andFilePermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH);
2621  if ((m & S_IROTH) && (d->extraCheckbox->checkState() == Qt::Checked)) {
2622  orFilePermissions |= S_IXOTH;
2623  }
2624  }
2625 
2626  orDirPermissions |= m & UniOthers;
2627  if (m & S_IROTH) {
2628  orDirPermissions |= S_IXOTH;
2629  }
2630  andDirPermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH);
2631  }
2632 
2633  if (((d->pmode == PermissionsMixed) || (d->pmode == PermissionsOnlyDirs)) &&
2634  (d->extraCheckbox->checkState() != Qt::PartiallyChecked)) {
2635  andDirPermissions &= ~S_ISVTX;
2636  if (d->extraCheckbox->checkState() == Qt::Checked) {
2637  orDirPermissions |= S_ISVTX;
2638  }
2639  }
2640 }
2641 
2642 void KFilePermissionsPropsPlugin::applyChanges()
2643 {
2644  mode_t orFilePermissions;
2645  mode_t orDirPermissions;
2646  mode_t andFilePermissions;
2647  mode_t andDirPermissions;
2648 
2649  if (!d->canChangePermissions) {
2650  return;
2651  }
2652 
2653  if (!d->isIrregular)
2654  getPermissionMasks(andFilePermissions,
2655  andDirPermissions,
2656  orFilePermissions,
2657  orDirPermissions);
2658  else {
2659  orFilePermissions = d->permissions;
2660  andFilePermissions = d->partialPermissions;
2661  orDirPermissions = d->permissions;
2662  andDirPermissions = d->partialPermissions;
2663  }
2664 
2665  QString owner, group;
2666  if (d->usrEdit) {
2667  owner = d->usrEdit->text();
2668  }
2669  if (d->grpEdit) {
2670  group = d->grpEdit->text();
2671  } else if (d->grpCombo) {
2672  group = d->grpCombo->currentText();
2673  }
2674 
2675  if (owner == d->strOwner) {
2676  owner.clear(); // no change
2677  }
2678 
2679  if (group == d->strGroup) {
2680  group.clear();
2681  }
2682 
2683  bool recursive = d->cbRecursive && d->cbRecursive->isChecked();
2684  bool permissionChange = false;
2685 
2686  KFileItemList files, dirs;
2687  const KFileItemList items = properties->items();
2688  KFileItemList::const_iterator it = items.begin();
2689  const KFileItemList::const_iterator kend = items.end();
2690  for (; it != kend; ++it) {
2691  if ((*it).isDir()) {
2692  dirs.append(*it);
2693  if ((*it).permissions() != (((*it).permissions() & andDirPermissions) | orDirPermissions)) {
2694  permissionChange = true;
2695  }
2696  } else if ((*it).isFile()) {
2697  files.append(*it);
2698  if ((*it).permissions() != (((*it).permissions() & andFilePermissions) | orFilePermissions)) {
2699  permissionChange = true;
2700  }
2701  }
2702  }
2703 
2704  const bool ACLChange = (d->extendedACL != properties->item().ACL());
2705  const bool defaultACLChange = (d->defaultACL != properties->item().defaultACL());
2706 
2707  if (owner.isEmpty() && group.isEmpty() && !recursive
2708  && !permissionChange && !ACLChange && !defaultACLChange) {
2709  return;
2710  }
2711 
2712  KIO::Job *job;
2713  if (!files.isEmpty()) {
2714  job = KIO::chmod(files, orFilePermissions, ~andFilePermissions,
2715  owner, group, false);
2716  if (ACLChange && d->fileSystemSupportsACLs) {
2717  job->addMetaData(QStringLiteral("ACL_STRING"), d->extendedACL.isValid() ? d->extendedACL.asString() : QStringLiteral("ACL_DELETE"));
2718  }
2719  if (defaultACLChange && d->fileSystemSupportsACLs) {
2720  job->addMetaData(QStringLiteral("DEFAULT_ACL_STRING"), d->defaultACL.isValid() ? d->defaultACL.asString() : QStringLiteral("ACL_DELETE"));
2721  }
2722 
2723  connect(job, &KJob::result,
2724  this, &KFilePermissionsPropsPlugin::slotChmodResult);
2725  QEventLoop eventLoop;
2726  connect(this, &KFilePermissionsPropsPlugin::leaveModality,
2727  &eventLoop, &QEventLoop::quit);
2729  }
2730  if (!dirs.isEmpty()) {
2731  job = KIO::chmod(dirs, orDirPermissions, ~andDirPermissions,
2732  owner, group, recursive);
2733  if (ACLChange && d->fileSystemSupportsACLs) {
2734  job->addMetaData(QStringLiteral("ACL_STRING"), d->extendedACL.isValid() ? d->extendedACL.asString() : QStringLiteral("ACL_DELETE"));
2735  }
2736  if (defaultACLChange && d->fileSystemSupportsACLs) {
2737  job->addMetaData(QStringLiteral("DEFAULT_ACL_STRING"), d->defaultACL.isValid() ? d->defaultACL.asString() : QStringLiteral("ACL_DELETE"));
2738  }
2739 
2740  connect(job, &KJob::result,
2741  this, &KFilePermissionsPropsPlugin::slotChmodResult);
2742  QEventLoop eventLoop;
2743  connect(this, &KFilePermissionsPropsPlugin::leaveModality,
2744  &eventLoop, &QEventLoop::quit);
2746  }
2747 }
2748 
2749 void KFilePermissionsPropsPlugin::slotChmodResult(KJob *job)
2750 {
2751  // qDebug() << "KFilePermissionsPropsPlugin::slotChmodResult";
2752  if (job->error()) {
2753  job->uiDelegate()->showErrorMessage();
2754  }
2755  // allow apply() to return
2756  emit leaveModality();
2757 }
2758 
2759 class KChecksumsPlugin::KChecksumsPluginPrivate
2760 {
2761 public:
2762  KChecksumsPluginPrivate()
2763  {
2764  }
2765 
2766  ~KChecksumsPluginPrivate()
2767  {
2768  }
2769 
2770  QWidget m_widget;
2771  Ui::ChecksumsWidget m_ui;
2772 
2773  QFileSystemWatcher fileWatcher;
2774  QString m_md5;
2775  QString m_sha1;
2776  QString m_sha256;
2777  QString m_sha512;
2778 };
2779 
2780 KChecksumsPlugin::KChecksumsPlugin(KPropertiesDialog *dialog)
2781  : KPropertiesDialogPlugin(dialog), d(new KChecksumsPluginPrivate)
2782 {
2783  d->m_ui.setupUi(&d->m_widget);
2784  properties->addPage(&d->m_widget, i18nc("@title:tab", "C&hecksums"));
2785 
2786  d->m_ui.md5CopyButton->hide();
2787  d->m_ui.sha1CopyButton->hide();
2788  d->m_ui.sha256CopyButton->hide();
2789  d->m_ui.sha512CopyButton->hide();
2790 
2791  connect(d->m_ui.lineEdit, &QLineEdit::textChanged, this, [=](const QString &text) {
2792  slotVerifyChecksum(text.toLower());
2793  });
2794 
2795  connect(d->m_ui.md5Button, &QPushButton::clicked, this, &KChecksumsPlugin::slotShowMd5);
2796  connect(d->m_ui.sha1Button, &QPushButton::clicked, this, &KChecksumsPlugin::slotShowSha1);
2797  connect(d->m_ui.sha256Button, &QPushButton::clicked, this, &KChecksumsPlugin::slotShowSha256);
2798  connect(d->m_ui.sha512Button, &QPushButton::clicked, this, &KChecksumsPlugin::slotShowSha512);
2799 
2800  d->fileWatcher.addPath(properties->item().localPath());
2801  connect(&d->fileWatcher, &QFileSystemWatcher::fileChanged, this, &KChecksumsPlugin::slotInvalidateCache);
2802 
2803  auto clipboard = QApplication::clipboard();
2804  connect(d->m_ui.md5CopyButton, &QPushButton::clicked, this, [=]() {
2805  clipboard->setText(d->m_md5);
2806  });
2807 
2808  connect(d->m_ui.sha1CopyButton, &QPushButton::clicked, this, [=]() {
2809  clipboard->setText(d->m_sha1);
2810  });
2811 
2812  connect(d->m_ui.sha256CopyButton, &QPushButton::clicked, this, [=]() {
2813  clipboard->setText(d->m_sha256);
2814  });
2815 
2816  connect(d->m_ui.sha512CopyButton, &QPushButton::clicked, this, [=]() {
2817  clipboard->setText(d->m_sha512);
2818  });
2819 
2820  connect(d->m_ui.pasteButton, &QPushButton::clicked, this, [=]() {
2821  d->m_ui.lineEdit->setText(clipboard->text());
2822  });
2823 
2824  setDefaultState();
2825 }
2826 
2827 KChecksumsPlugin::~KChecksumsPlugin()
2828 {
2829  delete d;
2830 }
2831 
2832 bool KChecksumsPlugin::supports(const KFileItemList &items)
2833 {
2834  if (items.count() != 1) {
2835  return false;
2836  }
2837 
2838  const KFileItem &item = items.first();
2839  return item.isFile() && !item.localPath().isEmpty() && item.isReadable() && !item.isDesktopFile() && !item.isLink();
2840 }
2841 
2842 void KChecksumsPlugin::slotInvalidateCache()
2843 {
2844  d->m_md5 = QString();
2845  d->m_sha1 = QString();
2846  d->m_sha256 = QString();
2847  d->m_sha512 = QString();
2848 }
2849 
2850 void KChecksumsPlugin::slotShowMd5()
2851 {
2852  auto label = new QLabel(i18nc("@action:button", "Calculating..."), &d->m_widget);
2853  label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
2854 
2855  d->m_ui.calculateWidget->layout()->replaceWidget(d->m_ui.md5Button, label);
2856  d->m_ui.md5Button->hide();
2857 
2858  showChecksum(QCryptographicHash::Md5, label, d->m_ui.md5CopyButton);
2859 }
2860 
2861 void KChecksumsPlugin::slotShowSha1()
2862 {
2863  auto label = new QLabel(i18nc("@action:button", "Calculating..."), &d->m_widget);
2864  label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
2865 
2866  d->m_ui.calculateWidget->layout()->replaceWidget(d->m_ui.sha1Button, label);
2867  d->m_ui.sha1Button->hide();
2868 
2869  showChecksum(QCryptographicHash::Sha1, label, d->m_ui.sha1CopyButton);
2870 }
2871 
2872 void KChecksumsPlugin::slotShowSha256()
2873 {
2874  auto label = new QLabel(i18nc("@action:button", "Calculating..."), &d->m_widget);
2875  label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
2876 
2877  d->m_ui.calculateWidget->layout()->replaceWidget(d->m_ui.sha256Button, label);
2878  d->m_ui.sha256Button->hide();
2879 
2880  showChecksum(QCryptographicHash::Sha256, label, d->m_ui.sha256CopyButton);
2881 }
2882 
2883 void KChecksumsPlugin::slotShowSha512()
2884 {
2885  auto label = new QLabel(i18nc("@action:button", "Calculating..."), &d->m_widget);
2886  label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
2887 
2888  d->m_ui.calculateWidget->layout()->replaceWidget(d->m_ui.sha512Button, label);
2889  d->m_ui.sha512Button->hide();
2890 
2891  showChecksum(QCryptographicHash::Sha512, label, d->m_ui.sha512CopyButton);
2892 }
2893 
2894 void KChecksumsPlugin::slotVerifyChecksum(const QString &input)
2895 {
2896  auto algorithm = detectAlgorithm(input);
2897 
2898  // Input is not a supported hash algorithm.
2899  if (algorithm == QCryptographicHash::Md4) {
2900  if (input.isEmpty()) {
2901  setDefaultState();
2902  } else {
2903  setInvalidChecksumState();
2904  }
2905  return;
2906  }
2907 
2908  const QString checksum = cachedChecksum(algorithm);
2909 
2910  // Checksum already in cache.
2911  if (!checksum.isEmpty()) {
2912  const bool isMatch = (checksum == input);
2913  if (isMatch) {
2914  setMatchState();
2915  } else {
2916  setMismatchState();
2917  }
2918 
2919  return;
2920  }
2921 
2922  // Calculate checksum in another thread.
2923  auto futureWatcher = new QFutureWatcher<QString>(this);
2924  connect(futureWatcher, &QFutureWatcher<QString>::finished, this, [=]() {
2925 
2926  const QString checksum = futureWatcher->result();
2927  futureWatcher->deleteLater();
2928 
2929  cacheChecksum(checksum, algorithm);
2930 
2931  switch (algorithm) {
2933  slotShowMd5();
2934  break;
2936  slotShowSha1();
2937  break;
2939  slotShowSha256();
2940  break;
2942  slotShowSha512();
2943  break;
2944  default:
2945  break;
2946  }
2947 
2948  const bool isMatch = (checksum == input);
2949  if (isMatch) {
2950  setMatchState();
2951  } else {
2952  setMismatchState();
2953  }
2954  });
2955 
2956  // Notify the user about the background computation.
2957  setVerifyState();
2958 
2959  auto future = QtConcurrent::run(&KChecksumsPlugin::computeChecksum, algorithm, properties->item().localPath());
2960  futureWatcher->setFuture(future);
2961 }
2962 
2963 bool KChecksumsPlugin::isMd5(const QString &input)
2964 {
2965  QRegularExpression regex(QStringLiteral("^[a-f0-9]{32}$"));
2966  return regex.match(input).hasMatch();
2967 }
2968 
2969 bool KChecksumsPlugin::isSha1(const QString &input)
2970 {
2971  QRegularExpression regex(QStringLiteral("^[a-f0-9]{40}$"));
2972  return regex.match(input).hasMatch();
2973 }
2974 
2975 bool KChecksumsPlugin::isSha256(const QString &input)
2976 {
2977  QRegularExpression regex(QStringLiteral("^[a-f0-9]{64}$"));
2978  return regex.match(input).hasMatch();
2979 }
2980 
2981 bool KChecksumsPlugin::isSha512(const QString &input)
2982 {
2983  QRegularExpression regex(QStringLiteral("^[a-f0-9]{128}$"));
2984  return regex.match(input).hasMatch();
2985 }
2986 
2987 QString KChecksumsPlugin::computeChecksum(QCryptographicHash::Algorithm algorithm, const QString &path)
2988 {
2989  QFile file(path);
2990  if (!file.open(QIODevice::ReadOnly)) {
2991  return QString();
2992  }
2993 
2994  QCryptographicHash hash(algorithm);
2995  hash.addData(&file);
2996 
2997  return QString::fromLatin1(hash.result().toHex());
2998 }
2999 
3000 QCryptographicHash::Algorithm KChecksumsPlugin::detectAlgorithm(const QString &input)
3001 {
3002  if (isMd5(input)) {
3003  return QCryptographicHash::Md5;
3004  }
3005 
3006  if (isSha1(input)) {
3007  return QCryptographicHash::Sha1;
3008  }
3009 
3010  if (isSha256(input)) {
3012  }
3013 
3014  if (isSha512(input)) {
3016  }
3017 
3018  // Md4 used as negative error code.
3019  return QCryptographicHash::Md4;
3020 }
3021 
3022 void KChecksumsPlugin::setDefaultState()
3023 {
3024  QColor defaultColor = d->m_widget.palette().color(QPalette::Base);
3025 
3026  QPalette palette = d->m_widget.palette();
3027  palette.setColor(QPalette::Base, defaultColor);
3028 
3029  d->m_ui.feedbackLabel->hide();
3030  d->m_ui.lineEdit->setPalette(palette);
3031  d->m_ui.lineEdit->setToolTip(QString());
3032 }
3033 
3034 void KChecksumsPlugin::setInvalidChecksumState()
3035 {
3037  QColor warningColor = colorScheme.background(KColorScheme::NegativeBackground).color();
3038 
3039  QPalette palette = d->m_widget.palette();
3040  palette.setColor(QPalette::Base, warningColor);
3041 
3042  d->m_ui.feedbackLabel->setText(i18n("Invalid checksum."));
3043  d->m_ui.feedbackLabel->show();
3044  d->m_ui.lineEdit->setPalette(palette);
3045  d->m_ui.lineEdit->setToolTip(i18nc("@info:tooltip", "The given input is not a valid MD5, SHA1 or SHA256 checksum."));
3046 }
3047 
3048 void KChecksumsPlugin::setMatchState()
3049 {
3051  QColor positiveColor = colorScheme.background(KColorScheme::PositiveBackground).color();
3052 
3053  QPalette palette = d->m_widget.palette();
3054  palette.setColor(QPalette::Base, positiveColor);
3055 
3056  d->m_ui.feedbackLabel->setText(i18n("Checksums match."));
3057  d->m_ui.feedbackLabel->show();
3058  d->m_ui.lineEdit->setPalette(palette);
3059  d->m_ui.lineEdit->setToolTip(i18nc("@info:tooltip", "The computed checksum and the expected checksum match."));
3060 }
3061 
3062 void KChecksumsPlugin::setMismatchState()
3063 {
3065  QColor warningColor = colorScheme.background(KColorScheme::NegativeBackground).color();
3066 
3067  QPalette palette = d->m_widget.palette();
3068  palette.setColor(QPalette::Base, warningColor);
3069 
3070  d->m_ui.feedbackLabel->setText(i18n("<p>Checksums do not match.</p>"
3071  "This may be due to a faulty download. Try re-downloading the file.<br/>"
3072  "If the verification still fails, contact the source of the file."));
3073  d->m_ui.feedbackLabel->show();
3074  d->m_ui.lineEdit->setPalette(palette);
3075  d->m_ui.lineEdit->setToolTip(i18nc("@info:tooltip", "The computed checksum and the expected checksum differ."));
3076 }
3077 
3078 void KChecksumsPlugin::setVerifyState()
3079 {
3080  // Users can paste a checksum at any time, so reset to default.
3081  setDefaultState();
3082 
3083  d->m_ui.feedbackLabel->setText(i18nc("notify the user about a computation in the background", "Verifying checksum..."));
3084  d->m_ui.feedbackLabel->show();
3085 }
3086 
3087 void KChecksumsPlugin::showChecksum(QCryptographicHash::Algorithm algorithm, QLabel *label, QPushButton *copyButton)
3088 {
3089  const QString checksum = cachedChecksum(algorithm);
3090 
3091  // Checksum in cache, nothing else to do.
3092  if (!checksum.isEmpty()) {
3093  label->setText(checksum);
3094  return;
3095  }
3096 
3097  // Calculate checksum in another thread.
3098  auto futureWatcher = new QFutureWatcher<QString>(this);
3099  connect(futureWatcher, &QFutureWatcher<QString>::finished, this, [=]() {
3100  const QString checksum = futureWatcher->result();
3101  futureWatcher->deleteLater();
3102 
3103  label->setText(checksum);
3104  cacheChecksum(checksum, algorithm);
3105 
3106  copyButton->show();
3107  });
3108 
3109  auto future = QtConcurrent::run(&KChecksumsPlugin::computeChecksum, algorithm, properties->item().localPath());
3110  futureWatcher->setFuture(future);
3111 }
3112 
3113 QString KChecksumsPlugin::cachedChecksum(QCryptographicHash::Algorithm algorithm) const
3114 {
3115  switch (algorithm) {
3117  return d->m_md5;
3119  return d->m_sha1;
3121  return d->m_sha256;
3123  return d->m_sha512;
3124  default:
3125  break;
3126  }
3127 
3128  return QString();
3129 }
3130 
3131 void KChecksumsPlugin::cacheChecksum(const QString &checksum, QCryptographicHash::Algorithm algorithm)
3132 {
3133  switch (algorithm) {
3135  d->m_md5 = checksum;
3136  break;
3138  d->m_sha1 = checksum;
3139  break;
3141  d->m_sha256 = checksum;
3142  break;
3144  d->m_sha512 = checksum;
3145  break;
3146  default:
3147  return;
3148  }
3149 }
3150 
3151 class KUrlPropsPlugin::KUrlPropsPluginPrivate
3152 {
3153 public:
3154  KUrlPropsPluginPrivate()
3155  {
3156  }
3157  ~KUrlPropsPluginPrivate()
3158  {
3159  }
3160 
3161  QFrame *m_frame;
3162  KUrlRequester *URLEdit;
3163  QString URLStr;
3164  bool fileNameReadOnly = false;
3165 };
3166 
3167 KUrlPropsPlugin::KUrlPropsPlugin(KPropertiesDialog *_props)
3168  : KPropertiesDialogPlugin(_props), d(new KUrlPropsPluginPrivate)
3169 {
3170  d->m_frame = new QFrame();
3171  properties->addPage(d->m_frame, i18n("U&RL"));
3172  QVBoxLayout *layout = new QVBoxLayout(d->m_frame);
3173  layout->setContentsMargins(0, 0, 0, 0);
3174 
3175  QLabel *l;
3176  l = new QLabel(d->m_frame);
3177  l->setObjectName(QStringLiteral("Label_1"));
3178  l->setText(i18n("URL:"));
3179  layout->addWidget(l, Qt::AlignRight);
3180 
3181  d->URLEdit = new KUrlRequester(d->m_frame);
3182  layout->addWidget(d->URLEdit);
3183 
3186  job->exec();
3187  QUrl url = job->mostLocalUrl();
3188 
3189  if (url.isLocalFile()) {
3190  QString path = url.toLocalFile();
3191 
3192  QFile f(path);
3193  if (!f.open(QIODevice::ReadOnly)) {
3194  return;
3195  }
3196  f.close();
3197 
3198  KDesktopFile config(path);
3199  const KConfigGroup dg = config.desktopGroup();
3200  d->URLStr = dg.readPathEntry("URL", QString());
3201 
3202  if (!d->URLStr.isEmpty()) {
3203  d->URLEdit->setUrl(QUrl(d->URLStr));
3204  }
3205  }
3206 
3207  connect(d->URLEdit, &KUrlRequester::textChanged,
3209 
3210  layout->addStretch(1);
3211 }
3212 
3213 KUrlPropsPlugin::~KUrlPropsPlugin()
3214 {
3215  delete d;
3216 }
3217 
3218 void KUrlPropsPlugin::setFileNameReadOnly(bool ro)
3219 {
3220  d->fileNameReadOnly = ro;
3221 }
3222 
3223 // QString KUrlPropsPlugin::tabName () const
3224 // {
3225 // return i18n ("U&RL");
3226 // }
3227 
3228 bool KUrlPropsPlugin::supports(const KFileItemList &_items)
3229 {
3230  if (_items.count() != 1) {
3231  return false;
3232  }
3233  const KFileItem &item = _items.first();
3234  // check if desktop file
3235  if (!item.isDesktopFile()) {
3236  return false;
3237  }
3238 
3239  // open file and check type
3240  bool isLocal;
3241  QUrl url = item.mostLocalUrl(&isLocal);
3242  if (!isLocal) {
3243  return false;
3244  }
3245 
3246  KDesktopFile config(url.toLocalFile());
3247  return config.hasLinkType();
3248 }
3249 
3250 void KUrlPropsPlugin::applyChanges()
3251 {
3254  job->exec();
3255  const QUrl url = job->mostLocalUrl();
3256 
3257  if (!url.isLocalFile()) {
3258  KMessageBox::sorry(nullptr, i18n("Could not save properties. Only entries on local file systems are supported."));
3259  return;
3260  }
3261 
3262  QString path = url.toLocalFile();
3263  QFile f(path);
3264  if (!f.open(QIODevice::ReadWrite)) {
3265  KMessageBox::sorry(nullptr, i18n("<qt>Could not save properties. You do not have "
3266  "sufficient access to write to <b>%1</b>.</qt>", path));
3267  return;
3268  }
3269  f.close();
3270 
3271  KDesktopFile config(path);
3272  KConfigGroup dg = config.desktopGroup();
3273  dg.writeEntry("Type", QStringLiteral("Link"));
3274  dg.writePathEntry("URL", d->URLEdit->url().toString());
3275  // Users can't create a Link .desktop file with a Name field,
3276  // but distributions can. Update the Name field in that case,
3277  // if the file name could have been changed.
3278  if (!d->fileNameReadOnly && dg.hasKey("Name")) {
3279  const QString nameStr = nameFromFileName(properties->url().fileName());
3280  dg.writeEntry("Name", nameStr);
3282 
3283  }
3284 }
3285 
3286 /* ----------------------------------------------------
3287  *
3288  * KDevicePropsPlugin
3289  *
3290  * -------------------------------------------------- */
3291 
3292 class KDevicePropsPlugin::KDevicePropsPluginPrivate
3293 {
3294 public:
3295  KDevicePropsPluginPrivate()
3296  {
3297  }
3298  ~KDevicePropsPluginPrivate()
3299  {
3300  }
3301 
3302  bool isMounted() const
3303  {
3304  const QString dev = device->currentText();
3305  return !dev.isEmpty() && KMountPoint::currentMountPoints().findByDevice(dev);
3306  }
3307 
3308  QFrame *m_frame;
3309  QStringList mountpointlist;
3310  QLabel *m_freeSpaceText;
3311  QLabel *m_freeSpaceLabel;
3312  QProgressBar *m_freeSpaceBar;
3313 
3314  KComboBox *device;
3315  QLabel *mountpoint;
3316  QCheckBox *readonly;
3317 
3318  QStringList m_devicelist;
3319 };
3320 
3321 KDevicePropsPlugin::KDevicePropsPlugin(KPropertiesDialog *_props) : KPropertiesDialogPlugin(_props), d(new KDevicePropsPluginPrivate)
3322 {
3323  d->m_frame = new QFrame();
3324  properties->addPage(d->m_frame, i18n("De&vice"));
3325 
3326  QStringList devices;
3328 
3329  for (const KMountPoint::Ptr &mp : mountPoints) {
3330  QString mountPoint = mp->mountPoint();
3331  QString device = mp->mountedFrom();
3332  // qDebug()<<"mountPoint :"<<mountPoint<<" device :"<<device<<" mp->mountType() :"<<mp->mountType();
3333 
3334  if ((mountPoint != QLatin1String("-")) && (mountPoint != QLatin1String("none")) && !mountPoint.isEmpty()
3335  && device != QLatin1String("none")) {
3336  devices.append(device + QLatin1String(" (")
3337  + mountPoint + QLatin1Char(')'));
3338  d->m_devicelist.append(device);
3339  d->mountpointlist.append(mountPoint);
3340  }
3341  }
3342 
3343  QGridLayout *layout = new QGridLayout(d->m_frame);
3344 
3345  layout->setContentsMargins(0, 0, 0, 0);
3346  layout->setColumnStretch(1, 1);
3347 
3348  QLabel *label;
3349  label = new QLabel(d->m_frame);
3350  label->setText(devices.isEmpty() ?
3351  i18n("Device (/dev/fd0):") : // old style
3352  i18n("Device:")); // new style (combobox)
3353  layout->addWidget(label, 0, 0, Qt::AlignRight);
3354 
3355  d->device = new KComboBox(d->m_frame);
3356  d->device->setObjectName(QStringLiteral("ComboBox_device"));
3357  d->device->setEditable(true);
3358  d->device->addItems(devices);
3359  layout->addWidget(d->device, 0, 1);
3360  connect(d->device, QOverload<int>::of(&QComboBox::activated),
3361  this, &KDevicePropsPlugin::slotActivated);
3362 
3363  d->readonly = new QCheckBox(d->m_frame);
3364  d->readonly->setObjectName(QStringLiteral("CheckBox_readonly"));
3365  d->readonly->setText(i18n("Read only"));
3366  layout->addWidget(d->readonly, 1, 1);
3367 
3368  label = new QLabel(d->m_frame);
3369  label->setText(i18n("File system:"));
3370  layout->addWidget(label, 2, 0, Qt::AlignRight);
3371 
3372  QLabel *fileSystem = new QLabel(d->m_frame);
3373  layout->addWidget(fileSystem, 2, 1);
3374 
3375  label = new QLabel(d->m_frame);
3376  label->setText(devices.isEmpty() ?
3377  i18n("Mount point (/mnt/floppy):") : // old style
3378  i18n("Mount point:")); // new style (combobox)
3379  layout->addWidget(label, 3, 0, Qt::AlignRight);
3380 
3381  d->mountpoint = new QLabel(d->m_frame);
3382  d->mountpoint->setObjectName(QStringLiteral("LineEdit_mountpoint"));
3383 
3384  layout->addWidget(d->mountpoint, 3, 1);
3385 
3386  // show disk free
3387  d->m_freeSpaceText = new QLabel(i18nc("Amount of used and available space on this device or partition", "Free space:"), d->m_frame);
3388  layout->addWidget(d->m_freeSpaceText, 4, 0, Qt::AlignRight);
3389 
3390  d->m_freeSpaceLabel = new QLabel(d->m_frame);
3391  layout->addWidget(d->m_freeSpaceLabel, 4, 1);
3392 
3393  d->m_freeSpaceBar = new QProgressBar(d->m_frame);
3394  d->m_freeSpaceBar->setObjectName(QStringLiteral("freeSpaceBar"));
3395  layout->addWidget(d->m_freeSpaceBar, 5, 0, 1, 2);
3396 
3397  // we show it in the slot when we know the values
3398  d->m_freeSpaceText->hide();
3399  d->m_freeSpaceLabel->hide();
3400  d->m_freeSpaceBar->hide();
3401 
3402  KSeparator *sep = new KSeparator(Qt::Horizontal, d->m_frame);
3403  layout->addWidget(sep, 6, 0, 1, 2);
3404 
3405  layout->setRowStretch(7, 1);
3406 
3407  KIO::StatJob *job = KIO::mostLocalUrl(_props->url());
3408  KJobWidgets::setWindow(job, _props);
3409  job->exec();
3410  QUrl url = job->mostLocalUrl();
3411 
3412  if (!url.isLocalFile()) {
3413  return;
3414  }
3415  QString path = url.toLocalFile();
3416 
3417  QFile f(path);
3418  if (!f.open(QIODevice::ReadOnly)) {
3419  return;
3420  }
3421  f.close();
3422 
3423  const KDesktopFile _config(path);
3424  const KConfigGroup config = _config.desktopGroup();
3425  QString deviceStr = config.readEntry("Dev");
3426  QString mountPointStr = config.readEntry("MountPoint");
3427  bool ro = config.readEntry("ReadOnly", false);
3428 
3429  fileSystem->setText(config.readEntry("FSType"));
3430 
3431  d->device->setEditText(deviceStr);
3432  if (!deviceStr.isEmpty()) {
3433  // Set default options for this device (first matching entry)
3434  int index = d->m_devicelist.indexOf(deviceStr);
3435  if (index != -1) {
3436  //qDebug() << "found it" << index;
3437  slotActivated(index);
3438  }
3439  }
3440 
3441  if (!mountPointStr.isEmpty()) {
3442  d->mountpoint->setText(mountPointStr);
3443  updateInfo();
3444  }
3445 
3446  d->readonly->setChecked(ro);
3447 
3448  connect(d->device, QOverload<int>::of(&QComboBox::activated),
3452  connect(d->readonly, &QAbstractButton::toggled,
3454 
3456  this, &KDevicePropsPlugin::slotDeviceChanged);
3457 }
3458 
3459 KDevicePropsPlugin::~KDevicePropsPlugin()
3460 {
3461  delete d;
3462 }
3463 
3464 // QString KDevicePropsPlugin::tabName () const
3465 // {
3466 // return i18n ("De&vice");
3467 // }
3468 
3469 void KDevicePropsPlugin::updateInfo()
3470 {
3471  // we show it in the slot when we know the values
3472  d->m_freeSpaceText->hide();
3473  d->m_freeSpaceLabel->hide();
3474  d->m_freeSpaceBar->hide();
3475 
3476  if (!d->mountpoint->text().isEmpty() && d->isMounted()) {
3477  KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo(d->mountpoint->text());
3478  slotFoundMountPoint(info.mountPoint(), info.size() / 1024, info.used() / 1024, info.available() / 1024);
3479  }
3480 }
3481 
3482 void KDevicePropsPlugin::slotActivated(int index)
3483 {
3484  // index can be more than the number of known devices, when the user types
3485  // a "custom" device.
3486  if (index < d->m_devicelist.count()) {
3487  // Update mountpoint so that it matches the device that was selected in the combo
3488  d->device->setEditText(d->m_devicelist[index]);
3489  d->mountpoint->setText(d->mountpointlist[index]);
3490  }
3491 
3492  updateInfo();
3493 }
3494 
3495 void KDevicePropsPlugin::slotDeviceChanged()
3496 {
3497  // Update mountpoint so that it matches the typed device
3498  int index = d->m_devicelist.indexOf(d->device->currentText());
3499  if (index != -1) {
3500  d->mountpoint->setText(d->mountpointlist[index]);
3501  } else {
3502  d->mountpoint->setText(QString());
3503  }
3504 
3505  updateInfo();
3506 }
3507 
3508 void KDevicePropsPlugin::slotFoundMountPoint(const QString &,
3509  quint64 kibSize,
3510  quint64 /*kibUsed*/,
3511  quint64 kibAvail)
3512 {
3513  d->m_freeSpaceText->show();
3514  d->m_freeSpaceLabel->show();
3515 
3516  const int percUsed = kibSize != 0 ? (100 - (int)(100.0 * kibAvail / kibSize)) : 100;
3517 
3518  d->m_freeSpaceLabel->setText(
3519  i18nc("Available space out of total partition size (percent used)", "%1 free of %2 (%3% used)",
3520  KIO::convertSizeFromKiB(kibAvail),
3521  KIO::convertSizeFromKiB(kibSize),
3522  percUsed));
3523 
3524  d->m_freeSpaceBar->setRange(0, 100);
3525  d->m_freeSpaceBar->setValue(percUsed);
3526  d->m_freeSpaceBar->show();
3527 }
3528 
3529 bool KDevicePropsPlugin::supports(const KFileItemList &_items)
3530 {
3531  if (_items.count() != 1) {
3532  return false;
3533  }
3534  const KFileItem &item = _items.first();
3535  // check if desktop file
3536  if (!item.isDesktopFile()) {
3537  return false;
3538  }
3539 
3540  // open file and check type
3541  bool isLocal;
3542  QUrl url = item.mostLocalUrl(&isLocal);
3543  if (!isLocal) {
3544  return false;
3545  }
3546 
3547  KDesktopFile config(url.toLocalFile());
3548  return config.hasDeviceType();
3549 }
3550 
3551 void KDevicePropsPlugin::applyChanges()
3552 {
3555  job->exec();
3556  const QUrl url = job->mostLocalUrl();
3557 
3558  if (!url.isLocalFile()) {
3559  return;
3560  }
3561  const QString path = url.toLocalFile();
3562  QFile f(path);
3563  if (!f.open(QIODevice::ReadWrite)) {
3564  KMessageBox::sorry(nullptr, i18n("<qt>Could not save properties. You do not have sufficient "
3565  "access to write to <b>%1</b>.</qt>", path));
3566  return;
3567  }
3568  f.close();
3569 
3570  KDesktopFile _config(path);
3571  KConfigGroup config = _config.desktopGroup();
3572  config.writeEntry("Type", QStringLiteral("FSDevice"));
3573 
3574  config.writeEntry("Dev", d->device->currentText());
3575  config.writeEntry("MountPoint", d->mountpoint->text());
3576 
3577  config.writeEntry("ReadOnly", d->readonly->isChecked());
3578 
3579  config.sync();
3580 }
3581 
3582 /* ----------------------------------------------------
3583  *
3584  * KDesktopPropsPlugin
3585  *
3586  * -------------------------------------------------- */
3587 
3588 class KDesktopPropsPlugin::KDesktopPropsPluginPrivate
3589 {
3590 public:
3591  KDesktopPropsPluginPrivate()
3592  : w(new Ui_KPropertiesDesktopBase)
3593  , m_frame(new QFrame())
3594  {
3595  }
3596  ~KDesktopPropsPluginPrivate()
3597  {
3598  delete w;
3599  }
3600  Ui_KPropertiesDesktopBase *w;
3601  QWidget *m_frame;
3602 
3603  QString m_origCommandStr;
3604  QString m_terminalOptionStr;
3605  QString m_suidUserStr;
3606  QString m_dbusStartupType;
3607  QString m_dbusServiceName;
3608  QString m_origDesktopFile;
3609  bool m_terminalBool;
3610  bool m_suidBool;
3611  bool m_hasDiscreteGpuBool;
3612  bool m_runOnDiscreteGpuBool;
3613  bool m_startupBool;
3614 };
3615 
3616 KDesktopPropsPlugin::KDesktopPropsPlugin(KPropertiesDialog *_props)
3617  : KPropertiesDialogPlugin(_props), d(new KDesktopPropsPluginPrivate)
3618 {
3619  QMimeDatabase db;
3620 
3621  d->w->setupUi(d->m_frame);
3622 
3623  properties->addPage(d->m_frame, i18n("&Application"));
3624 
3625  bool bKDesktopMode = properties->url().scheme() == QLatin1String("desktop") ||
3626  properties->currentDir().scheme() == QLatin1String("desktop");
3627 
3628  if (bKDesktopMode) {
3629  // Hide Name entry
3630  d->w->nameEdit->hide();
3631  d->w->nameLabel->hide();
3632  }
3633 
3634  d->w->pathEdit->setMode(KFile::Directory | KFile::LocalOnly);
3635  d->w->pathEdit->lineEdit()->setAcceptDrops(false);
3636 
3642 
3643  connect(d->w->browseButton, &QAbstractButton::clicked, this, &KDesktopPropsPlugin::slotBrowseExec);
3644  connect(d->w->addFiletypeButton, &QAbstractButton::clicked, this, &KDesktopPropsPlugin::slotAddFiletype);
3645  connect(d->w->delFiletypeButton, &QAbstractButton::clicked, this, &KDesktopPropsPlugin::slotDelFiletype);
3646  connect(d->w->advancedButton, &QAbstractButton::clicked, this, &KDesktopPropsPlugin::slotAdvanced);
3647 
3648  enum DiscreteGpuCheck { NotChecked, Present, Absent };
3649  static DiscreteGpuCheck s_gpuCheck = NotChecked;
3650 
3651  if (s_gpuCheck == NotChecked) {
3652  // Check whether we have a discrete gpu
3653  bool hasDiscreteGpu = false;
3654  QDBusInterface iface(QStringLiteral("org.kde.Solid.PowerManagement"),
3655  QStringLiteral("/org/kde/Solid/PowerManagement"),
3656  QStringLiteral("org.kde.Solid.PowerManagement"),
3658  if (iface.isValid()) {
3659  QDBusReply<bool> reply = iface.call(QStringLiteral("hasDualGpu"));
3660  if (reply.isValid()) {
3661  hasDiscreteGpu = reply.value();
3662  }
3663  }
3664 
3665  s_gpuCheck = hasDiscreteGpu ? Present : Absent;
3666  }
3667 
3668  d->m_hasDiscreteGpuBool = s_gpuCheck == Present;
3669 
3670  // now populate the page
3671 
3672  KIO::StatJob *job = KIO::mostLocalUrl(_props->url());
3673  KJobWidgets::setWindow(job, _props);
3674  job->exec();
3675  QUrl url = job->mostLocalUrl();
3676 
3677  if (!url.isLocalFile()) {
3678  return;
3679  }
3680 
3681  d->m_origDesktopFile = url.toLocalFile();
3682 
3683  QFile f(d->m_origDesktopFile);
3684  if (!f.open(QIODevice::ReadOnly)) {
3685  return;
3686  }
3687  f.close();
3688 
3689  KDesktopFile _config(d->m_origDesktopFile);
3690  KConfigGroup config = _config.desktopGroup();
3691  QString nameStr = _config.readName();
3692  QString genNameStr = _config.readGenericName();
3693  QString commentStr = _config.readComment();
3694  QString commandStr = config.readEntry("Exec", QString());
3695 
3696  d->m_origCommandStr = commandStr;
3697  QString pathStr = config.readEntry("Path", QString()); // not readPathEntry, see kservice.cpp
3698  d->m_terminalBool = config.readEntry("Terminal", false);
3699  d->m_terminalOptionStr = config.readEntry("TerminalOptions");
3700  d->m_suidBool = config.readEntry("X-KDE-SubstituteUID", false);
3701  d->m_suidUserStr = config.readEntry("X-KDE-Username");
3702  if (d->m_hasDiscreteGpuBool) {
3703  d->m_runOnDiscreteGpuBool = config.readEntry("X-KDE-RunOnDiscreteGpu", false);
3704  }
3705  if (config.hasKey("StartupNotify")) {
3706  d->m_startupBool = config.readEntry("StartupNotify", true);
3707  } else {
3708  d->m_startupBool = config.readEntry("X-KDE-StartupNotify", true);
3709  }
3710  d->m_dbusStartupType = config.readEntry("X-DBUS-StartupType").toLower();
3711  // ### should there be a GUI for this setting?
3712  // At least we're copying it over to the local file, to avoid side effects (#157853)
3713  d->m_dbusServiceName = config.readEntry("X-DBUS-ServiceName");
3714 
3715  const QStringList mimeTypes = config.readXdgListEntry("MimeType");
3716 
3717  if (nameStr.isEmpty() || bKDesktopMode) {
3718  // We'll use the file name if no name is specified
3719  // because we _need_ a Name for a valid file.
3720  // But let's do it in apply, not here, so that we pick up the right name.
3721  setDirty();
3722  }
3723  if (!bKDesktopMode) {
3724  d->w->nameEdit->setText(nameStr);
3725  }
3726 
3727  d->w->genNameEdit->setText(genNameStr);
3728  d->w->commentEdit->setText(commentStr);
3729  d->w->commandEdit->setText(commandStr);
3730  d->w->pathEdit->lineEdit()->setText(pathStr);
3731 
3732  // was: d->w->filetypeList->setFullWidth(true);
3733  // d->w->filetypeList->header()->setStretchEnabled(true, d->w->filetypeList->columns()-1);
3734 
3735  for (QStringList::ConstIterator it = mimeTypes.begin();
3736  it != mimeTypes.end();) {
3737  QMimeType p = db.mimeTypeForName(*it);
3738  ++it;
3739  QString preference;
3740  if (it != mimeTypes.end()) {
3741  bool numeric;
3742  (*it).toInt(&numeric);
3743  if (numeric) {
3744  preference = *it;
3745  ++it;
3746  }
3747  }
3748  if (p.isValid()) {
3749  QTreeWidgetItem *item = new QTreeWidgetItem();
3750  item->setText(0, p.name());
3751  item->setText(1, p.comment());
3752  item->setText(2, preference);
3753  d->w->filetypeList->addTopLevelItem(item);
3754  }
3755  }
3756  d->w->filetypeList->resizeColumnToContents(0);
3757 
3758 }
3759 
3760 KDesktopPropsPlugin::~KDesktopPropsPlugin()
3761 {
3762  delete d;
3763 }
3764 
3765 void KDesktopPropsPlugin::slotAddFiletype()
3766 {
3767  QMimeDatabase db;
3768  KMimeTypeChooserDialog dlg(i18n("Add File Type for %1", properties->url().fileName()),
3769  i18n("Select one or more file types to add:"),
3770  QStringList(), // no preselected mimetypes
3771  QString(),
3772  QStringList(),
3774  d->m_frame);
3775 
3776  if (dlg.exec() == QDialog::Accepted) {
3777  const QStringList list = dlg.chooser()->mimeTypes();
3778  for (const QString &mimetype : list) {
3779  QMimeType p = db.mimeTypeForName(mimetype);
3780  if (!p.isValid()) {
3781  continue;
3782  }
3783 
3784  bool found = false;
3785  int count = d->w->filetypeList->topLevelItemCount();
3786  for (int i = 0; !found && i < count; ++i) {
3787  if (d->w->filetypeList->topLevelItem(i)->text(0) == mimetype) {
3788  found = true;
3789  }
3790  }
3791  if (!found) {
3792  QTreeWidgetItem *item = new QTreeWidgetItem();
3793  item->setText(0, p.name());
3794  item->setText(1, p.comment());
3795  d->w->filetypeList->addTopLevelItem(item);
3796  }
3797  d->w->filetypeList->resizeColumnToContents(0);
3798  }
3799  }
3800  emit changed();
3801 }
3802 
3803 void KDesktopPropsPlugin::slotDelFiletype()
3804 {
3805  QTreeWidgetItem *cur = d->w->filetypeList->currentItem();
3806  if (cur) {
3807  delete cur;
3808  emit changed();
3809  }
3810 }
3811 
3812 void KDesktopPropsPlugin::checkCommandChanged()
3813 {
3814  if (KIO::DesktopExecParser::executableName(d->w->commandEdit->text()) !=
3815  KIO::DesktopExecParser::executableName(d->m_origCommandStr)) {
3816  d->m_origCommandStr = d->w->commandEdit->text();
3817  d->m_dbusStartupType.clear(); // Reset
3818  d->m_dbusServiceName.clear();
3819  }
3820 }
3821 
3822 void KDesktopPropsPlugin::applyChanges()
3823 {
3824  // qDebug();
3827  job->exec();
3828  const QUrl url = job->mostLocalUrl();
3829 
3830  if (!url.isLocalFile()) {
3831  KMessageBox::sorry(nullptr, i18n("Could not save properties. Only entries on local file systems are supported."));
3832  return;
3833  }
3834 
3835  const QString path(url.toLocalFile());
3836 
3837  // make sure the directory exists
3838  QDir().mkpath(QFileInfo(path).absolutePath());
3839  QFile f(path);
3840  if (!f.open(QIODevice::ReadWrite)) {
3841  KMessageBox::sorry(nullptr, i18n("<qt>Could not save properties. You do not have "
3842  "sufficient access to write to <b>%1</b>.</qt>", path));
3843  return;
3844  }
3845  f.close();
3846 
3847  // If the command is changed we reset certain settings that are strongly
3848  // coupled to the command.
3849  checkCommandChanged();
3850 
3851  KDesktopFile origConfig(d->m_origDesktopFile);
3852  QScopedPointer<KDesktopFile> _config(origConfig.copyTo(path));
3853  KConfigGroup config = _config->desktopGroup();
3854  config.writeEntry("Type", QStringLiteral("Application"));
3855  config.writeEntry("Comment", d->w->commentEdit->text());
3856  config.writeEntry("Comment", d->w->commentEdit->text(), KConfigGroup::Persistent | KConfigGroup::Localized); // for compat
3857  config.writeEntry("GenericName", d->w->genNameEdit->text());
3858  config.writeEntry("GenericName", d->w->genNameEdit->text(), KConfigGroup::Persistent | KConfigGroup::Localized); // for compat
3859  config.writeEntry("Exec", d->w->commandEdit->text());
3860  config.writeEntry("Path", d->w->pathEdit->lineEdit()->text()); // not writePathEntry, see kservice.cpp
3861 
3862  // Write mimeTypes
3863  QStringList mimeTypes;
3864  int count = d->w->filetypeList->topLevelItemCount();
3865  for (int i = 0; i < count; ++i) {
3866  QTreeWidgetItem *item = d->w->filetypeList->topLevelItem(i);
3867  QString preference = item->text(2);
3868  mimeTypes.append(item->text(0));
3869  if (!preference.isEmpty()) {
3870  mimeTypes.append(preference);
3871  }
3872  }
3873 
3874  // qDebug() << mimeTypes;
3875  config.writeXdgListEntry("MimeType", mimeTypes);
3876 
3877  if (!d->w->nameEdit->isHidden()) {
3878  QString nameStr = d->w->nameEdit->text();
3879  config.writeEntry("Name", nameStr);
3880  config.writeEntry("Name", nameStr, KConfigGroup::Persistent | KConfigGroup::Localized);
3881  }
3882 
3883  config.writeEntry("Terminal", d->m_terminalBool);
3884  config.writeEntry("TerminalOptions", d->m_terminalOptionStr);
3885  config.writeEntry("X-KDE-SubstituteUID", d->m_suidBool);
3886  config.writeEntry("X-KDE-Username", d->m_suidUserStr);
3887  if (d->m_hasDiscreteGpuBool) {
3888  config.writeEntry("X-KDE-RunOnDiscreteGpu", d->m_runOnDiscreteGpuBool);
3889  }
3890  config.writeEntry("StartupNotify", d->m_startupBool);
3891  config.writeEntry("X-DBUS-StartupType", d->m_dbusStartupType);
3892  config.writeEntry("X-DBUS-ServiceName", d->m_dbusServiceName);
3893  config.sync();
3894 
3895  // KSycoca update needed?
3896  bool updateNeeded = !relativeAppsLocation(path).isEmpty();
3897  if (updateNeeded) {
3899  }
3900 }
3901 
3902 void KDesktopPropsPlugin::slotBrowseExec()
3903 {
3904  QUrl f = QFileDialog::getOpenFileUrl(d->m_frame);
3905  if (f.isEmpty()) {
3906  return;
3907  }
3908 
3909  if (!f.isLocalFile()) {
3910  KMessageBox::sorry(d->m_frame, i18n("Only executables on local file systems are supported."));
3911  return;
3912  }
3913 
3914  QString path = f.toLocalFile();
3915  path = KShell::quoteArg(path);
3916  d->w->commandEdit->setText(path);
3917 }
3918 
3919 void KDesktopPropsPlugin::slotAdvanced()
3920 {
3921  QDialog dlg(d->m_frame);
3922  dlg.setObjectName(QStringLiteral("KPropertiesDesktopAdv"));
3923  dlg.setModal(true);
3924  dlg.setWindowTitle(i18n("Advanced Options for %1", properties->url().fileName()));
3925 
3926  Ui_KPropertiesDesktopAdvBase w;
3927  QWidget *mainWidget = new QWidget(&dlg);
3928  w.setupUi(mainWidget);
3929 
3930  QDialogButtonBox *buttonBox = new QDialogButtonBox(&dlg);
3932  connect(buttonBox, &QDialogButtonBox::accepted, &dlg, &QDialog::accept);
3933  connect(buttonBox, &QDialogButtonBox::rejected, &dlg, &QDialog::reject);
3934 
3935  QVBoxLayout *layout = new QVBoxLayout;
3936  layout->addWidget(mainWidget);
3937  layout->addWidget(buttonBox);
3938  dlg.setLayout(layout);
3939 
3940  // If the command is changed we reset certain settings that are strongly
3941  // coupled to the command.
3942  checkCommandChanged();
3943 
3944  // check to see if we use konsole if not do not add the nocloseonexit
3945  // because we don't know how to do this on other terminal applications
3946  KConfigGroup confGroup(KSharedConfig::openConfig(), QStringLiteral("General"));
3947  QString preferredTerminal = confGroup.readPathEntry("TerminalApplication",
3948  QStringLiteral("konsole"));
3949 
3950  bool terminalCloseBool = false;
3951 
3952  if (preferredTerminal == QLatin1String("konsole")) {
3953  terminalCloseBool = d->m_terminalOptionStr.contains(QLatin1String("--noclose"));
3954  w.terminalCloseCheck->setChecked(terminalCloseBool);
3955  d->m_terminalOptionStr.remove(QStringLiteral("--noclose"));
3956  } else {
3957  w.terminalCloseCheck->hide();
3958  }
3959 
3960  w.terminalCheck->setChecked(d->m_terminalBool);
3961  w.terminalEdit->setText(d->m_terminalOptionStr);
3962  w.terminalCloseCheck->setEnabled(d->m_terminalBool);
3963  w.terminalEdit->setEnabled(d->m_terminalBool);
3964  w.terminalEditLabel->setEnabled(d->m_terminalBool);
3965 
3966  w.suidCheck->setChecked(d->m_suidBool);
3967  w.suidEdit->setText(d->m_suidUserStr);
3968  w.suidEdit->setEnabled(d->m_suidBool);
3969  w.suidEditLabel->setEnabled(d->m_suidBool);
3970 
3971  if (d->m_hasDiscreteGpuBool) {
3972  w.discreteGpuCheck->setChecked(d->m_runOnDiscreteGpuBool);
3973  } else {
3974  w.discreteGpuGroupBox->hide();
3975  }
3976 
3977  w.startupInfoCheck->setChecked(d->m_startupBool);
3978 
3979  if (d->m_dbusStartupType == QLatin1String("unique")) {
3980  w.dbusCombo->setCurrentIndex(2);
3981  } else if (d->m_dbusStartupType == QLatin1String("multi")) {
3982  w.dbusCombo->setCurrentIndex(1);
3983  } else if (d->m_dbusStartupType == QLatin1String("wait")) {
3984  w.dbusCombo->setCurrentIndex(3);
3985  } else {
3986  w.dbusCombo->setCurrentIndex(0);
3987  }
3988 
3989  // Provide username completion up to 1000 users.
3990  const int maxEntries = 1000;
3991  QStringList userNames = KUser::allUserNames(maxEntries);
3992  if (userNames.size() < maxEntries) {
3993  KCompletion *kcom = new KCompletion;
3995  w.suidEdit->setCompletionObject(kcom, true);
3996  w.suidEdit->setAutoDeleteCompletionObject(true);
3997  w.suidEdit->setCompletionMode(KCompletion::CompletionAuto);
3998  kcom->setItems(userNames);
3999  }
4000 
4001  connect(w.terminalEdit, &QLineEdit::textChanged,
4003  connect(w.terminalCloseCheck, &QAbstractButton::toggled,
4005  connect(w.terminalCheck, &QAbstractButton::toggled,
4007  connect(w.suidCheck, &QAbstractButton::toggled,
4009  connect(w.suidEdit, &QLineEdit::textChanged,
4011  connect(w.discreteGpuCheck, &QAbstractButton::toggled,
4013  connect(w.startupInfoCheck, &QAbstractButton::toggled,
4015  connect(w.dbusCombo, QOverload<int>::of(&QComboBox::activated),
4017 
4018  if (dlg.exec() == QDialog::Accepted) {
4019  d->m_terminalOptionStr = w.terminalEdit->text().trimmed();
4020  d->m_terminalBool = w.terminalCheck->isChecked();
4021  d->m_suidBool = w.suidCheck->isChecked();
4022  d->m_suidUserStr = w.suidEdit->text().trimmed();
4023  if (d->m_hasDiscreteGpuBool) {
4024  d->m_runOnDiscreteGpuBool = w.discreteGpuCheck->isChecked();
4025  }
4026  d->m_startupBool = w.startupInfoCheck->isChecked();
4027 
4028  if (w.terminalCloseCheck->isChecked()) {
4029  d->m_terminalOptionStr.append(QLatin1String(" --noclose"));
4030  }
4031 
4032  switch (w.dbusCombo->currentIndex()) {
4033  case 1: d->m_dbusStartupType = QStringLiteral("multi"); break;
4034  case 2: d->m_dbusStartupType = QStringLiteral("unique"); break;
4035  case 3: d->m_dbusStartupType = QStringLiteral("wait"); break;
4036  default: d->m_dbusStartupType = QStringLiteral("none"); break;
4037  }
4038  }
4039 }
4040 
4041 bool KDesktopPropsPlugin::supports(const KFileItemList &_items)
4042 {
4043  if (_items.count() != 1) {
4044  return false;
4045  }
4046 
4047  const KFileItem &item = _items.first();
4048 
4049  // check if desktop file
4050  if (!item.isDesktopFile()) {
4051  return false;
4052  }
4053 
4054  // open file and check type
4055  bool isLocal;
4056  QUrl url = item.mostLocalUrl(&isLocal);
4057  if (!isLocal) {
4058  return false;
4059  }
4060 
4061  KDesktopFile config(url.toLocalFile());
4062  return config.hasApplicationType() &&
4063  KAuthorized::authorize(QStringLiteral("run_desktop_files")) &&
4064  KAuthorized::authorize(QStringLiteral("shell_access"));
4065 }
4066 
4067 #include "moc_kpropertiesdialog.cpp"
4068 #include "moc_kpropertiesdialog_p.cpp"
QString url(QUrl::FormattingOptions options) const const
KJOBWIDGETS_EXPORT void setWindow(KJob *job, QWidget *widget)
void setWordWrap(bool wordWrap)
KWIDGETSADDONS_EXPORT void editMimeType(const QString &mimeType, QWidget *widget)
StripTrailingSlash
virtual void slotCancel()
Called when the user presses &#39;Cancel&#39;.
QString readPathEntry(const QString &pKey, const QString &aDefault) const
When set, automatically overwrite the destination if it exists already.
Definition: job_base.h:291
bool close()
static void rebuildKSycoca(QWidget *parent)
Rebuild KSycoca and show a progress dialog while doing so.
bool sync() override
void renamed(KIO::Job *job, const QUrl &from, const QUrl &to)
The user chose to rename from to to.
bool isValid() const const
QString toNativeSeparators(const QString &pathName)
QString suffixForFileName(const QString &fileName) const const
QRegularExpressionMatch match(const QString &subject, int offset, QRegularExpression::MatchType matchType, QRegularExpression::MatchOptions matchOptions) const const
qulonglong filesize_t
64-bit file size
Definition: global.h:40
bool isWritable() const
Checks whether the file or directory is writable.
Definition: kfileitem.cpp:1172
void setContentsMargins(int left, int top, int right, int bottom)
void quit()
KIOCORE_EXPORT FileSystemFreeSpaceJob * fileSystemFreeSpace(const QUrl &url)
Get a filesystem&#39;s total and available space.
QString toDisplayString(QUrl::FormattingOptions options) const const
virtual void reject()
void rename(const QString &_name)
Renames the item to the specified name.
int width() const const
void addMetaData(const QString &key, const QString &value)
Add key/value pair to the meta data that is sent to the slave.
Definition: job.cpp:231
QBrush background(BackgroundRole=NormalBackground) const
void writePathEntry(const QString &pKey, const QString &path, WriteConfigFlags pFlags=Normal)
bool isDir() const
Returns true if this item represents a directory.
Definition: kfileitem.cpp:1229
QString writableLocation(QStandardPaths::StandardLocation type)
void applied()
This signal is emitted when the properties changes are applied (for example, with the OK button) ...
KIOCORE_EXPORT QString encodeFileName(const QString &str)
Encodes (from the text displayed to the real filename) This translates &#39;/&#39; into a "unicode fraction s...
Definition: global.cpp:141
QByteArray toHex() const const
QString toString(qlonglong i) const const
void append(const T &value)
void setColor(QPalette::ColorGroup group, QPalette::ColorRole role, const QColor &color)
void addWidget(QWidget *widget, int row, int column, Qt::Alignment alignment)
Computes a directory size (similar to "du", but doesn&#39;t give the same results since we simply sum up ...
void setUiDelegate(KJobUiDelegate *delegate)
QWidget * window() const const
QVector::iterator begin()
UseEffectiveUID
Universal Directory Service.
Definition: udsentry.h:77
KFileItemList items() const
QString mimetype() const
Returns the mimetype of the file item.
Definition: kfileitem.cpp:792
QString user() const
Returns the owner of the file.
Definition: kfileitem.cpp:751
static Ptr serviceByDesktopName(const QString &_name)
void setCurrentPage(KPageWidgetItem *item)
static List currentMountPoints(DetailsNeededFlags infoNeeded=BasicInfoNeeded)
This function gives a list of all currently used mountpoints.
QString readComment() const
OpenFileManagerWindowJob * highlightInFileManager(const QList< QUrl > &urls, const QByteArray &asn)
Convenience method for creating a job to highlight a certain file or folder.
T * create(QObject *parent=nullptr, const QVariantList &args=QVariantList())
bool isNull() const
Return true if default-constructed.
Definition: kfileitem.cpp:1627
virtual QString errorString() const
This class is a widget showing a lineedit and a button, which invokes a filedialog.
Definition: kurlrequester.h:49
QStyle * style() const const
virtual int pixelMetric(QStyle::PixelMetric metric, const QStyleOption *option, const QWidget *widget) const const =0
Hide progress information dialog, i.e.
Definition: job_base.h:275
void setPixmap(const QPixmap &)
const T & constFirst() const const
bool exec()
void clear()
Ptr findByDevice(const QString &device) const
Returns the mount point associated with device, i.e.
void accept() override
Called when the user presses &#39;Ok&#39;.
void abortApplying()
To abort applying changes.
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
QString toString() const
virtual int exec()
void setAlignment(Qt::Alignment)
void textChanged(const QString &text)
bool hasApplicationType() const
void writeEntry(const QString &key, const QVariant &value, WriteConfigFlags pFlags=Normal)
bool isValid() const const
A KIO job that retrieves information about a file or directory.
Definition: statjob.h:26
QDBusConnection sessionBus()
int fontHeight() const
Returns the font height.
mode_t permissions() const
Returns the permissions of the file (stat.st_mode containing only permissions).
Definition: kfileitem.cpp:1491
void setIcon(const QString &icon)
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
void setIconType(KIconLoader::Group group, KIconLoader::Context context, bool user=false)
void setIconSize(int size)
bool exists() const const
bool isReadable() const
Checks whether the file or directory is readable.
Definition: kfileitem.cpp:1135
KIOCORE_EXPORT QString convertSize(KIO::filesize_t size)
Converts size from bytes to the string representation.
Definition: global.cpp:43
bool isValid() const
Returns whether the KACL object represents a valid acl.
Definition: kacl.cpp:120
KPropertiesDialog * properties
Pointer to the dialog.
void chop(int n)
LeftToRight
bool isEmpty() const const
void setIcon(const QIcon &icon)
AlignCenter
QUrl getOpenFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options, const QStringList &supportedSchemes)
bool supportsMoving() const
Check if moving capability is supported.
void setStrictIconSize(bool b)
int size() const const
QStringList standardLocations(QStandardPaths::StandardLocation type)
int lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
bool isNull() const const
QDateTime time(FileTimes which) const
Requests the modification, access or creation time, depending on which.
Definition: kfileitem.cpp:742
static QStringList allUserNames(uint maxCount=KCOREADDONS_UINT_MAX)
QDialogButtonBox * buttonBox()
QMimeType mimeTypeForFile(const QString &fileName, QMimeDatabase::MatchMode mode) const const
void clear()
void addItem(const QString &text, const QVariant &userData)
void setBuddy(QWidget *buddy)
const QColor & color() const const
bool isLocalFile() const
Returns true if the file is a local file.
Definition: kfileitem.cpp:1524
void saveAs(const QUrl &oldUrl, QUrl &newUrl)
Emitted before changes to oldUrl are saved as newUrl.
A KIO job that retrieves the total and available size of a filesystem.
static bool showDialog(const KFileItem &item, QWidget *parent=nullptr, bool modal=true)
Immediately displays a Properties dialog using constructor with the same parameters.
void timeout()
QString canonicalFilePath() const const
void changed()
Emit this signal when the user changed anything in the plugin&#39;s tabs.
void setEnabled(bool)
KACL ACL() const
Returns the access control list for the file.
Definition: kfileitem.cpp:709
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
int count(const T &value) const const
KPropertiesDialogPlugin(KPropertiesDialog *_props)
Constructor To insert tabs into the properties dialog, use the add methods provided by KPageDialog (t...
int count() const
count fields
Definition: udsentry.cpp:424
bool exists() const const
bool sync() override
A Plugin in the Properties dialog This is an abstract class.
void propertiesClosed()
This signal is emitted when the Properties Dialog is closed (for example, with OK or Cancel buttons) ...
void append(const T &value)
KIO::filesize_t size() const
The total size of the partition mounted at mountPoint()
void insertPlugin(KPropertiesDialogPlugin *plugin)
Adds a "3rd party" properties plugin to the dialog.
void setAutoErrorHandlingEnabled(bool enable)
KPageWidgetItem * addPage(QWidget *widget, const QString &name)
Extra data (used only if you specified Columns/ColumnsTypes) NB: you cannot repeat this entry; use UD...
Definition: udsentry.h:340
void setRowStretch(int row, int stretch)
void setLayout(QLayout *layout)
KIOCORE_EXPORT QString convertSizeFromKiB(KIO::filesize_t kibSize)
Converts size from kibi-bytes (2^10) to the string representation.
Definition: global.cpp:50
virtual void applyChanges()
Applies all changes to the file.
void setLayoutDirection(Qt::LayoutDirection direction)
int exec(QEventLoop::ProcessEventsFlags flags)
KIOCORE_EXPORT DirectorySizeJob * directorySize(const QUrl &directory)
Computes a directory size (by doing a recursive listing).
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString fileName() const const
KDesktopFile * copyTo(const QString &file) const
QDBusReply::Type value() const const
int toInt(bool *ok, int base) const const
static KDiskFreeSpaceInfo freeSpaceInfo(const QString &path)
Static method used to determine the free disk space.
QString loginName() const
void setWidth(int width)
bool isEmpty() const const
QUrl url() const
The URL of the file that has its properties being displayed.
void setObjectName(const QString &name)
void showFileSharingPage()
Shows the page that was previously set by setFileSharingPage(), or does nothing if no page was set ye...
QString localPath() const
Returns the local path if isLocalFile() == true or the KIO item has a UDS_LOCAL_PATH atom...
Definition: kfileitem.cpp:672
QString linkDest() const
Returns the link destination if isLink() == true.
Definition: kfileitem.cpp:639
bool isEmpty() const const
void clicked(bool checked)
#define I18N_NOOP(text)
void activated(int index)
static bool isDesktopFile(const KFileItem &_item)
Convenience method for most ::supports methods.
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
List of KFileItems, which adds a few helper methods to QList<KFileItem>.
Definition: kfileitem.h:587
QUrl mostLocalUrl() const
most local URL
Definition: statjob.cpp:116
QString stringValue(uint field) const
Definition: udsentry.cpp:353
QString path(QUrl::ComponentFormattingOptions options) const const
bool hasMatch() const const
QString mimeComment() const
Returns the user-readable string representing the type of this file, like "OpenDocument Text File"...
Definition: kfileitem.cpp:873
static KMimeTypeTrader * self()
void addData(const char *data, int length)
KService::List query(const QString &mimeType, const QString &genericServiceType=QStringLiteral("Application"), const QString &constraint=QString()) const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
KIOCORE_EXPORT QString decodeFileName(const QString &str)
Decodes (from the filename to the text displayed) This doesn&#39;t do anything anymore, it used to do the opposite of encodeFileName when encodeFileName was using %2F for &#39;/&#39;.
Definition: global.cpp:148
QStringList readXdgListEntry(const QString &pKey, const QStringList &aDefault=QStringList()) const
QString defaultName() const
If the dialog is being built from a template, this method returns the default name.
void fileChanged(const QString &path)
void deleteLater()
T & first()
void setText(const QString &)
virtual void slotOk()
Called when the user presses &#39;Ok&#39;.
bool isFile() const
Returns true if this item represents a file (and not a directory)
Definition: kfileitem.cpp:1249
QString group() const
Returns the group of the file.
Definition: kfileitem.cpp:760
void hide()
static bool canDisplay(const KFileItemList &_items)
Determine whether there are any property pages available for the given file items.
TextSelectableByMouse
KIOCORE_EXPORT SimpleJob * symlink(const QString &target, const QUrl &dest, JobFlags flags=DefaultFlags)
Create or move a symlink.
Definition: simplejob.cpp:367
KIO::filesize_t size() const
Returns the size of the file, if known.
Definition: kfileitem.cpp:681
KJobUiDelegate * uiDelegate() const
static KUserId currentEffectiveUserId()
virtual bool open(QIODevice::OpenMode mode) override
int indexOf(QStringView str, int from) const const
QString iconName() const
Returns the full path name to the icon that represents this mime type.
Definition: kfileitem.cpp:976
QString scheme() const const
QMimeType mimeTypeForName(const QString &nameOrAlias) const const
void animatedShow()
void setSizePolicy(QSizePolicy)
virtual void accept()
QUrl currentDir() const
If the dialog is being built from a template, this method returns the current directory.
QList< QUrl > urlList() const
Definition: kfileitem.cpp:1665
void updateUrl(const QUrl &newUrl)
Updates the item URL (either called by rename or because a global apps/mimelnk desktop file is being ...
QString toLocalFile() const const
static ExtraFieldList extraFields(const QUrl &url)
Definition of extra fields in the UDS entries, returned by a listDir operation.
bool isExtended() const
The interface to the extended ACL.
Definition: kacl.cpp:131
The main properties dialog class.
QList::iterator end()
KCOREADDONS_EXPORT QString quoteArg(const QString &arg)
bool isDesktopFile() const
Checks whether the file is a readable local .desktop file, i.e.
Definition: kfileitem.cpp:1687
void setTristate(bool y)
bool isLink() const
Returns true if this item represents a link in the UNIX sense of a link.
Definition: kfileitem.cpp:1513
Provides information about the common properties of a group of KFileItem objects. ...
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
KIOCORE_EXPORT ChmodJob * chmod(const KFileItemList &lstItems, int permissions, int mask, const QString &newOwner, const QString &newGroup, bool recursive, JobFlags flags=DefaultFlags)
Creates a job that changes permissions/ownership on several files or directories, optionally recursiv...
Definition: chmodjob.cpp:273
void setFixedSize(const QSize &s)
void currentTextChanged(const QString &text)
QDateTime fromString(const QString &string, Qt::DateFormat format)
KPropertiesDialog(const KFileItem &item, QWidget *parent=nullptr)
Brings up a Properties dialog, as shown above.
bool isValid() const const
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
QCA_EXPORT void init()
bool hasKey(const QString &key) const
QWindow * windowHandle() const const
bool isValid() const const
void setChecked(bool)
QString i18n(const char *text, const TYPE &arg...)
QString readGenericName() const
void addLayout(QLayout *layout, int row, int column, Qt::Alignment alignment)
void setTextInteractionFlags(Qt::TextInteractionFlags flags)
KIO::filesize_t available() const
The available space in the partition mounted at mountPoint()
void setWhatsThis(const QString &)
const QString & icon() const
PM_LayoutHorizontalSpacing
QDBusMessage call(const QString &method, Args &&...args)
void setFileNameReadOnly(bool ro)
Call this to make the filename lineedit readonly, to prevent the user from renaming the file...
void reject() override
Called when the user presses &#39;Cancel&#39; or Esc.
const ushort * utf16() const const
void iconChanged(const QString &icon)
bool isNull() const const
QString mid(int position, int n) const const
QFontMetrics fontMetrics() const const
QString mountPoint() const
The mount point of the partition the requested path points to.
void canceled()
This signal is emitted when the properties changes are aborted (for example, with the Cancel button) ...
virtual void showErrorMessage()
QUrl adjusted(QUrl::FormattingOptions options) const const
void setStandardButtons(QDialogButtonBox::StandardButtons buttons)
void addStretch(int stretch)
KACL defaultACL() const
Returns the default access control list for the directory.
Definition: kfileitem.cpp:727
static List possibleMountPoints(DetailsNeededFlags infoNeeded=BasicInfoNeeded)
This function gives a list of all possible mountpoints.
void textChanged(const QString &)
Emitted when the text in the lineedit changes.
void setColumnStretch(int column, int stretch)
virtual void close() override
Ptr findByPath(const QString &path) const
Find the mountpoint on which resides path For instance if /home is a separate partition, findByPath("/home/user/blah") will return /home.
QString name(bool lowerCase=false) const
Return the name of the file item (without a path).
Definition: kfileitem.cpp:1542
virtual Q_SCRIPTABLE void start()=0
void addItem(QLayoutItem *item, int row, int column, int rowSpan, int columnSpan, Qt::Alignment alignment)
void reparseConfiguration()
The KACL class encapsulates a POSIX Access Control List.
Definition: kacl.h:35
void toggled(bool checked)
void setWindowTitle(const QString &)
void setMessageType(KMessageWidget::MessageType type)
typedef ConstIterator
int height() const const
virtual QSize sizeHint() const const override
bool isSuperUser() const
The base class for all jobs.
Definition: job_base.h:45
QUrl mostLocalUrl(bool *local=nullptr) const
Tries to give a local URL for this file item if possible.
Definition: kfileitem.cpp:1416
void setText(int column, const QString &text)
void setCurrentIndex(int index)
void setFileSharingPage(QWidget *page)
Sets the file sharing page.
KCONFIGGUI_EXPORT void restoreWindowSize(QWindow *window, const KConfigGroup &config)
int length() const const
QStringList groupNames(uint maxCount=KCOREADDONS_UINT_MAX) const
KIOCORE_EXPORT QString itemsSummaryString(uint items, uint files, uint dirs, KIO::filesize_t size, bool showSize)
Helper for showing information about a set of files and directories.
Definition: global.cpp:114
char * data()
PartiallyChecked
QByteArray result() const const
void setText(const QString &text)
QString fromLatin1(const char *str, int size)
List of mount points.
Definition: kmountpoint.h:32
QPushButton * button(QDialogButtonBox::StandardButton which) const const
QString canonicalPath() const const
mode_t mode() const
Returns the file type (stat.st_mode containing only S_IFDIR, S_IFLNK, ...).
Definition: kfileitem.cpp:1502
QIcon fromTheme(const QString &name)
void result(KIO::Job *job, KIO::filesize_t size, KIO::filesize_t available)
Signals the result.
void show()
KIOCORE_EXPORT StatJob * mostLocalUrl(const QUrl &url, JobFlags flags=DefaultFlags)
Tries to map a local URL for the given URL, using a KIO job.
Definition: statjob.cpp:235
Horizontal
KIOCORE_EXPORT StatJob * stat(const QUrl &url, JobFlags flags=DefaultFlags)
Find all details for one file or directory.
Definition: statjob.cpp:209
QUrl url() const
Returns the url of the file.
Definition: kfileitem.cpp:1482
void result(KJob *job)
bool isValid() const
static QVector< KPluginMetaData > findPlugins(const QString &directory, std::function< bool(const KPluginMetaData &)> filter=std::function< bool(const KPluginMetaData &)>())
const UDSEntry & statResult() const
Result of the stat operation.
Definition: statjob.cpp:111
KIO::UDSEntry entry() const
Returns the UDS entry.
Definition: kfileitem.cpp:1616
ApplicationLauncherJob runs an application and watches it while running.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
T qobject_cast(QObject *object)
QObject * parent() const const
KIOCORE_EXPORT CopyJob * moveAs(const QUrl &src, const QUrl &dest, JobFlags flags=DefaultFlags)
Moves a file or directory src to the given destination dest.
Definition: copyjob.cpp:2255
void setText(const QString &text)
QFuture< T > run(Function function,...)
virtual ~KPropertiesDialog()
Cleans up the properties dialog and frees any associated resources, including the dialog itself...
virtual void setItems(const QStringList &itemList)
QVector::iterator end()
T readEntry(const QString &key, const T &aDefault) const
QClipboard * clipboard()
QString text(int column) const const
KCONFIGGUI_EXPORT void saveWindowSize(const QWindow *window, KConfigGroup &config, KConfigGroup::WriteConfigFlags options=KConfigGroup::Normal)
QList::iterator begin()
QString fileName(QUrl::ComponentFormattingOptions options) const const
QString readName() const
virtual void setOrder(CompOrder order)
KIO::filesize_t used() const
The used space in the partition mounted at mountPoint()
void setSpacing(int spacing)
QByteArray encodeName(const QString &fileName)
KIOCORE_EXPORT CopyJob * copyAs(const QUrl &src, const QUrl &dest, JobFlags flags=DefaultFlags)
Copy a file or directory src into the destination dest, which is the destination name in any case...
Definition: copyjob.cpp:2229
A KFileItem is a generic class to handle a file, local or remote.
Definition: kfileitem.h:35
bool mkpath(const QString &dirPath) const const
void setCheckState(Qt::CheckState state)
CopyJob is used to move, copy or symlink files and directories.
Definition: copyjob.h:54
QUrl fromLocalFile(const QString &localFile)
void sorry(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Notify)
void addLayout(QLayout *layout, int stretch)
static QString executableName(const QString &execLine)
Given a full command line (e.g.
int error() const
KIOCORE_EXPORT QString iconNameForUrl(const QUrl &url)
Return the icon name for a URL.
Definition: global.cpp:215
bool isLocalFile() const const
Determine the space left on an arbitrary partition.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Sat Sep 19 2020 23:00:36 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.