• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.10.2 API Reference
  • KDE Home
  • Contact Us
 

KIO

  • kio
  • kfile
kpropertiesdialog.cpp
Go to the documentation of this file.
1 /* This file is part of the KDE project
2 
3  Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
4  Copyright (c) 1999, 2000 Preston Brown <pbrown@kde.org>
5  Copyright (c) 2000 Simon Hausmann <hausmann@kde.org>
6  Copyright (c) 2000 David Faure <faure@kde.org>
7  Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
8 
9  This library is free software; you can redistribute it and/or
10  modify it under the terms of the GNU Library General Public
11  License as published by the Free Software Foundation; either
12  version 2 of the License, or (at your option) any later version.
13 
14  This library is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  Library General Public License for more details.
18 
19  You should have received a copy of the GNU Library General Public License
20  along with this library; see the file COPYING.LIB. If not, write to
21  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  Boston, MA 02110-1301, USA.
23 */
24 
25 /*
26  * kpropertiesdialog.cpp
27  * View/Edit Properties of files, locally or remotely
28  *
29  * some FilePermissionsPropsPlugin-changes by
30  * Henner Zeller <zeller@think.de>
31  * some layout management by
32  * Bertrand Leconte <B.Leconte@mail.dotcom.fr>
33  * the rest of the layout management, bug fixes, adaptation to libkio,
34  * template feature by
35  * David Faure <faure@kde.org>
36  * More layout, cleanups, and fixes by
37  * Preston Brown <pbrown@kde.org>
38  * Plugin capability, cleanups and port to KDialog by
39  * Simon Hausmann <hausmann@kde.org>
40  * KDesktopPropsPlugin by
41  * Waldo Bastian <bastian@kde.org>
42  */
43 
44 #include "kpropertiesdialog.h"
45 #include "kpropertiesdialog_p.h"
46 
47 
48 #include <config.h>
49 #include <config-acl.h>
50 extern "C" {
51 #include <pwd.h>
52 #include <grp.h>
53 #include <time.h>
54 #include <sys/stat.h>
55 #include <sys/types.h>
56 }
57 #include <unistd.h>
58 #include <errno.h>
59 #include <algorithm>
60 #include <functional>
61 
62 #include <QtCore/QFile>
63 #include <QtCore/QDir>
64 #include <QtGui/QLabel>
65 #include <QtGui/QPushButton>
66 #include <QtGui/QCheckBox>
67 #include <QtCore/QMutableStringListIterator>
68 #include <QtCore/QTextIStream>
69 #include <QtGui/QPainter>
70 #include <QtGui/QLayout>
71 #include <QtGui/QStyle>
72 #include <QtGui/QProgressBar>
73 #include <QVector>
74 #include <QFileInfo>
75 
76 #ifdef HAVE_POSIX_ACL
77 extern "C" {
78 # include <sys/xattr.h>
79 }
80 #endif
81 
82 #include <kauthorized.h>
83 #include <kdialog.h>
84 #include <kdirnotify.h>
85 #include <kdiskfreespaceinfo.h>
86 #include <kdebug.h>
87 #include <kdesktopfile.h>
88 #include <kicondialog.h>
89 #include <kurl.h>
90 #include <kurlrequester.h>
91 #include <klocale.h>
92 #include <kglobal.h>
93 #include <kglobalsettings.h>
94 #include <kstandarddirs.h>
95 #include <kjobuidelegate.h>
96 #include <kio/job.h>
97 #include <kio/copyjob.h>
98 #include <kio/chmodjob.h>
99 #include <kio/directorysizejob.h>
100 #include <kio/renamedialog.h>
101 #include <kio/netaccess.h>
102 #include <kio/jobuidelegate.h>
103 #include <kfiledialog.h>
104 #include <kmimetype.h>
105 #include <kmountpoint.h>
106 #include <kiconloader.h>
107 #include <kmessagebox.h>
108 #include <kservice.h>
109 #include <kcombobox.h>
110 #include <kcompletion.h>
111 #include <klineedit.h>
112 #include <kseparator.h>
113 #include <ksqueezedtextlabel.h>
114 #include <kmimetypetrader.h>
115 #include <kmetaprops.h>
116 #include <kpreviewprops.h>
117 #include <krun.h>
118 #include <kvbox.h>
119 #include <kacl.h>
120 #include <kconfiggroup.h>
121 #include <kshell.h>
122 #include <kcapacitybar.h>
123 #include <kfileitemlistproperties.h>
124 
125 #ifndef Q_OS_WIN
126 #include "kfilesharedialog.h"
127 #endif
128 
129 #include "ui_kpropertiesdesktopbase.h"
130 #include "ui_kpropertiesdesktopadvbase.h"
131 #ifdef HAVE_POSIX_ACL
132 #include "kacleditwidget.h"
133 #endif
134 
135 #include <kbuildsycocaprogressdialog.h>
136 #include <kmimetypechooser.h>
137 
138 #ifdef Q_WS_WIN
139 # include <kkernel_win.h>
140 #ifdef __GNUC__
141 # warning TODO: port completely to win32
142 #endif
143 #endif
144 
145 using namespace KDEPrivate;
146 
147 static QString nameFromFileName(QString nameStr)
148 {
149  if ( nameStr.endsWith(QLatin1String(".desktop")) )
150  nameStr.truncate( nameStr.length() - 8 );
151  if ( nameStr.endsWith(QLatin1String(".kdelnk")) )
152  nameStr.truncate( nameStr.length() - 7 );
153  // Make it human-readable (%2F => '/', ...)
154  nameStr = KIO::decodeFileName( nameStr );
155  return nameStr;
156 }
157 
158 mode_t KFilePermissionsPropsPlugin::fperm[3][4] = {
159  {S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID},
160  {S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID},
161  {S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX}
162 };
163 
164 class KPropertiesDialog::KPropertiesDialogPrivate
165 {
166 public:
167  KPropertiesDialogPrivate(KPropertiesDialog *qq)
168  {
169  q = qq;
170  m_aborted = false;
171  fileSharePage = 0;
172  }
173  ~KPropertiesDialogPrivate()
174  {
175  }
176 
180  void init();
184  void insertPages();
185 
186  KPropertiesDialog *q;
187  bool m_aborted:1;
188  QWidget* fileSharePage;
192  KUrl m_singleUrl;
196  KFileItemList m_items;
200  QString m_defaultName;
201  KUrl m_currentDir;
205  QList<KPropertiesDialogPlugin*> m_pageList;
206 };
207 
208 KPropertiesDialog::KPropertiesDialog (const KFileItem& item,
209  QWidget* parent)
210  : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
211 {
212  setCaption( i18n( "Properties for %1" , KIO::decodeFileName(item.url().fileName())) );
213 
214  Q_ASSERT( !item.isNull() );
215  d->m_items.append(item);
216 
217  d->m_singleUrl = item.url();
218  Q_ASSERT(!d->m_singleUrl.isEmpty());
219 
220  d->init();
221 }
222 
223 KPropertiesDialog::KPropertiesDialog (const QString& title,
224  QWidget* parent)
225  : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
226 {
227  setCaption( i18n( "Properties for %1", title ) );
228 
229  d->init();
230 }
231 
232 KPropertiesDialog::KPropertiesDialog(const KFileItemList& _items,
233  QWidget* parent)
234  : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
235 {
236  if ( _items.count() > 1 )
237  setCaption( i18np( "Properties for 1 item", "Properties for %1 Selected Items", _items.count() ) );
238  else
239  setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_items.first().url().fileName())) );
240 
241  Q_ASSERT( !_items.isEmpty() );
242  d->m_singleUrl = _items.first().url();
243  Q_ASSERT(!d->m_singleUrl.isEmpty());
244 
245  d->m_items = _items;
246 
247  d->init();
248 }
249 
250 KPropertiesDialog::KPropertiesDialog (const KUrl& _url,
251  QWidget* parent)
252  : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
253 {
254  setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_url.fileName())) );
255 
256  d->m_singleUrl = _url;
257 
258  KIO::UDSEntry entry;
259  KIO::NetAccess::stat(_url, entry, parent);
260 
261  d->m_items.append(KFileItem(entry, _url));
262  d->init();
263 }
264 
265 KPropertiesDialog::KPropertiesDialog (const KUrl& _tempUrl, const KUrl& _currentDir,
266  const QString& _defaultName,
267  QWidget* parent)
268  : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
269 {
270  setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_tempUrl.fileName())) );
271 
272  d->m_singleUrl = _tempUrl;
273  d->m_defaultName = _defaultName;
274  d->m_currentDir = _currentDir;
275  Q_ASSERT(!d->m_singleUrl.isEmpty());
276 
277  // Create the KFileItem for the _template_ file, in order to read from it.
278  d->m_items.append(KFileItem(KFileItem::Unknown, KFileItem::Unknown, d->m_singleUrl));
279  d->init();
280 }
281 
282 bool KPropertiesDialog::showDialog(const KFileItem& item, QWidget* parent,
283  bool modal)
284 {
285  // TODO: do we really want to show the win32 property dialog?
286  // This means we lose metainfo, support for .desktop files, etc. (DF)
287 #ifdef Q_WS_WIN
288  QString localPath = item.localPath();
289  if (!localPath.isEmpty())
290  return showWin32FilePropertyDialog(localPath);
291 #endif
292  KPropertiesDialog* dlg = new KPropertiesDialog(item, parent);
293  if (modal) {
294  dlg->exec();
295  } else {
296  dlg->show();
297  }
298 
299  return true;
300 }
301 
302 bool KPropertiesDialog::showDialog(const KUrl& _url, QWidget* parent,
303  bool modal)
304 {
305 #ifdef Q_WS_WIN
306  if (_url.isLocalFile())
307  return showWin32FilePropertyDialog( _url.toLocalFile() );
308 #endif
309  KPropertiesDialog* dlg = new KPropertiesDialog(_url, parent);
310  if (modal) {
311  dlg->exec();
312  } else {
313  dlg->show();
314  }
315 
316  return true;
317 }
318 
319 bool KPropertiesDialog::showDialog(const KFileItemList& _items, QWidget* parent,
320  bool modal)
321 {
322  if (_items.count()==1) {
323  const KFileItem item = _items.first();
324  if (item.entry().count() == 0 && item.localPath().isEmpty()) // this remote item wasn't listed by a slave
325  // Let's stat to get more info on the file
326  return KPropertiesDialog::showDialog(item.url(), parent, modal);
327  else
328  return KPropertiesDialog::showDialog(_items.first(), parent, modal);
329  }
330  KPropertiesDialog* dlg = new KPropertiesDialog(_items, parent);
331  if (modal) {
332  dlg->exec();
333  } else {
334  dlg->show();
335  }
336  return true;
337 }
338 
339 void KPropertiesDialog::KPropertiesDialogPrivate::init()
340 {
341  q->setFaceType(KPageDialog::Tabbed);
342  q->setButtons(KDialog::Ok | KDialog::Cancel);
343  q->setDefaultButton(KDialog::Ok);
344 
345  connect(q, SIGNAL(okClicked()), q, SLOT(slotOk()));
346  connect(q, SIGNAL(cancelClicked()), q, SLOT(slotCancel()));
347 
348  insertPages();
349 
350  KConfigGroup group(KGlobal::config(), "KPropertiesDialog");
351  q->restoreDialogSize(group);
352 }
353 
354 void KPropertiesDialog::showFileSharingPage()
355 {
356  if (d->fileSharePage) {
357  // FIXME: this showFileSharingPage thingy looks broken! (tokoe)
358  // showPage( pageIndex( d->fileSharePage));
359  }
360 }
361 
362 void KPropertiesDialog::setFileSharingPage(QWidget* page) {
363  d->fileSharePage = page;
364 }
365 
366 
367 void KPropertiesDialog::setFileNameReadOnly( bool ro )
368 {
369  foreach(KPropertiesDialogPlugin *it, d->m_pageList) {
370  KFilePropsPlugin* plugin = dynamic_cast<KFilePropsPlugin*>(it);
371  if ( plugin ) {
372  plugin->setFileNameReadOnly( ro );
373  break;
374  }
375  }
376 }
377 
378 KPropertiesDialog::~KPropertiesDialog()
379 {
380  qDeleteAll(d->m_pageList);
381  delete d;
382 
383  KConfigGroup group(KGlobal::config(), "KPropertiesDialog");
384  saveDialogSize(group, KConfigBase::Persistent);
385 }
386 
387 void KPropertiesDialog::insertPlugin (KPropertiesDialogPlugin* plugin)
388 {
389  connect (plugin, SIGNAL (changed()),
390  plugin, SLOT (setDirty()));
391 
392  d->m_pageList.append(plugin);
393 }
394 
395 KUrl KPropertiesDialog::kurl() const
396 {
397  return d->m_singleUrl;
398 }
399 
400 KFileItem& KPropertiesDialog::item()
401 {
402  return d->m_items.first();
403 }
404 
405 KFileItemList KPropertiesDialog::items() const
406 {
407  return d->m_items;
408 }
409 
410 KUrl KPropertiesDialog::currentDir() const
411 {
412  return d->m_currentDir;
413 }
414 
415 QString KPropertiesDialog::defaultName() const
416 {
417  return d->m_defaultName;
418 }
419 
420 bool KPropertiesDialog::canDisplay( const KFileItemList& _items )
421 {
422  // TODO: cache the result of those calls. Currently we parse .desktop files far too many times
423  return KFilePropsPlugin::supports( _items ) ||
424  KFilePermissionsPropsPlugin::supports( _items ) ||
425  KDesktopPropsPlugin::supports( _items ) ||
426  KUrlPropsPlugin::supports( _items ) ||
427  KDevicePropsPlugin::supports( _items ) ||
428  KFileMetaPropsPlugin::supports( _items ) ||
429  KPreviewPropsPlugin::supports( _items );
430 }
431 
432 void KPropertiesDialog::slotOk()
433 {
434  QList<KPropertiesDialogPlugin*>::const_iterator pageListIt;
435  d->m_aborted = false;
436 
437  KFilePropsPlugin * filePropsPlugin = qobject_cast<KFilePropsPlugin*>(d->m_pageList.first());
438 
439  // If any page is dirty, then set the main one (KFilePropsPlugin) as
440  // dirty too. This is what makes it possible to save changes to a global
441  // desktop file into a local one. In other cases, it doesn't hurt.
442  for (pageListIt = d->m_pageList.constBegin(); pageListIt != d->m_pageList.constEnd(); ++pageListIt) {
443  if ( (*pageListIt)->isDirty() && filePropsPlugin )
444  {
445  filePropsPlugin->setDirty();
446  break;
447  }
448  }
449 
450  // Apply the changes in the _normal_ order of the tabs now
451  // This is because in case of renaming a file, KFilePropsPlugin will call
452  // KPropertiesDialog::rename, so other tab will be ok with whatever order
453  // BUT for file copied from templates, we need to do the renaming first !
454  for (pageListIt = d->m_pageList.constBegin(); pageListIt != d->m_pageList.constEnd() && !d->m_aborted; ++pageListIt) {
455  if ( (*pageListIt)->isDirty() )
456  {
457  kDebug( 250 ) << "applying changes for " << (*pageListIt)->metaObject()->className();
458  (*pageListIt)->applyChanges();
459  // applyChanges may change d->m_aborted.
460  }
461  else {
462  kDebug( 250 ) << "skipping page " << (*pageListIt)->metaObject()->className();
463  }
464  }
465 
466  if ( !d->m_aborted && filePropsPlugin )
467  filePropsPlugin->postApplyChanges();
468 
469  if ( !d->m_aborted )
470  {
471  emit applied();
472  emit propertiesClosed();
473  deleteLater(); // somewhat like Qt::WA_DeleteOnClose would do.
474  accept();
475  } // else, keep dialog open for user to fix the problem.
476 }
477 
478 void KPropertiesDialog::slotCancel()
479 {
480  emit canceled();
481  emit propertiesClosed();
482 
483  deleteLater();
484  done( Rejected );
485 }
486 
487 void KPropertiesDialog::KPropertiesDialogPrivate::insertPages()
488 {
489  if (m_items.isEmpty())
490  return;
491 
492  if ( KFilePropsPlugin::supports( m_items ) ) {
493  KPropertiesDialogPlugin *p = new KFilePropsPlugin(q);
494  q->insertPlugin(p);
495  }
496 
497  if ( KFilePermissionsPropsPlugin::supports( m_items ) ) {
498  KPropertiesDialogPlugin *p = new KFilePermissionsPropsPlugin(q);
499  q->insertPlugin(p);
500  }
501 
502  if ( KDesktopPropsPlugin::supports( m_items ) ) {
503  KPropertiesDialogPlugin *p = new KDesktopPropsPlugin(q);
504  q->insertPlugin(p);
505  }
506 
507  if ( KUrlPropsPlugin::supports( m_items ) ) {
508  KPropertiesDialogPlugin *p = new KUrlPropsPlugin(q);
509  q->insertPlugin(p);
510  }
511 
512  if ( KDevicePropsPlugin::supports( m_items ) ) {
513  KPropertiesDialogPlugin *p = new KDevicePropsPlugin(q);
514  q->insertPlugin(p);
515  }
516 
517  if ( KFileMetaPropsPlugin::supports( m_items ) ) {
518  KPropertiesDialogPlugin *p = new KFileMetaPropsPlugin(q);
519  q->insertPlugin(p);
520  }
521 
522  if ( KPreviewPropsPlugin::supports( m_items ) ) {
523  KPropertiesDialogPlugin *p = new KPreviewPropsPlugin(q);
524  q->insertPlugin(p);
525  }
526 
527  //plugins
528 
529  if ( m_items.count() != 1 )
530  return;
531 
532  const KFileItem item = m_items.first();
533  const QString mimetype = item.mimetype();
534 
535  if ( mimetype.isEmpty() )
536  return;
537 
538  QString query = QString::fromLatin1(
539  "((not exist [X-KDE-Protocol]) or "
540  " ([X-KDE-Protocol] == '%1' ) )"
541  ).arg(item.url().protocol());
542 
543  kDebug( 250 ) << "trader query: " << query;
544  const KService::List offers = KMimeTypeTrader::self()->query( mimetype, "KPropertiesDialog/Plugin", query );
545  foreach (const KService::Ptr &ptr, offers) {
546  KPropertiesDialogPlugin *plugin = ptr->createInstance<KPropertiesDialogPlugin>(q);
547  if (!plugin)
548  continue;
549  plugin->setObjectName(ptr->name());
550 
551  q->insertPlugin(plugin);
552  }
553 }
554 
555 void KPropertiesDialog::updateUrl( const KUrl& _newUrl )
556 {
557  Q_ASSERT(d->m_items.count() == 1);
558  kDebug(250) << "KPropertiesDialog::updateUrl (pre)" << _newUrl.url();
559  KUrl newUrl = _newUrl;
560  emit saveAs(d->m_singleUrl, newUrl);
561  kDebug(250) << "KPropertiesDialog::updateUrl (post)" << newUrl.url();
562 
563  d->m_singleUrl = newUrl;
564  d->m_items.first().setUrl(newUrl);
565  Q_ASSERT(!d->m_singleUrl.isEmpty());
566  // If we have an Desktop page, set it dirty, so that a full file is saved locally
567  // Same for a URL page (because of the Name= hack)
568  foreach (KPropertiesDialogPlugin *it, d->m_pageList) {
569  if ( qobject_cast<KUrlPropsPlugin*>(it) ||
570  qobject_cast<KDesktopPropsPlugin*>(it) )
571  {
572  //kDebug(250) << "Setting page dirty";
573  it->setDirty();
574  break;
575  }
576  }
577 }
578 
579 void KPropertiesDialog::rename( const QString& _name )
580 {
581  Q_ASSERT(d->m_items.count() == 1);
582  kDebug(250) << "KPropertiesDialog::rename " << _name;
583  KUrl newUrl;
584  // if we're creating from a template : use currentdir
585  if (!d->m_currentDir.isEmpty()) {
586  newUrl = d->m_currentDir;
587  newUrl.addPath(_name);
588  } else {
589  QString tmpurl = d->m_singleUrl.url();
590  if (!tmpurl.isEmpty() && tmpurl.at(tmpurl.length() - 1) == '/') {
591  // It's a directory, so strip the trailing slash first
592  tmpurl.truncate(tmpurl.length() - 1);
593  }
594 
595  newUrl = tmpurl;
596  newUrl.setFileName(_name);
597  }
598  updateUrl(newUrl);
599 }
600 
601 void KPropertiesDialog::abortApplying()
602 {
603  d->m_aborted = true;
604 }
605 
606 class KPropertiesDialogPlugin::KPropertiesDialogPluginPrivate
607 {
608 public:
609  KPropertiesDialogPluginPrivate()
610  {
611  }
612  ~KPropertiesDialogPluginPrivate()
613  {
614  }
615 
616  bool m_bDirty;
617  int fontHeight;
618 };
619 
620 KPropertiesDialogPlugin::KPropertiesDialogPlugin( KPropertiesDialog *_props )
621  : QObject( _props ),d(new KPropertiesDialogPluginPrivate)
622 {
623  properties = _props;
624  d->fontHeight = 2*properties->fontMetrics().height();
625  d->m_bDirty = false;
626 }
627 
628 KPropertiesDialogPlugin::~KPropertiesDialogPlugin()
629 {
630  delete d;
631 }
632 
633 #ifndef KDE_NO_DEPRECATED
634 bool KPropertiesDialogPlugin::isDesktopFile( const KFileItem& _item )
635 {
636  return _item.isDesktopFile();
637 }
638 #endif
639 
640 void KPropertiesDialogPlugin::setDirty( bool b )
641 {
642  d->m_bDirty = b;
643 }
644 
645 void KPropertiesDialogPlugin::setDirty()
646 {
647  d->m_bDirty = true;
648 }
649 
650 bool KPropertiesDialogPlugin::isDirty() const
651 {
652  return d->m_bDirty;
653 }
654 
655 void KPropertiesDialogPlugin::applyChanges()
656 {
657  kWarning(250) << "applyChanges() not implemented in page !";
658 }
659 
660 int KPropertiesDialogPlugin::fontHeight() const
661 {
662  return d->fontHeight;
663 }
664 
666 
667 class KFilePropsPlugin::KFilePropsPluginPrivate
668 {
669 public:
670  KFilePropsPluginPrivate()
671  {
672  dirSizeJob = 0L;
673  dirSizeUpdateTimer = 0L;
674  m_lined = 0;
675  m_capacityBar = 0;
676  m_linkTargetLineEdit = 0;
677  }
678  ~KFilePropsPluginPrivate()
679  {
680  if ( dirSizeJob )
681  dirSizeJob->kill();
682  }
683 
684  KIO::DirectorySizeJob * dirSizeJob;
685  QTimer *dirSizeUpdateTimer;
686  QFrame *m_frame;
687  bool bMultiple;
688  bool bIconChanged;
689  bool bKDesktopMode;
690  bool bDesktopFile;
691  KCapacityBar *m_capacityBar;
692  QString mimeType;
693  QString oldFileName;
694  KLineEdit* m_lined;
695 
696  QWidget *iconArea;
697  QWidget *nameArea;
698 
699  QLabel *m_sizeLabel;
700  QPushButton *m_sizeDetermineButton;
701  QPushButton *m_sizeStopButton;
702  KLineEdit* m_linkTargetLineEdit;
703 
704  QString m_sRelativePath;
705  bool m_bFromTemplate;
706 
710  QString oldName;
711 };
712 
713 KFilePropsPlugin::KFilePropsPlugin( KPropertiesDialog *_props )
714  : KPropertiesDialogPlugin( _props ),d(new KFilePropsPluginPrivate)
715 {
716  d->bMultiple = (properties->items().count() > 1);
717  d->bIconChanged = false;
718  d->bDesktopFile = KDesktopPropsPlugin::supports(properties->items());
719  kDebug(250) << "KFilePropsPlugin::KFilePropsPlugin bMultiple=" << d->bMultiple;
720 
721  // We set this data from the first item, and we'll
722  // check that the other items match against it, resetting when not.
723  bool isLocal;
724  const KFileItem item = properties->item();
725  KUrl url = item.mostLocalUrl( isLocal );
726  bool isReallyLocal = item.url().isLocalFile();
727  bool bDesktopFile = item.isDesktopFile();
728  mode_t mode = item.mode();
729  bool hasDirs = item.isDir() && !item.isLink();
730  bool hasRoot = url.path() == QLatin1String("/");
731  QString iconStr = KMimeType::iconNameForUrl(url, mode);
732  QString directory = properties->kurl().directory();
733  QString protocol = properties->kurl().protocol();
734  d->bKDesktopMode = protocol == QLatin1String("desktop") ||
735  properties->currentDir().protocol() == QLatin1String("desktop");
736  QString mimeComment = item.mimeComment();
737  d->mimeType = item.mimetype();
738  KIO::filesize_t totalSize = item.size();
739  QString magicMimeComment;
740  if ( isLocal ) {
741  KMimeType::Ptr magicMimeType = KMimeType::findByFileContent(url.toLocalFile());
742  if ( magicMimeType->name() != KMimeType::defaultMimeType() )
743  magicMimeComment = magicMimeType->comment();
744  }
745 #ifdef Q_WS_WIN
746  if ( isReallyLocal ) {
747  directory = QDir::toNativeSeparators( directory.mid( 1 ) );
748  }
749 #endif
750 
751  // Those things only apply to 'single file' mode
752  QString filename;
753  bool isTrash = false;
754  d->m_bFromTemplate = false;
755 
756  // And those only to 'multiple' mode
757  uint iDirCount = hasDirs ? 1 : 0;
758  uint iFileCount = 1-iDirCount;
759 
760  d->m_frame = new QFrame();
761  properties->addPage(d->m_frame, i18nc("@title:tab File properties", "&General"));
762 
763  QVBoxLayout *vbl = new QVBoxLayout( d->m_frame );
764  vbl->setMargin( 0 );
765  vbl->setObjectName( QLatin1String( "vbl" ) );
766  QGridLayout *grid = new QGridLayout(); // unknown rows
767  grid->setColumnStretch(0, 0);
768  grid->setColumnStretch(1, 0);
769  grid->setColumnStretch(2, 1);
770  grid->addItem(new QSpacerItem(KDialog::spacingHint(),0), 0, 1);
771  vbl->addLayout(grid);
772  int curRow = 0;
773 
774  if ( !d->bMultiple )
775  {
776  QString path;
777  if ( !d->m_bFromTemplate ) {
778  isTrash = ( properties->kurl().protocol().toLower() == "trash" );
779  // Extract the full name, but without file: for local files
780  if ( isReallyLocal )
781  path = properties->kurl().toLocalFile();
782  else
783  path = properties->kurl().prettyUrl();
784  } else {
785  path = properties->currentDir().path(KUrl::AddTrailingSlash) + properties->defaultName();
786  directory = properties->currentDir().prettyUrl();
787  }
788 
789  if (d->bDesktopFile) {
790  determineRelativePath( path );
791  }
792 
793  // Extract the file name only
794  filename = properties->defaultName();
795  if ( filename.isEmpty() ) { // no template
796  const QFileInfo finfo (item.name()); // this gives support for UDS_NAME, e.g. for kio_trash or kio_system
797  filename = finfo.fileName(); // Make sure only the file's name is displayed (#160964).
798  } else {
799  d->m_bFromTemplate = true;
800  setDirty(); // to enforce that the copy happens
801  }
802  d->oldFileName = filename;
803 
804  // Make it human-readable
805  filename = nameFromFileName( filename );
806 
807  if ( d->bKDesktopMode && d->bDesktopFile ) {
808  KDesktopFile config(url.toLocalFile());
809  if ( config.desktopGroup().hasKey( "Name" ) ) {
810  filename = config.readName();
811  }
812  }
813 
814  d->oldName = filename;
815  }
816  else
817  {
818  // Multiple items: see what they have in common
819  const KFileItemList items = properties->items();
820  KFileItemList::const_iterator kit = items.begin();
821  const KFileItemList::const_iterator kend = items.end();
822  for ( ++kit /*no need to check the first one again*/ ; kit != kend; ++kit )
823  {
824  const KUrl url = (*kit).url();
825  kDebug(250) << "KFilePropsPlugin::KFilePropsPlugin " << url.prettyUrl();
826  // The list of things we check here should match the variables defined
827  // at the beginning of this method.
828  if ( url.isLocalFile() != isLocal )
829  isLocal = false; // not all local
830  if ( bDesktopFile && (*kit).isDesktopFile() != bDesktopFile )
831  bDesktopFile = false; // not all desktop files
832  if ( (*kit).mode() != mode )
833  mode = (mode_t)0;
834  if ( KMimeType::iconNameForUrl(url, mode) != iconStr )
835  iconStr = "document-multiple";
836  if ( url.directory() != directory )
837  directory.clear();
838  if ( url.protocol() != protocol )
839  protocol.clear();
840  if ( !mimeComment.isNull() && (*kit).mimeComment() != mimeComment )
841  mimeComment.clear();
842  if ( isLocal && !magicMimeComment.isNull() ) {
843  KMimeType::Ptr magicMimeType = KMimeType::findByFileContent(url.toLocalFile());
844  if ( magicMimeType->comment() != magicMimeComment )
845  magicMimeComment.clear();
846  }
847 
848  if ( isLocal && url.path() == QLatin1String("/") )
849  hasRoot = true;
850  if ( (*kit).isDir() && !(*kit).isLink() )
851  {
852  iDirCount++;
853  hasDirs = true;
854  }
855  else
856  {
857  iFileCount++;
858  totalSize += (*kit).size();
859  }
860  }
861  }
862 
863  if (!isReallyLocal && !protocol.isEmpty())
864  {
865  directory += ' ';
866  directory += '(';
867  directory += protocol;
868  directory += ')';
869  }
870 
871  if (!isTrash && (bDesktopFile || S_ISDIR(mode))
872  && !d->bMultiple // not implemented for multiple
873  && enableIconButton()) // #56857
874  {
875  KIconButton *iconButton = new KIconButton( d->m_frame );
876  int bsize = 66 + 2 * iconButton->style()->pixelMetric(QStyle::PM_ButtonMargin);
877  iconButton->setFixedSize(bsize, bsize);
878  iconButton->setIconSize(48);
879  iconButton->setStrictIconSize(false);
880  QString iconStr = KMimeType::findByUrl(url, mode)->iconName(url);
881  if (bDesktopFile && isLocal) {
882  KDesktopFile config(url.toLocalFile());
883  KConfigGroup group = config.desktopGroup();
884  iconStr = group.readEntry( "Icon" );
885  if ( config.hasDeviceType() )
886  iconButton->setIconType( KIconLoader::Desktop, KIconLoader::Device );
887  else
888  iconButton->setIconType( KIconLoader::Desktop, KIconLoader::Application );
889  } else {
890  iconButton->setIconType( KIconLoader::Desktop, KIconLoader::Place );
891  }
892  iconButton->setIcon(iconStr);
893  d->iconArea = iconButton;
894  connect(iconButton, SIGNAL(iconChanged(QString)),
895  this, SLOT(slotIconChanged()));
896  } else {
897  QLabel *iconLabel = new QLabel( d->m_frame );
898  int bsize = 66 + 2 * iconLabel->style()->pixelMetric(QStyle::PM_ButtonMargin);
899  iconLabel->setFixedSize(bsize, bsize);
900  iconLabel->setPixmap( KIconLoader::global()->loadIcon( iconStr, KIconLoader::Desktop, 48) );
901  d->iconArea = iconLabel;
902  }
903  grid->addWidget(d->iconArea, curRow, 0, Qt::AlignLeft);
904 
905  if (d->bMultiple || isTrash || hasRoot)
906  {
907  QLabel *lab = new QLabel(d->m_frame );
908  if ( d->bMultiple )
909  lab->setText( KIO::itemsSummaryString( iFileCount + iDirCount, iFileCount, iDirCount, 0, false ) );
910  else
911  lab->setText( filename );
912  d->nameArea = lab;
913  } else
914  {
915  d->m_lined = new KLineEdit( d->m_frame );
916  d->m_lined->setText(filename);
917  d->nameArea = d->m_lined;
918  d->m_lined->setFocus();
919 
920  //if we don't have permissions to rename, we need to make "m_lined" read only.
921  KFileItemListProperties itemList(KFileItemList()<< item);
922  setFileNameReadOnly(!itemList.supportsMoving());
923 
924  // Enhanced rename: Don't highlight the file extension.
925  QString extension = KMimeType::extractKnownExtension( filename );
926  if ( !extension.isEmpty() )
927  d->m_lined->setSelection( 0, filename.length() - extension.length() - 1 );
928  else
929  {
930  int lastDot = filename.lastIndexOf('.');
931  if (lastDot > 0)
932  d->m_lined->setSelection(0, lastDot);
933  }
934 
935  connect( d->m_lined, SIGNAL(textChanged(QString)),
936  this, SLOT(nameFileChanged(QString)) );
937  }
938 
939  grid->addWidget(d->nameArea, curRow++, 2);
940 
941  KSeparator* sep = new KSeparator( Qt::Horizontal, d->m_frame);
942  grid->addWidget(sep, curRow, 0, 1, 3);
943  ++curRow;
944 
945  QLabel *l;
946  if (!mimeComment.isEmpty() && !isTrash) {
947  l = new QLabel(i18n("Type:"), d->m_frame );
948  grid->addWidget(l, curRow, 0, Qt::AlignRight | Qt::AlignTop);
949 
950  KVBox *box = new KVBox(d->m_frame);
951  box->setSpacing(2); // without that spacing the button literally “sticks” to the label ;)
952  l = new QLabel(mimeComment, box );
953  grid->addWidget(box, curRow++, 2);
954 
955  QPushButton *button = new QPushButton(box);
956  button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); // Minimum still makes the button grow to the entire layout width
957  button->setIcon( KIcon(QString::fromLatin1("configure")) );
958 
959  if ( d->mimeType == KMimeType::defaultMimeType() )
960  button->setText(i18n("Create New File Type"));
961  else
962  button->setText(i18n("File Type Options"));
963 
964  connect( button, SIGNAL(clicked()), SLOT(slotEditFileType()));
965 
966  if (!KAuthorized::authorizeKAction("editfiletype"))
967  button->hide();
968  }
969 
970  if ( !magicMimeComment.isEmpty() && magicMimeComment != mimeComment )
971  {
972  l = new QLabel(i18n("Contents:"), d->m_frame );
973  grid->addWidget(l, curRow, 0, Qt::AlignRight);
974 
975  l = new QLabel(magicMimeComment, d->m_frame );
976  grid->addWidget(l, curRow++, 2);
977  }
978 
979  if ( !directory.isEmpty() )
980  {
981  l = new QLabel( i18n("Location:"), d->m_frame );
982  grid->addWidget(l, curRow, 0, Qt::AlignRight);
983 
984  l = new KSqueezedTextLabel( directory, d->m_frame );
985  // force the layout direction to be always LTR
986  l->setLayoutDirection(Qt::LeftToRight);
987  // but if we are in RTL mode, align the text to the right
988  // otherwise the text is on the wrong side of the dialog
989  if (properties->layoutDirection() == Qt::RightToLeft)
990  l->setAlignment( Qt::AlignRight );
991  l->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard);
992  grid->addWidget(l, curRow++, 2);
993  }
994 
995  l = new QLabel(i18n("Size:"), d->m_frame );
996  grid->addWidget(l, curRow, 0, Qt::AlignRight);
997 
998  d->m_sizeLabel = new QLabel( d->m_frame );
999  grid->addWidget( d->m_sizeLabel, curRow++, 2 );
1000 
1001  if ( !hasDirs ) // Only files [and symlinks]
1002  {
1003  d->m_sizeLabel->setText(QString::fromLatin1("%1 (%2)").arg(KIO::convertSize(totalSize))
1004  .arg(KGlobal::locale()->formatNumber(totalSize, 0)));
1005  d->m_sizeDetermineButton = 0L;
1006  d->m_sizeStopButton = 0L;
1007  }
1008  else // Directory
1009  {
1010  QHBoxLayout * sizelay = new QHBoxLayout();
1011  grid->addLayout( sizelay, curRow++, 2 );
1012 
1013  // buttons
1014  d->m_sizeDetermineButton = new QPushButton( i18n("Calculate"), d->m_frame );
1015  d->m_sizeStopButton = new QPushButton( i18n("Stop"), d->m_frame );
1016  connect( d->m_sizeDetermineButton, SIGNAL(clicked()), this, SLOT(slotSizeDetermine()) );
1017  connect( d->m_sizeStopButton, SIGNAL(clicked()), this, SLOT(slotSizeStop()) );
1018  sizelay->addWidget(d->m_sizeDetermineButton, 0);
1019  sizelay->addWidget(d->m_sizeStopButton, 0);
1020  sizelay->addStretch(10); // so that the buttons don't grow horizontally
1021 
1022  // auto-launch for local dirs only, and not for '/'
1023  if ( isLocal && !hasRoot )
1024  {
1025  d->m_sizeDetermineButton->setText( i18n("Refresh") );
1026  slotSizeDetermine();
1027  }
1028  else
1029  d->m_sizeStopButton->setEnabled( false );
1030  }
1031 
1032  if (!d->bMultiple && item.isLink()) {
1033  l = new QLabel(i18n("Points to:"), d->m_frame );
1034  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1035 
1036  d->m_linkTargetLineEdit = new KLineEdit(item.linkDest(), d->m_frame );
1037  grid->addWidget(d->m_linkTargetLineEdit, curRow++, 2);
1038  connect(d->m_linkTargetLineEdit, SIGNAL(textChanged(QString)), this, SLOT(setDirty()));
1039  }
1040 
1041  if (!d->bMultiple) // Dates for multiple don't make much sense...
1042  {
1043  KDateTime dt = item.time(KFileItem::CreationTime);
1044  if ( !dt.isNull() )
1045  {
1046  l = new QLabel(i18n("Created:"), d->m_frame );
1047  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1048 
1049  l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
1050  grid->addWidget(l, curRow++, 2);
1051  }
1052 
1053  dt = item.time(KFileItem::ModificationTime);
1054  if ( !dt.isNull() )
1055  {
1056  l = new QLabel(i18n("Modified:"), d->m_frame );
1057  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1058 
1059  l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
1060  grid->addWidget(l, curRow++, 2);
1061  }
1062 
1063  dt = item.time(KFileItem::AccessTime);
1064  if ( !dt.isNull() )
1065  {
1066  l = new QLabel(i18n("Accessed:"), d->m_frame );
1067  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1068 
1069  l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
1070  grid->addWidget(l, curRow++, 2);
1071  }
1072  }
1073 
1074  if ( isLocal && hasDirs ) // only for directories
1075  {
1076 
1077  KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(url.toLocalFile());
1078  if (mp) {
1079  KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo( mp->mountPoint() );
1080  if(info.size() != 0 )
1081  {
1082  sep = new KSeparator( Qt::Horizontal, d->m_frame);
1083  grid->addWidget(sep, curRow, 0, 1, 3);
1084  ++curRow;
1085  if (mp->mountPoint() != "/")
1086  {
1087  l = new QLabel(i18n("Mounted on:"), d->m_frame );
1088  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1089 
1090  l = new KSqueezedTextLabel( mp->mountPoint(), d->m_frame );
1091  l->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard);
1092  grid->addWidget( l, curRow++, 2 );
1093  }
1094 
1095  l = new QLabel(i18n("Device usage:"), d->m_frame );
1096  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1097 
1098  d->m_capacityBar = new KCapacityBar( KCapacityBar::DrawTextOutline, d->m_frame );
1099  grid->addWidget( d->m_capacityBar, curRow++, 2);
1100 
1101  slotFoundMountPoint( info.mountPoint(), info.size()/1024, info.used()/1024, info.available()/1024);
1102  }
1103  }
1104  }
1105 
1106  vbl->addStretch(1);
1107 }
1108 
1109 bool KFilePropsPlugin::enableIconButton() const
1110 {
1111  bool iconEnabled = false;
1112  const KFileItem item = properties->item();
1113  // If the current item is a directory, check if it's writable,
1114  // so we can create/update a .directory
1115  // Current item is a file, same thing: check if it is writable
1116  if (item.isWritable()) {
1117  iconEnabled = true;
1118  }
1119  return iconEnabled;
1120 }
1121 
1122 // QString KFilePropsPlugin::tabName () const
1123 // {
1124 // return i18n ("&General");
1125 // }
1126 
1127 void KFilePropsPlugin::setFileNameReadOnly( bool ro )
1128 {
1129  if ( d->m_lined && !d->m_bFromTemplate )
1130  {
1131  d->m_lined->setReadOnly( ro );
1132  if (ro)
1133  {
1134  // Don't put the initial focus on the line edit when it is ro
1135  properties->setButtonFocus(KDialog::Ok);
1136  }
1137  }
1138 }
1139 
1140 void KFilePropsPlugin::slotEditFileType()
1141 {
1142  QString mime;
1143  if (d->mimeType == KMimeType::defaultMimeType()) {
1144  const int pos = d->oldFileName.lastIndexOf('.');
1145  if (pos != -1)
1146  mime = '*' + d->oldFileName.mid(pos);
1147  else
1148  mime = '*';
1149  } else {
1150  mime = d->mimeType;
1151  }
1152  QString keditfiletype = QString::fromLatin1("keditfiletype");
1153  KRun::runCommand( keditfiletype
1154 #ifdef Q_WS_X11
1155  + " --parent " + QString::number( (ulong)properties->window()->winId())
1156 #endif
1157  + " --caption " + KShell::quoteArg(KGlobal::caption())
1158  + ' ' + KShell::quoteArg(mime),
1159  keditfiletype, keditfiletype /*unused*/, properties->window());
1160 }
1161 
1162 void KFilePropsPlugin::slotIconChanged()
1163 {
1164  d->bIconChanged = true;
1165  emit changed();
1166 }
1167 
1168 void KFilePropsPlugin::nameFileChanged(const QString &text )
1169 {
1170  properties->enableButtonOk(!text.isEmpty());
1171  emit changed();
1172 }
1173 
1174 void KFilePropsPlugin::determineRelativePath( const QString & path )
1175 {
1176  // now let's make it relative
1177  d->m_sRelativePath = KGlobal::dirs()->relativeLocation("apps", path);
1178  if (d->m_sRelativePath.startsWith('/'))
1179  {
1180  d->m_sRelativePath =KGlobal::dirs()->relativeLocation("xdgdata-apps", path);
1181  if (d->m_sRelativePath.startsWith('/'))
1182  d->m_sRelativePath.clear();
1183  else
1184  d->m_sRelativePath = path;
1185  }
1186 }
1187 
1188 void KFilePropsPlugin::slotFoundMountPoint( const QString&,
1189  quint64 kibSize,
1190  quint64 /*kibUsed*/,
1191  quint64 kibAvail )
1192 {
1193  d->m_capacityBar->setText(
1194  i18nc("Available space out of total partition size (percent used)", "%1 free of %2 (%3% used)",
1195  KIO::convertSizeFromKiB(kibAvail),
1196  KIO::convertSizeFromKiB(kibSize),
1197  100 - (int)(100.0 * kibAvail / kibSize) ));
1198 
1199  d->m_capacityBar->setValue(100 - (int)(100.0 * kibAvail / kibSize));
1200 }
1201 
1202 void KFilePropsPlugin::slotDirSizeUpdate()
1203 {
1204  KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
1205  KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles();
1206  KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs();
1207  d->m_sizeLabel->setText(
1208  i18n("Calculating... %1 (%2)\n%3, %4",
1209  KIO::convertSize(totalSize),
1210  totalSize,
1211  i18np("1 file", "%1 files", totalFiles),
1212  i18np("1 sub-folder", "%1 sub-folders", totalSubdirs)));
1213 }
1214 
1215 void KFilePropsPlugin::slotDirSizeFinished( KJob * job )
1216 {
1217  if (job->error())
1218  d->m_sizeLabel->setText( job->errorString() );
1219  else
1220  {
1221  KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
1222  KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles();
1223  KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs();
1224  d->m_sizeLabel->setText( QString::fromLatin1("%1 (%2)\n%3, %4")
1225  .arg(KIO::convertSize(totalSize))
1226  .arg(KGlobal::locale()->formatNumber(totalSize, 0))
1227  .arg(i18np("1 file","%1 files",totalFiles))
1228  .arg(i18np("1 sub-folder","%1 sub-folders",totalSubdirs)));
1229  }
1230  d->m_sizeStopButton->setEnabled(false);
1231  // just in case you change something and try again :)
1232  d->m_sizeDetermineButton->setText( i18n("Refresh") );
1233  d->m_sizeDetermineButton->setEnabled(true);
1234  d->dirSizeJob = 0;
1235  delete d->dirSizeUpdateTimer;
1236  d->dirSizeUpdateTimer = 0;
1237 }
1238 
1239 void KFilePropsPlugin::slotSizeDetermine()
1240 {
1241  d->m_sizeLabel->setText( i18n("Calculating...") );
1242  kDebug(250) << " KFilePropsPlugin::slotSizeDetermine() properties->item()=" << properties->item();
1243  kDebug(250) << " URL=" << properties->item().url().url();
1244 
1245  d->dirSizeJob = KIO::directorySize( properties->items() );
1246  d->dirSizeUpdateTimer = new QTimer(this);
1247  connect( d->dirSizeUpdateTimer, SIGNAL(timeout()),
1248  SLOT(slotDirSizeUpdate()) );
1249  d->dirSizeUpdateTimer->start(500);
1250  connect( d->dirSizeJob, SIGNAL(result(KJob*)),
1251  SLOT(slotDirSizeFinished(KJob*)) );
1252  d->m_sizeStopButton->setEnabled(true);
1253  d->m_sizeDetermineButton->setEnabled(false);
1254 
1255  // also update the "Free disk space" display
1256  if ( d->m_capacityBar )
1257  {
1258  bool isLocal;
1259  const KFileItem item = properties->item();
1260  KUrl url = item.mostLocalUrl( isLocal );
1261  if (isLocal) {
1262  KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(url.toLocalFile());
1263  if (mp) {
1264  KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo( mp->mountPoint() );
1265  slotFoundMountPoint( info.mountPoint(), info.size()/1024, info.used()/1024, info.available()/1024);
1266  }
1267  }
1268  }
1269 }
1270 
1271 void KFilePropsPlugin::slotSizeStop()
1272 {
1273  if ( d->dirSizeJob )
1274  {
1275  KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
1276  d->m_sizeLabel->setText(i18n("At least %1",
1277  KIO::convertSize(totalSize)));
1278  d->dirSizeJob->kill();
1279  d->dirSizeJob = 0;
1280  }
1281  if ( d->dirSizeUpdateTimer )
1282  d->dirSizeUpdateTimer->stop();
1283 
1284  d->m_sizeStopButton->setEnabled(false);
1285  d->m_sizeDetermineButton->setEnabled(true);
1286 }
1287 
1288 KFilePropsPlugin::~KFilePropsPlugin()
1289 {
1290  delete d;
1291 }
1292 
1293 bool KFilePropsPlugin::supports( const KFileItemList& /*_items*/ )
1294 {
1295  return true;
1296 }
1297 
1298 void KFilePropsPlugin::applyChanges()
1299 {
1300  if ( d->dirSizeJob )
1301  slotSizeStop();
1302 
1303  kDebug(250) << "KFilePropsPlugin::applyChanges";
1304 
1305  if (qobject_cast<QLineEdit*>(d->nameArea))
1306  {
1307  QString n = ((QLineEdit *) d->nameArea)->text();
1308  // Remove trailing spaces (#4345)
1309  while ( ! n.isEmpty() && n[n.length()-1].isSpace() )
1310  n.truncate( n.length() - 1 );
1311  if ( n.isEmpty() )
1312  {
1313  KMessageBox::sorry( properties, i18n("The new file name is empty."));
1314  properties->abortApplying();
1315  return;
1316  }
1317 
1318  // Do we need to rename the file ?
1319  kDebug(250) << "oldname = " << d->oldName;
1320  kDebug(250) << "newname = " << n;
1321  if ( d->oldName != n || d->m_bFromTemplate ) { // true for any from-template file
1322  KIO::Job * job = 0L;
1323  KUrl oldurl = properties->kurl();
1324 
1325  QString newFileName = KIO::encodeFileName(n);
1326  if (d->bDesktopFile && !newFileName.endsWith(QLatin1String(".desktop")) &&
1327  !newFileName.endsWith(QLatin1String(".kdelnk")))
1328  newFileName += ".desktop";
1329 
1330  // Tell properties. Warning, this changes the result of properties->kurl() !
1331  properties->rename( newFileName );
1332 
1333  // Update also relative path (for apps and mimetypes)
1334  if ( !d->m_sRelativePath.isEmpty() )
1335  determineRelativePath( properties->kurl().toLocalFile() );
1336 
1337  kDebug(250) << "New URL = " << properties->kurl().url();
1338  kDebug(250) << "old = " << oldurl.url();
1339 
1340  // Don't remove the template !!
1341  if ( !d->m_bFromTemplate ) // (normal renaming)
1342  job = KIO::moveAs( oldurl, properties->kurl() );
1343  else // Copying a template
1344  job = KIO::copyAs( oldurl, properties->kurl() );
1345 
1346  connect( job, SIGNAL(result(KJob*)),
1347  SLOT(slotCopyFinished(KJob*)) );
1348  connect( job, SIGNAL(renamed(KIO::Job*,KUrl,KUrl)),
1349  SLOT(slotFileRenamed(KIO::Job*,KUrl,KUrl)) );
1350  // wait for job
1351  QEventLoop eventLoop;
1352  connect(this, SIGNAL(leaveModality()),
1353  &eventLoop, SLOT(quit()));
1354  eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
1355  return;
1356  }
1357  properties->updateUrl(properties->kurl());
1358  // Update also relative path (for apps and mimetypes)
1359  if ( !d->m_sRelativePath.isEmpty() )
1360  determineRelativePath( properties->kurl().toLocalFile() );
1361  }
1362 
1363  // No job, keep going
1364  slotCopyFinished( 0L );
1365 }
1366 
1367 void KFilePropsPlugin::slotCopyFinished( KJob * job )
1368 {
1369  kDebug(250) << "KFilePropsPlugin::slotCopyFinished";
1370  if (job)
1371  {
1372  // allow apply() to return
1373  emit leaveModality();
1374  if ( job->error() )
1375  {
1376  job->uiDelegate()->showErrorMessage();
1377  // Didn't work. Revert the URL to the old one
1378  properties->updateUrl( static_cast<KIO::CopyJob*>(job)->srcUrls().first() );
1379  properties->abortApplying(); // Don't apply the changes to the wrong file !
1380  return;
1381  }
1382  }
1383 
1384  Q_ASSERT( !properties->item().isNull() );
1385  Q_ASSERT( !properties->item().url().isEmpty() );
1386 
1387  // Save the file where we can -> usually in ~/.kde/...
1388  if (d->bDesktopFile && !d->m_sRelativePath.isEmpty())
1389  {
1390  kDebug(250) << "KFilePropsPlugin::slotCopyFinished " << d->m_sRelativePath;
1391  KUrl newURL;
1392  newURL.setPath( KDesktopFile::locateLocal(d->m_sRelativePath) );
1393  kDebug(250) << "KFilePropsPlugin::slotCopyFinished path=" << newURL.path();
1394  properties->updateUrl( newURL );
1395  }
1396 
1397  if ( d->bKDesktopMode && d->bDesktopFile ) {
1398  // Renamed? Update Name field
1399  // Note: The desktop ioslave does this as well, but not when
1400  // the file is copied from a template.
1401  if ( d->m_bFromTemplate ) {
1402  KIO::UDSEntry entry;
1403  KIO::NetAccess::stat( properties->kurl(), entry, 0 );
1404  KFileItem item( entry, properties->kurl() );
1405  KDesktopFile config( item.localPath() );
1406  KConfigGroup cg = config.desktopGroup();
1407  QString nameStr = nameFromFileName(properties->kurl().fileName());
1408  cg.writeEntry( "Name", nameStr );
1409  cg.writeEntry( "Name", nameStr, KConfigGroup::Persistent|KConfigGroup::Localized);
1410  }
1411  }
1412 
1413  if (d->m_linkTargetLineEdit && !d->bMultiple) {
1414  const KFileItem item = properties->item();
1415  const QString newTarget = d->m_linkTargetLineEdit->text();
1416  if (newTarget != item.linkDest()) {
1417  kDebug(250) << "Updating target of symlink to" << newTarget;
1418  KIO::Job* job = KIO::symlink(newTarget, item.url(), KIO::Overwrite);
1419  job->ui()->setAutoErrorHandlingEnabled(true);
1420  job->exec();
1421  }
1422  }
1423 
1424  // "Link to Application" templates need to be made executable
1425  // Instead of matching against a filename we check if the destination
1426  // is an Application now.
1427  if ( d->m_bFromTemplate ) {
1428  // destination is not necessarily local, use the src template
1429  KDesktopFile templateResult ( static_cast<KIO::CopyJob*>(job)->srcUrls().first().toLocalFile() );
1430  if ( templateResult.hasApplicationType() ) {
1431  // We can either stat the file and add the +x bit or use the larger chmod() job
1432  // with a umask designed to only touch u+x. This is only one KIO job, so let's
1433  // do that.
1434 
1435  KFileItem appLink ( properties->item() );
1436  KFileItemList fileItemList;
1437  fileItemList << appLink;
1438 
1439  // first 0100 adds u+x, second 0100 only allows chmod to change u+x
1440  KIO::Job* chmodJob = KIO::chmod( fileItemList, 0100, 0100, QString(), QString(), KIO::HideProgressInfo );
1441  chmodJob->exec();
1442  }
1443  }
1444 }
1445 
1446 void KFilePropsPlugin::applyIconChanges()
1447 {
1448  KIconButton *iconButton = qobject_cast<KIconButton*>(d->iconArea);
1449  if ( !iconButton || !d->bIconChanged )
1450  return;
1451  // handle icon changes - only local files (or pseudo-local) for now
1452  // TODO: Use KTempFile and KIO::file_copy with overwrite = true
1453  KUrl url = properties->kurl();
1454  url = KIO::NetAccess::mostLocalUrl( url, properties );
1455  if ( url.isLocalFile()) {
1456  QString path;
1457 
1458  if (S_ISDIR(properties->item().mode()))
1459  {
1460  path = url.toLocalFile(KUrl::AddTrailingSlash) + QString::fromLatin1(".directory");
1461  // don't call updateUrl because the other tabs (i.e. permissions)
1462  // apply to the directory, not the .directory file.
1463  }
1464  else
1465  path = url.toLocalFile();
1466 
1467  // Get the default image
1468  QString str = KMimeType::findByUrl( url,
1469  properties->item().mode(),
1470  true )->iconName();
1471  // Is it another one than the default ?
1472  QString sIcon;
1473  if ( str != iconButton->icon() )
1474  sIcon = iconButton->icon();
1475  // (otherwise write empty value)
1476 
1477  kDebug(250) << "**" << path << "**";
1478 
1479  // If default icon and no .directory file -> don't create one
1480  if ( !sIcon.isEmpty() || QFile::exists(path) )
1481  {
1482  KDesktopFile cfg(path);
1483  kDebug(250) << "sIcon = " << (sIcon);
1484  kDebug(250) << "str = " << (str);
1485  cfg.desktopGroup().writeEntry( "Icon", sIcon );
1486  cfg.sync();
1487 
1488  cfg.reparseConfiguration();
1489  if ( cfg.desktopGroup().readEntry("Icon") != sIcon ) {
1490  KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not "
1491  "have sufficient access to write to <b>%1</b>.</qt>", path));
1492  }
1493  }
1494  }
1495 }
1496 
1497 void KFilePropsPlugin::slotFileRenamed( KIO::Job *, const KUrl &, const KUrl & newUrl )
1498 {
1499  // This is called in case of an existing local file during the copy/move operation,
1500  // if the user chooses Rename.
1501  properties->updateUrl( newUrl );
1502 }
1503 
1504 void KFilePropsPlugin::postApplyChanges()
1505 {
1506  // Save the icon only after applying the permissions changes (#46192)
1507  applyIconChanges();
1508 
1509  const KFileItemList items = properties->items();
1510  const KUrl::List lst = items.urlList();
1511  org::kde::KDirNotify::emitFilesChanged( lst.toStringList() );
1512 }
1513 
1514 class KFilePermissionsPropsPlugin::KFilePermissionsPropsPluginPrivate
1515 {
1516 public:
1517  KFilePermissionsPropsPluginPrivate()
1518  {
1519  }
1520  ~KFilePermissionsPropsPluginPrivate()
1521  {
1522  }
1523 
1524  QFrame *m_frame;
1525  QCheckBox *cbRecursive;
1526  QLabel *explanationLabel;
1527  KComboBox *ownerPermCombo, *groupPermCombo, *othersPermCombo;
1528  QCheckBox *extraCheckbox;
1529  mode_t partialPermissions;
1530  KFilePermissionsPropsPlugin::PermissionsMode pmode;
1531  bool canChangePermissions;
1532  bool isIrregular;
1533  bool hasExtendedACL;
1534  KACL extendedACL;
1535  KACL defaultACL;
1536  bool fileSystemSupportsACLs;
1537 
1538  KComboBox *grpCombo;
1539 
1540  KLineEdit *usrEdit;
1541  KLineEdit *grpEdit;
1542 
1543  // Old permissions
1544  mode_t permissions;
1545  // Old group
1546  QString strGroup;
1547  // Old owner
1548  QString strOwner;
1549 };
1550 
1551 #define UniOwner (S_IRUSR|S_IWUSR|S_IXUSR)
1552 #define UniGroup (S_IRGRP|S_IWGRP|S_IXGRP)
1553 #define UniOthers (S_IROTH|S_IWOTH|S_IXOTH)
1554 #define UniRead (S_IRUSR|S_IRGRP|S_IROTH)
1555 #define UniWrite (S_IWUSR|S_IWGRP|S_IWOTH)
1556 #define UniExec (S_IXUSR|S_IXGRP|S_IXOTH)
1557 #define UniSpecial (S_ISUID|S_ISGID|S_ISVTX)
1558 
1559 // synced with PermissionsTarget
1560 const mode_t KFilePermissionsPropsPlugin::permissionsMasks[3] = {UniOwner, UniGroup, UniOthers};
1561 const mode_t KFilePermissionsPropsPlugin::standardPermissions[4] = { 0, UniRead, UniRead|UniWrite, (mode_t)-1 };
1562 
1563 // synced with PermissionsMode and standardPermissions
1564 const char *KFilePermissionsPropsPlugin::permissionsTexts[4][4] = {
1565  { I18N_NOOP("Forbidden"),
1566  I18N_NOOP("Can Read"),
1567  I18N_NOOP("Can Read & Write"),
1568  0 },
1569 { I18N_NOOP("Forbidden"),
1570  I18N_NOOP("Can View Content"),
1571  I18N_NOOP("Can View & Modify Content"),
1572  0 },
1573 { 0, 0, 0, 0}, // no texts for links
1574 { I18N_NOOP("Forbidden"),
1575  I18N_NOOP("Can View Content & Read"),
1576  I18N_NOOP("Can View/Read & Modify/Write"),
1577  0 }
1578 };
1579 
1580 
1581 KFilePermissionsPropsPlugin::KFilePermissionsPropsPlugin( KPropertiesDialog *_props )
1582  : KPropertiesDialogPlugin( _props ),d(new KFilePermissionsPropsPluginPrivate)
1583 {
1584  d->cbRecursive = 0L;
1585  d->grpCombo = 0L; d->grpEdit = 0;
1586  d->usrEdit = 0L;
1587  QString path = properties->kurl().path(KUrl::RemoveTrailingSlash);
1588  QString fname = properties->kurl().fileName();
1589  bool isLocal = properties->kurl().isLocalFile();
1590  bool isTrash = ( properties->kurl().protocol().toLower() == "trash" );
1591  bool IamRoot = (geteuid() == 0);
1592 
1593  const KFileItem item = properties->item();
1594  bool isLink = item.isLink();
1595  bool isDir = item.isDir(); // all dirs
1596  bool hasDir = item.isDir(); // at least one dir
1597  d->permissions = item.permissions(); // common permissions to all files
1598  d->partialPermissions = d->permissions; // permissions that only some files have (at first we take everything)
1599  d->isIrregular = isIrregular(d->permissions, isDir, isLink);
1600  d->strOwner = item.user();
1601  d->strGroup = item.group();
1602  d->hasExtendedACL = item.ACL().isExtended() || item.defaultACL().isValid();
1603  d->extendedACL = item.ACL();
1604  d->defaultACL = item.defaultACL();
1605  d->fileSystemSupportsACLs = false;
1606 
1607  if ( properties->items().count() > 1 )
1608  {
1609  // Multiple items: see what they have in common
1610  const KFileItemList items = properties->items();
1611  KFileItemList::const_iterator it = items.begin();
1612  const KFileItemList::const_iterator kend = items.end();
1613  for ( ++it /*no need to check the first one again*/ ; it != kend; ++it )
1614  {
1615  const KUrl url = (*it).url();
1616  if (!d->isIrregular)
1617  d->isIrregular |= isIrregular((*it).permissions(),
1618  (*it).isDir() == isDir,
1619  (*it).isLink() == isLink);
1620  d->hasExtendedACL = d->hasExtendedACL || (*it).hasExtendedACL();
1621  if ( (*it).isLink() != isLink )
1622  isLink = false;
1623  if ( (*it).isDir() != isDir )
1624  isDir = false;
1625  hasDir |= (*it).isDir();
1626  if ( (*it).permissions() != d->permissions )
1627  {
1628  d->permissions &= (*it).permissions();
1629  d->partialPermissions |= (*it).permissions();
1630  }
1631  if ( (*it).user() != d->strOwner )
1632  d->strOwner.clear();
1633  if ( (*it).group() != d->strGroup )
1634  d->strGroup.clear();
1635  }
1636  }
1637 
1638  if (isLink)
1639  d->pmode = PermissionsOnlyLinks;
1640  else if (isDir)
1641  d->pmode = PermissionsOnlyDirs;
1642  else if (hasDir)
1643  d->pmode = PermissionsMixed;
1644  else
1645  d->pmode = PermissionsOnlyFiles;
1646 
1647  // keep only what's not in the common permissions
1648  d->partialPermissions = d->partialPermissions & ~d->permissions;
1649 
1650  bool isMyFile = false;
1651 
1652  if (isLocal && !d->strOwner.isEmpty()) { // local files, and all owned by the same person
1653  struct passwd *myself = getpwuid( geteuid() );
1654  if ( myself != 0L )
1655  {
1656  isMyFile = (d->strOwner == QString::fromLocal8Bit(myself->pw_name));
1657  } else
1658  kWarning() << "I don't exist ?! geteuid=" << geteuid();
1659  } else {
1660  //We don't know, for remote files, if they are ours or not.
1661  //So we let the user change permissions, and
1662  //KIO::chmod will tell, if he had no right to do it.
1663  isMyFile = true;
1664  }
1665 
1666  d->canChangePermissions = (isMyFile || IamRoot) && (!isLink);
1667 
1668 
1669  // create GUI
1670 
1671  d->m_frame = new QFrame();
1672  properties->addPage( d->m_frame, i18n("&Permissions") );
1673 
1674  QBoxLayout *box = new QVBoxLayout( d->m_frame );
1675  box->setMargin( 0 );
1676 
1677  QWidget *l;
1678  QLabel *lbl;
1679  QGroupBox *gb;
1680  QGridLayout *gl;
1681  QPushButton* pbAdvancedPerm = 0;
1682 
1683  /* Group: Access Permissions */
1684  gb = new QGroupBox ( i18n("Access Permissions"), d->m_frame );
1685  box->addWidget (gb);
1686 
1687  gl = new QGridLayout (gb);
1688  gl->setColumnStretch(1, 1);
1689 
1690  l = d->explanationLabel = new QLabel( "", gb );
1691  if (isLink)
1692  d->explanationLabel->setText(i18np("This file is a link and does not have permissions.",
1693  "All files are links and do not have permissions.",
1694  properties->items().count()));
1695  else if (!d->canChangePermissions)
1696  d->explanationLabel->setText(i18n("Only the owner can change permissions."));
1697  gl->addWidget(l, 0, 0, 1, 2);
1698 
1699  lbl = new QLabel( i18n("O&wner:"), gb);
1700  gl->addWidget(lbl, 1, 0, Qt::AlignRight);
1701  l = d->ownerPermCombo = new KComboBox(gb);
1702  lbl->setBuddy(l);
1703  gl->addWidget(l, 1, 1);
1704  connect(l, SIGNAL(activated(int)), this, SIGNAL(changed()));
1705  l->setWhatsThis(i18n("Specifies the actions that the owner is allowed to do."));
1706 
1707  lbl = new QLabel( i18n("Gro&up:"), gb);
1708  gl->addWidget(lbl, 2, 0, Qt::AlignRight);
1709  l = d->groupPermCombo = new KComboBox(gb);
1710  lbl->setBuddy(l);
1711  gl->addWidget(l, 2, 1);
1712  connect(l, SIGNAL(activated(int)), this, SIGNAL(changed()));
1713  l->setWhatsThis(i18n("Specifies the actions that the members of the group are allowed to do."));
1714 
1715  lbl = new QLabel( i18n("O&thers:"), gb);
1716  gl->addWidget(lbl, 3, 0, Qt::AlignRight);
1717  l = d->othersPermCombo = new KComboBox(gb);
1718  lbl->setBuddy(l);
1719  gl->addWidget(l, 3, 1);
1720  connect(l, SIGNAL(activated(int)), this, SIGNAL(changed()));
1721  l->setWhatsThis(i18n("Specifies the actions that all users, who are neither "
1722  "owner nor in the group, are allowed to do."));
1723 
1724  if (!isLink) {
1725  l = d->extraCheckbox = new QCheckBox(hasDir ?
1726  i18n("Only own&er can rename and delete folder content") :
1727  i18n("Is &executable"),
1728  gb );
1729  connect( d->extraCheckbox, SIGNAL(clicked()), this, SIGNAL(changed()) );
1730  gl->addWidget(l, 4, 1);
1731  l->setWhatsThis(hasDir ? i18n("Enable this option to allow only the folder's owner to "
1732  "delete or rename the contained files and folders. Other "
1733  "users can only add new files, which requires the 'Modify "
1734  "Content' permission.")
1735  : i18n("Enable this option to mark the file as executable. This only makes "
1736  "sense for programs and scripts. It is required when you want to "
1737  "execute them."));
1738 
1739  QLayoutItem *spacer = new QSpacerItem(0, 20, QSizePolicy::Minimum, QSizePolicy::Expanding);
1740  gl->addItem(spacer, 5, 0, 1, 3);
1741 
1742  pbAdvancedPerm = new QPushButton(i18n("A&dvanced Permissions"), gb);
1743  gl->addWidget(pbAdvancedPerm, 6, 0, 1, 2, Qt::AlignRight);
1744  connect(pbAdvancedPerm, SIGNAL(clicked()), this, SLOT(slotShowAdvancedPermissions()));
1745  }
1746  else
1747  d->extraCheckbox = 0;
1748 
1749 
1750  /**** Group: Ownership ****/
1751  gb = new QGroupBox ( i18n("Ownership"), d->m_frame );
1752  box->addWidget (gb);
1753 
1754  gl = new QGridLayout (gb);
1755  gl->addItem(new QSpacerItem(0, 10), 0, 0);
1756 
1757  /*** Set Owner ***/
1758  l = new QLabel( i18n("User:"), gb );
1759  gl->addWidget (l, 1, 0, Qt::AlignRight);
1760 
1761  /* GJ: Don't autocomplete more than 1000 users. This is a kind of random
1762  * value. Huge sites having 10.000+ user have a fair chance of using NIS,
1763  * (possibly) making this unacceptably slow.
1764  * OTOH, it is nice to offer this functionality for the standard user.
1765  */
1766  int i, maxEntries = 1000;
1767  struct passwd *user;
1768 
1769  /* File owner: For root, offer a KLineEdit with autocompletion.
1770  * For a user, who can never chown() a file, offer a QLabel.
1771  */
1772  if (IamRoot && isLocal)
1773  {
1774  d->usrEdit = new KLineEdit( gb );
1775  KCompletion *kcom = d->usrEdit->completionObject();
1776  kcom->setOrder(KCompletion::Sorted);
1777  setpwent();
1778  for (i=0; ((user = getpwent()) != 0L) && (i < maxEntries); ++i)
1779  kcom->addItem(QString::fromLatin1(user->pw_name));
1780  endpwent();
1781  d->usrEdit->setCompletionMode((i < maxEntries) ? KGlobalSettings::CompletionAuto :
1782  KGlobalSettings::CompletionNone);
1783  d->usrEdit->setText(d->strOwner);
1784  gl->addWidget(d->usrEdit, 1, 1);
1785  connect( d->usrEdit, SIGNAL(textChanged(QString)),
1786  this, SIGNAL(changed()) );
1787  }
1788  else
1789  {
1790  l = new QLabel(d->strOwner, gb);
1791  gl->addWidget(l, 1, 1);
1792  }
1793 
1794  /*** Set Group ***/
1795 
1796  QStringList groupList;
1797  QByteArray strUser;
1798  user = getpwuid(geteuid());
1799  if (user != 0L)
1800  strUser = user->pw_name;
1801 
1802 #ifdef HAVE_GETGROUPLIST
1803  // pick the groups to which the user belongs
1804  int groupCount = 0;
1805 #ifdef Q_OS_MAC
1806  QVarLengthArray<int> groups;
1807 #else
1808  QVarLengthArray<gid_t> groups;
1809 #endif
1810  if (getgrouplist(strUser, user->pw_gid, NULL, &groupCount) < 0) {
1811  groups.resize(groupCount);
1812  if (groups.data())
1813  getgrouplist(strUser, user->pw_gid, groups.data(), &groupCount);
1814  else
1815  groupCount = 0;
1816  }
1817 
1818  for (i = 0; i < groupCount; i++) {
1819  struct group *mygroup = getgrgid(groups[i]);
1820  if (mygroup)
1821  groupList += QString::fromLocal8Bit(mygroup->gr_name);
1822  }
1823 #endif // HAVE_GETGROUPLIST
1824 
1825  bool isMyGroup = groupList.contains(d->strGroup);
1826 
1827  /* add the group the file currently belongs to ..
1828  * .. if it is not there already
1829  */
1830  if (!isMyGroup)
1831  groupList += d->strGroup;
1832 
1833  l = new QLabel( i18n("Group:"), gb );
1834  gl->addWidget (l, 2, 0, Qt::AlignRight);
1835 
1836  /* Set group: if possible to change:
1837  * - Offer a KLineEdit for root, since he can change to any group.
1838  * - Offer a KComboBox for a normal user, since he can change to a fixed
1839  * (small) set of groups only.
1840  * If not changeable: offer a QLabel.
1841  */
1842  if (IamRoot && isLocal)
1843  {
1844  d->grpEdit = new KLineEdit(gb);
1845  KCompletion *kcom = new KCompletion;
1846  kcom->setItems(groupList);
1847  d->grpEdit->setCompletionObject(kcom, true);
1848  d->grpEdit->setAutoDeleteCompletionObject( true );
1849  d->grpEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
1850  d->grpEdit->setText(d->strGroup);
1851  gl->addWidget(d->grpEdit, 2, 1);
1852  connect( d->grpEdit, SIGNAL(textChanged(QString)),
1853  this, SIGNAL(changed()) );
1854  }
1855  else if ((groupList.count() > 1) && isMyFile && isLocal)
1856  {
1857  d->grpCombo = new KComboBox(gb);
1858  d->grpCombo->setObjectName(QLatin1String("combogrouplist"));
1859  d->grpCombo->addItems(groupList);
1860  d->grpCombo->setCurrentIndex(groupList.indexOf(d->strGroup));
1861  gl->addWidget(d->grpCombo, 2, 1);
1862  connect( d->grpCombo, SIGNAL(activated(int)),
1863  this, SIGNAL(changed()) );
1864  }
1865  else
1866  {
1867  l = new QLabel(d->strGroup, gb);
1868  gl->addWidget(l, 2, 1);
1869  }
1870 
1871  gl->setColumnStretch(2, 10);
1872 
1873  // "Apply recursive" checkbox
1874  if ( hasDir && !isLink && !isTrash )
1875  {
1876  d->cbRecursive = new QCheckBox( i18n("Apply changes to all subfolders and their contents"), d->m_frame );
1877  connect( d->cbRecursive, SIGNAL(clicked()), this, SIGNAL(changed()) );
1878  box->addWidget( d->cbRecursive );
1879  }
1880 
1881  updateAccessControls();
1882 
1883 
1884  if ( isTrash )
1885  {
1886  //don't allow to change properties for file into trash
1887  enableAccessControls(false);
1888  if ( pbAdvancedPerm)
1889  pbAdvancedPerm->setEnabled(false);
1890  }
1891 
1892  box->addStretch (10);
1893 }
1894 
1895 #ifdef HAVE_POSIX_ACL
1896 static bool fileSystemSupportsACL( const QByteArray& path )
1897 {
1898  bool fileSystemSupportsACLs = false;
1899 #ifdef Q_OS_FREEBSD
1900  struct statfs buf;
1901  fileSystemSupportsACLs = ( statfs( path.data(), &buf ) == 0 ) && ( buf.f_flags & MNT_ACLS );
1902 #else
1903  fileSystemSupportsACLs =
1904  getxattr( path.data(), "system.posix_acl_access", NULL, 0 ) >= 0 || errno == ENODATA;
1905 #endif
1906  return fileSystemSupportsACLs;
1907 }
1908 #endif
1909 
1910 
1911 void KFilePermissionsPropsPlugin::slotShowAdvancedPermissions() {
1912 
1913  bool isDir = (d->pmode == PermissionsOnlyDirs) || (d->pmode == PermissionsMixed);
1914  KDialog dlg( properties );
1915  dlg.setModal( true );
1916  dlg.setCaption( i18n("Advanced Permissions") );
1917  dlg.setButtons( KDialog::Ok | KDialog::Cancel );
1918 
1919  QLabel *l, *cl[3];
1920  QGroupBox *gb;
1921  QGridLayout *gl;
1922 
1923  QWidget *mainw = new QWidget( &dlg );
1924  QVBoxLayout *vbox = new QVBoxLayout(mainw);
1925  // Group: Access Permissions
1926  gb = new QGroupBox ( i18n("Access Permissions"), mainw );
1927  vbox->addWidget(gb);
1928 
1929  gl = new QGridLayout (gb);
1930  gl->addItem(new QSpacerItem(0, 10), 0, 0);
1931 
1932  QVector<QWidget*> theNotSpecials;
1933 
1934  l = new QLabel(i18n("Class"), gb );
1935  gl->addWidget(l, 1, 0);
1936  theNotSpecials.append( l );
1937 
1938  if (isDir)
1939  l = new QLabel( i18n("Show\nEntries"), gb );
1940  else
1941  l = new QLabel( i18n("Read"), gb );
1942  gl->addWidget (l, 1, 1);
1943  theNotSpecials.append( l );
1944  QString readWhatsThis;
1945  if (isDir)
1946  readWhatsThis = i18n("This flag allows viewing the content of the folder.");
1947  else
1948  readWhatsThis = i18n("The Read flag allows viewing the content of the file.");
1949  l->setWhatsThis(readWhatsThis);
1950 
1951  if (isDir)
1952  l = new QLabel( i18n("Write\nEntries"), gb );
1953  else
1954  l = new QLabel( i18n("Write"), gb );
1955  gl->addWidget (l, 1, 2);
1956  theNotSpecials.append( l );
1957  QString writeWhatsThis;
1958  if (isDir)
1959  writeWhatsThis = i18n("This flag allows adding, renaming and deleting of files. "
1960  "Note that deleting and renaming can be limited using the Sticky flag.");
1961  else
1962  writeWhatsThis = i18n("The Write flag allows modifying the content of the file.");
1963  l->setWhatsThis(writeWhatsThis);
1964 
1965  QString execWhatsThis;
1966  if (isDir) {
1967  l = new QLabel( i18nc("Enter folder", "Enter"), gb );
1968  execWhatsThis = i18n("Enable this flag to allow entering the folder.");
1969  }
1970  else {
1971  l = new QLabel( i18n("Exec"), gb );
1972  execWhatsThis = i18n("Enable this flag to allow executing the file as a program.");
1973  }
1974  l->setWhatsThis(execWhatsThis);
1975  theNotSpecials.append( l );
1976  // GJ: Add space between normal and special modes
1977  QSize size = l->sizeHint();
1978  size.setWidth(size.width() + 15);
1979  l->setFixedSize(size);
1980  gl->addWidget (l, 1, 3);
1981 
1982  l = new QLabel( i18n("Special"), gb );
1983  gl->addWidget(l, 1, 4, 1, 2);
1984  QString specialWhatsThis;
1985  if (isDir)
1986  specialWhatsThis = i18n("Special flag. Valid for the whole folder, the exact "
1987  "meaning of the flag can be seen in the right hand column.");
1988  else
1989  specialWhatsThis = i18n("Special flag. The exact meaning of the flag can be seen "
1990  "in the right hand column.");
1991  l->setWhatsThis(specialWhatsThis);
1992 
1993  cl[0] = new QLabel( i18n("User"), gb );
1994  gl->addWidget (cl[0], 2, 0);
1995  theNotSpecials.append( cl[0] );
1996 
1997  cl[1] = new QLabel( i18n("Group"), gb );
1998  gl->addWidget (cl[1], 3, 0);
1999  theNotSpecials.append( cl[1] );
2000 
2001  cl[2] = new QLabel( i18n("Others"), gb );
2002  gl->addWidget (cl[2], 4, 0);
2003  theNotSpecials.append( cl[2] );
2004 
2005  l = new QLabel(i18n("Set UID"), gb);
2006  gl->addWidget(l, 2, 5);
2007  QString setUidWhatsThis;
2008  if (isDir)
2009  setUidWhatsThis = i18n("If this flag is set, the owner of this folder will be "
2010  "the owner of all new files.");
2011  else
2012  setUidWhatsThis = i18n("If this file is an executable and the flag is set, it will "
2013  "be executed with the permissions of the owner.");
2014  l->setWhatsThis(setUidWhatsThis);
2015 
2016  l = new QLabel(i18n("Set GID"), gb);
2017  gl->addWidget(l, 3, 5);
2018  QString setGidWhatsThis;
2019  if (isDir)
2020  setGidWhatsThis = i18n("If this flag is set, the group of this folder will be "
2021  "set for all new files.");
2022  else
2023  setGidWhatsThis = i18n("If this file is an executable and the flag is set, it will "
2024  "be executed with the permissions of the group.");
2025  l->setWhatsThis(setGidWhatsThis);
2026 
2027  l = new QLabel(i18nc("File permission", "Sticky"), gb);
2028  gl->addWidget(l, 4, 5);
2029  QString stickyWhatsThis;
2030  if (isDir)
2031  stickyWhatsThis = i18n("If the Sticky flag is set on a folder, only the owner "
2032  "and root can delete or rename files. Otherwise everybody "
2033  "with write permissions can do this.");
2034  else
2035  stickyWhatsThis = i18n("The Sticky flag on a file is ignored on Linux, but may "
2036  "be used on some systems");
2037  l->setWhatsThis(stickyWhatsThis);
2038 
2039  mode_t aPermissions, aPartialPermissions;
2040  mode_t dummy1, dummy2;
2041 
2042  if (!d->isIrregular) {
2043  switch (d->pmode) {
2044  case PermissionsOnlyFiles:
2045  getPermissionMasks(aPartialPermissions,
2046  dummy1,
2047  aPermissions,
2048  dummy2);
2049  break;
2050  case PermissionsOnlyDirs:
2051  case PermissionsMixed:
2052  getPermissionMasks(dummy1,
2053  aPartialPermissions,
2054  dummy2,
2055  aPermissions);
2056  break;
2057  case PermissionsOnlyLinks:
2058  aPermissions = UniRead | UniWrite | UniExec | UniSpecial;
2059  aPartialPermissions = 0;
2060  break;
2061  }
2062  }
2063  else {
2064  aPermissions = d->permissions;
2065  aPartialPermissions = d->partialPermissions;
2066  }
2067 
2068  // Draw Checkboxes
2069  QCheckBox *cba[3][4];
2070  for (int row = 0; row < 3 ; ++row) {
2071  for (int col = 0; col < 4; ++col) {
2072  QCheckBox *cb = new QCheckBox(gb);
2073  if ( col != 3 ) theNotSpecials.append( cb );
2074  cba[row][col] = cb;
2075  cb->setChecked(aPermissions & fperm[row][col]);
2076  if ( aPartialPermissions & fperm[row][col] )
2077  {
2078  cb->setTristate();
2079  cb->setCheckState(Qt::PartiallyChecked);
2080  }
2081  else if (d->cbRecursive && d->cbRecursive->isChecked())
2082  cb->setTristate();
2083 
2084  cb->setEnabled( d->canChangePermissions );
2085  gl->addWidget (cb, row+2, col+1);
2086  switch(col) {
2087  case 0:
2088  cb->setWhatsThis(readWhatsThis);
2089  break;
2090  case 1:
2091  cb->setWhatsThis(writeWhatsThis);
2092  break;
2093  case 2:
2094  cb->setWhatsThis(execWhatsThis);
2095  break;
2096  case 3:
2097  switch(row) {
2098  case 0:
2099  cb->setWhatsThis(setUidWhatsThis);
2100  break;
2101  case 1:
2102  cb->setWhatsThis(setGidWhatsThis);
2103  break;
2104  case 2:
2105  cb->setWhatsThis(stickyWhatsThis);
2106  break;
2107  }
2108  break;
2109  }
2110  }
2111  }
2112  gl->setColumnStretch(6, 10);
2113 
2114 #ifdef HAVE_POSIX_ACL
2115  KACLEditWidget *extendedACLs = 0;
2116 
2117  // FIXME make it work with partial entries
2118  if ( properties->items().count() == 1 ) {
2119  QByteArray path = QFile::encodeName( properties->item().url().toLocalFile() );
2120  d->fileSystemSupportsACLs = fileSystemSupportsACL( path );
2121  }
2122  if ( d->fileSystemSupportsACLs ) {
2123  std::for_each( theNotSpecials.begin(), theNotSpecials.end(), std::mem_fun( &QWidget::hide ) );
2124  extendedACLs = new KACLEditWidget( mainw );
2125  vbox->addWidget(extendedACLs);
2126  if ( d->extendedACL.isValid() && d->extendedACL.isExtended() )
2127  extendedACLs->setACL( d->extendedACL );
2128  else
2129  extendedACLs->setACL( KACL( aPermissions ) );
2130 
2131  if ( d->defaultACL.isValid() )
2132  extendedACLs->setDefaultACL( d->defaultACL );
2133 
2134  if ( properties->items().first().isDir() )
2135  extendedACLs->setAllowDefaults( true );
2136  }
2137 #endif
2138  dlg.setMainWidget( mainw );
2139  if (dlg.exec() != KDialog::Accepted)
2140  return;
2141 
2142  mode_t andPermissions = mode_t(~0);
2143  mode_t orPermissions = 0;
2144  for (int row = 0; row < 3; ++row)
2145  for (int col = 0; col < 4; ++col) {
2146  switch (cba[row][col]->checkState())
2147  {
2148  case Qt::Checked:
2149  orPermissions |= fperm[row][col];
2150  //fall through
2151  case Qt::Unchecked:
2152  andPermissions &= ~fperm[row][col];
2153  break;
2154  default: // NoChange
2155  break;
2156  }
2157  }
2158 
2159  d->isIrregular = false;
2160  const KFileItemList items = properties->items();
2161  KFileItemList::const_iterator it = items.begin();
2162  const KFileItemList::const_iterator kend = items.end();
2163  for ( ; it != kend; ++it ) {
2164  if (isIrregular(((*it).permissions() & andPermissions) | orPermissions,
2165  (*it).isDir(), (*it).isLink())) {
2166  d->isIrregular = true;
2167  break;
2168  }
2169  }
2170 
2171  d->permissions = orPermissions;
2172  d->partialPermissions = andPermissions;
2173 
2174 #ifdef HAVE_POSIX_ACL
2175  // override with the acls, if present
2176  if ( extendedACLs ) {
2177  d->extendedACL = extendedACLs->getACL();
2178  d->defaultACL = extendedACLs->getDefaultACL();
2179  d->hasExtendedACL = d->extendedACL.isExtended() || d->defaultACL.isValid();
2180  d->permissions = d->extendedACL.basePermissions();
2181  d->permissions |= ( andPermissions | orPermissions ) & ( S_ISUID|S_ISGID|S_ISVTX );
2182  }
2183 #endif
2184 
2185  updateAccessControls();
2186  emit changed();
2187 }
2188 
2189 // QString KFilePermissionsPropsPlugin::tabName () const
2190 // {
2191 // return i18n ("&Permissions");
2192 // }
2193 
2194 KFilePermissionsPropsPlugin::~KFilePermissionsPropsPlugin()
2195 {
2196  delete d;
2197 }
2198 
2199 bool KFilePermissionsPropsPlugin::supports( const KFileItemList& /*_items*/ )
2200 {
2201  return true;
2202 }
2203 
2204 // sets a combo box in the Access Control frame
2205 void KFilePermissionsPropsPlugin::setComboContent(QComboBox *combo, PermissionsTarget target,
2206  mode_t permissions, mode_t partial) {
2207  combo->clear();
2208  if (d->isIrregular) //#176876
2209  return;
2210 
2211  if (d->pmode == PermissionsOnlyLinks) {
2212  combo->addItem(i18n("Link"));
2213  combo->setCurrentIndex(0);
2214  return;
2215  }
2216 
2217  mode_t tMask = permissionsMasks[target];
2218  int textIndex;
2219  for (textIndex = 0; standardPermissions[textIndex] != (mode_t)-1; textIndex++) {
2220  if ((standardPermissions[textIndex]&tMask) == (permissions&tMask&(UniRead|UniWrite)))
2221  break;
2222  }
2223  Q_ASSERT(standardPermissions[textIndex] != (mode_t)-1); // must not happen, would be irreglar
2224 
2225  for (int i = 0; permissionsTexts[(int)d->pmode][i]; i++)
2226  combo->addItem(i18n(permissionsTexts[(int)d->pmode][i]));
2227 
2228  if (partial & tMask & ~UniExec) {
2229  combo->addItem(i18n("Varying (No Change)"));
2230  combo->setCurrentIndex(3);
2231  }
2232  else {
2233  combo->setCurrentIndex(textIndex);
2234  }
2235 }
2236 
2237 // permissions are irregular if they cant be displayed in a combo box.
2238 bool KFilePermissionsPropsPlugin::isIrregular(mode_t permissions, bool isDir, bool isLink) {
2239  if (isLink) // links are always ok
2240  return false;
2241 
2242  mode_t p = permissions;
2243  if (p & (S_ISUID | S_ISGID)) // setuid/setgid -> irregular
2244  return true;
2245  if (isDir) {
2246  p &= ~S_ISVTX; // ignore sticky on dirs
2247 
2248  // check supported flag combinations
2249  mode_t p0 = p & UniOwner;
2250  if ((p0 != 0) && (p0 != (S_IRUSR | S_IXUSR)) && (p0 != UniOwner))
2251  return true;
2252  p0 = p & UniGroup;
2253  if ((p0 != 0) && (p0 != (S_IRGRP | S_IXGRP)) && (p0 != UniGroup))
2254  return true;
2255  p0 = p & UniOthers;
2256  if ((p0 != 0) && (p0 != (S_IROTH | S_IXOTH)) && (p0 != UniOthers))
2257  return true;
2258  return false;
2259  }
2260  if (p & S_ISVTX) // sticky on file -> irregular
2261  return true;
2262 
2263  // check supported flag combinations
2264  mode_t p0 = p & UniOwner;
2265  bool usrXPossible = !p0; // true if this file could be an executable
2266  if (p0 & S_IXUSR) {
2267  if ((p0 == S_IXUSR) || (p0 == (S_IWUSR | S_IXUSR)))
2268  return true;
2269  usrXPossible = true;
2270  }
2271  else if (p0 == S_IWUSR)
2272  return true;
2273 
2274  p0 = p & UniGroup;
2275  bool grpXPossible = !p0; // true if this file could be an executable
2276  if (p0 & S_IXGRP) {
2277  if ((p0 == S_IXGRP) || (p0 == (S_IWGRP | S_IXGRP)))
2278  return true;
2279  grpXPossible = true;
2280  }
2281  else if (p0 == S_IWGRP)
2282  return true;
2283  if (p0 == 0)
2284  grpXPossible = true;
2285 
2286  p0 = p & UniOthers;
2287  bool othXPossible = !p0; // true if this file could be an executable
2288  if (p0 & S_IXOTH) {
2289  if ((p0 == S_IXOTH) || (p0 == (S_IWOTH | S_IXOTH)))
2290  return true;
2291  othXPossible = true;
2292  }
2293  else if (p0 == S_IWOTH)
2294  return true;
2295 
2296  // check that there either all targets are executable-compatible, or none
2297  return (p & UniExec) && !(usrXPossible && grpXPossible && othXPossible);
2298 }
2299 
2300 // enables/disabled the widgets in the Access Control frame
2301 void KFilePermissionsPropsPlugin::enableAccessControls(bool enable) {
2302  d->ownerPermCombo->setEnabled(enable);
2303  d->groupPermCombo->setEnabled(enable);
2304  d->othersPermCombo->setEnabled(enable);
2305  if (d->extraCheckbox)
2306  d->extraCheckbox->setEnabled(enable);
2307  if ( d->cbRecursive )
2308  d->cbRecursive->setEnabled(enable);
2309 }
2310 
2311 // updates all widgets in the Access Control frame
2312 void KFilePermissionsPropsPlugin::updateAccessControls() {
2313  setComboContent(d->ownerPermCombo, PermissionsOwner,
2314  d->permissions, d->partialPermissions);
2315  setComboContent(d->groupPermCombo, PermissionsGroup,
2316  d->permissions, d->partialPermissions);
2317  setComboContent(d->othersPermCombo, PermissionsOthers,
2318  d->permissions, d->partialPermissions);
2319 
2320  switch(d->pmode) {
2321  case PermissionsOnlyLinks:
2322  enableAccessControls(false);
2323  break;
2324  case PermissionsOnlyFiles:
2325  enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
2326  if (d->canChangePermissions)
2327  d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
2328  i18np("This file uses advanced permissions",
2329  "These files use advanced permissions.",
2330  properties->items().count()) : "");
2331  if (d->partialPermissions & UniExec) {
2332  d->extraCheckbox->setTristate();
2333  d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
2334  }
2335  else {
2336  d->extraCheckbox->setTristate(false);
2337  d->extraCheckbox->setChecked(d->permissions & UniExec);
2338  }
2339  break;
2340  case PermissionsOnlyDirs:
2341  enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
2342  // if this is a dir, and we can change permissions, don't dis-allow
2343  // recursive, we can do that for ACL setting.
2344  if ( d->cbRecursive )
2345  d->cbRecursive->setEnabled( d->canChangePermissions && !d->isIrregular );
2346 
2347  if (d->canChangePermissions)
2348  d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
2349  i18np("This folder uses advanced permissions.",
2350  "These folders use advanced permissions.",
2351  properties->items().count()) : "");
2352  if (d->partialPermissions & S_ISVTX) {
2353  d->extraCheckbox->setTristate();
2354  d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
2355  }
2356  else {
2357  d->extraCheckbox->setTristate(false);
2358  d->extraCheckbox->setChecked(d->permissions & S_ISVTX);
2359  }
2360  break;
2361  case PermissionsMixed:
2362  enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
2363  if (d->canChangePermissions)
2364  d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
2365  i18n("These files use advanced permissions.") : "");
2366  break;
2367  if (d->partialPermissions & S_ISVTX) {
2368  d->extraCheckbox->setTristate();
2369  d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
2370  }
2371  else {
2372  d->extraCheckbox->setTristate(false);
2373  d->extraCheckbox->setChecked(d->permissions & S_ISVTX);
2374  }
2375  break;
2376  }
2377 }
2378 
2379 // gets masks for files and dirs from the Access Control frame widgets
2380 void KFilePermissionsPropsPlugin::getPermissionMasks(mode_t &andFilePermissions,
2381  mode_t &andDirPermissions,
2382  mode_t &orFilePermissions,
2383  mode_t &orDirPermissions) {
2384  andFilePermissions = mode_t(~UniSpecial);
2385  andDirPermissions = mode_t(~(S_ISUID|S_ISGID));
2386  orFilePermissions = 0;
2387  orDirPermissions = 0;
2388  if (d->isIrregular)
2389  return;
2390 
2391  mode_t m = standardPermissions[d->ownerPermCombo->currentIndex()];
2392  if (m != (mode_t) -1) {
2393  orFilePermissions |= m & UniOwner;
2394  if ((m & UniOwner) &&
2395  ((d->pmode == PermissionsMixed) ||
2396  ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked))))
2397  andFilePermissions &= ~(S_IRUSR | S_IWUSR);
2398  else {
2399  andFilePermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
2400  if ((m & S_IRUSR) && (d->extraCheckbox->checkState() == Qt::Checked))
2401  orFilePermissions |= S_IXUSR;
2402  }
2403 
2404  orDirPermissions |= m & UniOwner;
2405  if (m & S_IRUSR)
2406  orDirPermissions |= S_IXUSR;
2407  andDirPermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
2408  }
2409 
2410  m = standardPermissions[d->groupPermCombo->currentIndex()];
2411  if (m != (mode_t) -1) {
2412  orFilePermissions |= m & UniGroup;
2413  if ((m & UniGroup) &&
2414  ((d->pmode == PermissionsMixed) ||
2415  ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked))))
2416  andFilePermissions &= ~(S_IRGRP | S_IWGRP);
2417  else {
2418  andFilePermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
2419  if ((m & S_IRGRP) && (d->extraCheckbox->checkState() == Qt::Checked))
2420  orFilePermissions |= S_IXGRP;
2421  }
2422 
2423  orDirPermissions |= m & UniGroup;
2424  if (m & S_IRGRP)
2425  orDirPermissions |= S_IXGRP;
2426  andDirPermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
2427  }
2428 
2429  m = d->othersPermCombo->currentIndex() >= 0 ? standardPermissions[d->othersPermCombo->currentIndex()] : (mode_t)-1;
2430  if (m != (mode_t) -1) {
2431  orFilePermissions |= m & UniOthers;
2432  if ((m & UniOthers) &&
2433  ((d->pmode == PermissionsMixed) ||
2434  ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked))))
2435  andFilePermissions &= ~(S_IROTH | S_IWOTH);
2436  else {
2437  andFilePermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH);
2438  if ((m & S_IROTH) && (d->extraCheckbox->checkState() == Qt::Checked))
2439  orFilePermissions |= S_IXOTH;
2440  }
2441 
2442  orDirPermissions |= m & UniOthers;
2443  if (m & S_IROTH)
2444  orDirPermissions |= S_IXOTH;
2445  andDirPermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH);
2446  }
2447 
2448  if (((d->pmode == PermissionsMixed) || (d->pmode == PermissionsOnlyDirs)) &&
2449  (d->extraCheckbox->checkState() != Qt::PartiallyChecked)) {
2450  andDirPermissions &= ~S_ISVTX;
2451  if (d->extraCheckbox->checkState() == Qt::Checked)
2452  orDirPermissions |= S_ISVTX;
2453  }
2454 }
2455 
2456 void KFilePermissionsPropsPlugin::applyChanges()
2457 {
2458  mode_t orFilePermissions;
2459  mode_t orDirPermissions;
2460  mode_t andFilePermissions;
2461  mode_t andDirPermissions;
2462 
2463  if (!d->canChangePermissions)
2464  return;
2465 
2466  if (!d->isIrregular)
2467  getPermissionMasks(andFilePermissions,
2468  andDirPermissions,
2469  orFilePermissions,
2470  orDirPermissions);
2471  else {
2472  orFilePermissions = d->permissions;
2473  andFilePermissions = d->partialPermissions;
2474  orDirPermissions = d->permissions;
2475  andDirPermissions = d->partialPermissions;
2476  }
2477 
2478  QString owner, group;
2479  if (d->usrEdit)
2480  owner = d->usrEdit->text();
2481  if (d->grpEdit)
2482  group = d->grpEdit->text();
2483  else if (d->grpCombo)
2484  group = d->grpCombo->currentText();
2485 
2486  if (owner == d->strOwner)
2487  owner.clear(); // no change
2488 
2489  if (group == d->strGroup)
2490  group.clear();
2491 
2492  bool recursive = d->cbRecursive && d->cbRecursive->isChecked();
2493  bool permissionChange = false;
2494 
2495  KFileItemList files, dirs;
2496  const KFileItemList items = properties->items();
2497  KFileItemList::const_iterator it = items.begin();
2498  const KFileItemList::const_iterator kend = items.end();
2499  for ( ; it != kend; ++it ) {
2500  if ((*it).isDir()) {
2501  dirs.append(*it);
2502  if ((*it).permissions() != (((*it).permissions() & andDirPermissions) | orDirPermissions))
2503  permissionChange = true;
2504  }
2505  else if ((*it).isFile()) {
2506  files.append(*it);
2507  if ((*it).permissions() != (((*it).permissions() & andFilePermissions) | orFilePermissions))
2508  permissionChange = true;
2509  }
2510  }
2511 
2512  const bool ACLChange = ( d->extendedACL != properties->item().ACL() );
2513  const bool defaultACLChange = ( d->defaultACL != properties->item().defaultACL() );
2514 
2515  if (owner.isEmpty() && group.isEmpty() && !recursive
2516  && !permissionChange && !ACLChange && !defaultACLChange)
2517  return;
2518 
2519  KIO::Job * job;
2520  if (files.count() > 0) {
2521  job = KIO::chmod( files, orFilePermissions, ~andFilePermissions,
2522  owner, group, false );
2523  if ( ACLChange && d->fileSystemSupportsACLs )
2524  job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" );
2525  if ( defaultACLChange && d->fileSystemSupportsACLs )
2526  job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" );
2527 
2528  connect( job, SIGNAL(result(KJob*)),
2529  SLOT(slotChmodResult(KJob*)) );
2530  QEventLoop eventLoop;
2531  connect(this, SIGNAL(leaveModality()),
2532  &eventLoop, SLOT(quit()));
2533  eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
2534  }
2535  if (dirs.count() > 0) {
2536  job = KIO::chmod( dirs, orDirPermissions, ~andDirPermissions,
2537  owner, group, recursive );
2538  if ( ACLChange && d->fileSystemSupportsACLs )
2539  job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" );
2540  if ( defaultACLChange && d->fileSystemSupportsACLs )
2541  job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" );
2542 
2543  connect( job, SIGNAL(result(KJob*)),
2544  SLOT(slotChmodResult(KJob*)) );
2545  QEventLoop eventLoop;
2546  connect(this, SIGNAL(leaveModality()),
2547  &eventLoop, SLOT(quit()));
2548  eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
2549  }
2550 }
2551 
2552 void KFilePermissionsPropsPlugin::slotChmodResult( KJob * job )
2553 {
2554  kDebug(250) << "KFilePermissionsPropsPlugin::slotChmodResult";
2555  if (job->error())
2556  job->uiDelegate()->showErrorMessage();
2557  // allow apply() to return
2558  emit leaveModality();
2559 }
2560 
2561 
2562 
2563 
2564 class KUrlPropsPlugin::KUrlPropsPluginPrivate
2565 {
2566 public:
2567  KUrlPropsPluginPrivate()
2568  {
2569  }
2570  ~KUrlPropsPluginPrivate()
2571  {
2572  }
2573 
2574  QFrame *m_frame;
2575  KUrlRequester *URLEdit;
2576  QString URLStr;
2577 };
2578 
2579 KUrlPropsPlugin::KUrlPropsPlugin( KPropertiesDialog *_props )
2580  : KPropertiesDialogPlugin( _props ),d(new KUrlPropsPluginPrivate)
2581 {
2582  d->m_frame = new QFrame();
2583  properties->addPage(d->m_frame, i18n("U&RL"));
2584  QVBoxLayout *layout = new QVBoxLayout(d->m_frame);
2585  layout->setMargin(0);
2586 
2587  QLabel *l;
2588  l = new QLabel( d->m_frame );
2589  l->setObjectName( QLatin1String( "Label_1" ) );
2590  l->setText( i18n("URL:") );
2591  layout->addWidget(l, Qt::AlignRight);
2592 
2593  d->URLEdit = new KUrlRequester( d->m_frame );
2594  layout->addWidget(d->URLEdit);
2595 
2596  KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
2597  if (url.isLocalFile()) {
2598  QString path = url.toLocalFile();
2599 
2600  QFile f( path );
2601  if ( !f.open( QIODevice::ReadOnly ) ) {
2602  return;
2603  }
2604  f.close();
2605 
2606  KDesktopFile config( path );
2607  const KConfigGroup dg = config.desktopGroup();
2608  d->URLStr = dg.readPathEntry( "URL", QString() );
2609 
2610  if (!d->URLStr.isEmpty()) {
2611  d->URLEdit->setUrl( KUrl(d->URLStr) );
2612  }
2613  }
2614 
2615  connect( d->URLEdit, SIGNAL(textChanged(QString)),
2616  this, SIGNAL(changed()) );
2617 
2618  layout->addStretch (1);
2619 }
2620 
2621 KUrlPropsPlugin::~KUrlPropsPlugin()
2622 {
2623  delete d;
2624 }
2625 
2626 // QString KUrlPropsPlugin::tabName () const
2627 // {
2628 // return i18n ("U&RL");
2629 // }
2630 
2631 bool KUrlPropsPlugin::supports( const KFileItemList& _items )
2632 {
2633  if ( _items.count() != 1 )
2634  return false;
2635  const KFileItem item = _items.first();
2636  // check if desktop file
2637  if (!item.isDesktopFile())
2638  return false;
2639 
2640  // open file and check type
2641  bool isLocal;
2642  KUrl url = item.mostLocalUrl(isLocal);
2643  if (!isLocal) {
2644  return false;
2645  }
2646 
2647  KDesktopFile config(url.toLocalFile());
2648  return config.hasLinkType();
2649 }
2650 
2651 void KUrlPropsPlugin::applyChanges()
2652 {
2653  KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
2654  if (!url.isLocalFile()) {
2655  //FIXME: 4.2 add this: KMessageBox::sorry(0, i18n("Could not save properties. Only entries on local file systems are supported."));
2656  return;
2657  }
2658 
2659  QString path = url.toLocalFile();
2660  QFile f( path );
2661  if ( !f.open( QIODevice::ReadWrite ) ) {
2662  KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
2663  "sufficient access to write to <b>%1</b>.</qt>", path));
2664  return;
2665  }
2666  f.close();
2667 
2668  KDesktopFile config( path );
2669  KConfigGroup dg = config.desktopGroup();
2670  dg.writeEntry( "Type", QString::fromLatin1("Link"));
2671  dg.writePathEntry( "URL", d->URLEdit->url().url() );
2672  // Users can't create a Link .desktop file with a Name field,
2673  // but distributions can. Update the Name field in that case.
2674  if ( dg.hasKey("Name") )
2675  {
2676  QString nameStr = nameFromFileName(properties->kurl().fileName());
2677  dg.writeEntry( "Name", nameStr );
2678  dg.writeEntry( "Name", nameStr, KConfigBase::Persistent|KConfigBase::Localized );
2679 
2680  }
2681 }
2682 
2683 
2684 /* ----------------------------------------------------
2685  *
2686  * KDevicePropsPlugin
2687  *
2688  * -------------------------------------------------- */
2689 
2690 class KDevicePropsPlugin::KDevicePropsPluginPrivate
2691 {
2692 public:
2693  KDevicePropsPluginPrivate()
2694  {
2695  }
2696  ~KDevicePropsPluginPrivate()
2697  {
2698  }
2699 
2700  bool isMounted() const {
2701  const QString dev = device->currentText();
2702  return !dev.isEmpty() && KMountPoint::currentMountPoints().findByDevice(dev);
2703  }
2704 
2705  QFrame *m_frame;
2706  QStringList mountpointlist;
2707  QLabel *m_freeSpaceText;
2708  QLabel *m_freeSpaceLabel;
2709  QProgressBar *m_freeSpaceBar;
2710 
2711  KComboBox* device;
2712  QLabel* mountpoint;
2713  QCheckBox* readonly;
2714 
2715  QStringList m_devicelist;
2716 };
2717 
2718 KDevicePropsPlugin::KDevicePropsPlugin( KPropertiesDialog *_props ) : KPropertiesDialogPlugin( _props ),d(new KDevicePropsPluginPrivate)
2719 {
2720  d->m_frame = new QFrame();
2721  properties->addPage(d->m_frame, i18n("De&vice"));
2722 
2723  QStringList devices;
2724  const KMountPoint::List mountPoints = KMountPoint::possibleMountPoints();
2725 
2726  for(KMountPoint::List::ConstIterator it = mountPoints.begin();
2727  it != mountPoints.end(); ++it)
2728  {
2729  const KMountPoint::Ptr mp = (*it);
2730  QString mountPoint = mp->mountPoint();
2731  QString device = mp->mountedFrom();
2732  kDebug()<<"mountPoint :"<<mountPoint<<" device :"<<device<<" mp->mountType() :"<<mp->mountType();
2733 
2734  if ((mountPoint != "-") && (mountPoint != "none") && !mountPoint.isEmpty()
2735  && device != "none")
2736  {
2737  devices.append( device + QString::fromLatin1(" (")
2738  + mountPoint + QString::fromLatin1(")") );
2739  d->m_devicelist.append(device);
2740  d->mountpointlist.append(mountPoint);
2741  }
2742  }
2743 
2744  QGridLayout *layout = new QGridLayout( d->m_frame );
2745 
2746  layout->setMargin(0);
2747  layout->setColumnStretch(1, 1);
2748 
2749  QLabel* label;
2750  label = new QLabel( d->m_frame );
2751  label->setText( devices.count() == 0 ?
2752  i18n("Device (/dev/fd0):") : // old style
2753  i18n("Device:") ); // new style (combobox)
2754  layout->addWidget(label, 0, 0, Qt::AlignRight);
2755 
2756  d->device = new KComboBox( d->m_frame );
2757  d->device->setObjectName( QLatin1String( "ComboBox_device" ) );
2758  d->device->setEditable( true );
2759  d->device->addItems( devices );
2760  layout->addWidget(d->device, 0, 1);
2761  connect( d->device, SIGNAL(activated(int)),
2762  this, SLOT(slotActivated(int)) );
2763 
2764  d->readonly = new QCheckBox( d->m_frame );
2765  d->readonly->setObjectName( QLatin1String( "CheckBox_readonly" ) );
2766  d->readonly->setText( i18n("Read only") );
2767  layout->addWidget(d->readonly, 1, 1);
2768 
2769  label = new QLabel( d->m_frame );
2770  label->setText( i18n("File system:") );
2771  layout->addWidget(label, 2, 0, Qt::AlignRight);
2772 
2773  QLabel *fileSystem = new QLabel( d->m_frame );
2774  layout->addWidget(fileSystem, 2, 1);
2775 
2776  label = new QLabel( d->m_frame );
2777  label->setText( devices.count()==0 ?
2778  i18n("Mount point (/mnt/floppy):") : // old style
2779  i18n("Mount point:")); // new style (combobox)
2780  layout->addWidget(label, 3, 0, Qt::AlignRight);
2781 
2782  d->mountpoint = new QLabel( d->m_frame );
2783  d->mountpoint->setObjectName( QLatin1String( "LineEdit_mountpoint" ) );
2784 
2785  layout->addWidget(d->mountpoint, 3, 1);
2786 
2787  // show disk free
2788  d->m_freeSpaceText = new QLabel(i18n("Device usage:"), d->m_frame );
2789  layout->addWidget(d->m_freeSpaceText, 4, 0, Qt::AlignRight);
2790 
2791  d->m_freeSpaceLabel = new QLabel( d->m_frame );
2792  layout->addWidget( d->m_freeSpaceLabel, 4, 1 );
2793 
2794  d->m_freeSpaceBar = new QProgressBar( d->m_frame );
2795  d->m_freeSpaceBar->setObjectName( "freeSpaceBar" );
2796  layout->addWidget(d->m_freeSpaceBar, 5, 0, 1, 2);
2797 
2798  // we show it in the slot when we know the values
2799  d->m_freeSpaceText->hide();
2800  d->m_freeSpaceLabel->hide();
2801  d->m_freeSpaceBar->hide();
2802 
2803  KSeparator* sep = new KSeparator( Qt::Horizontal, d->m_frame);
2804  layout->addWidget(sep, 6, 0, 1, 2);
2805 
2806  layout->setRowStretch(7, 1);
2807 
2808  KUrl url = KIO::NetAccess::mostLocalUrl( _props->kurl(), _props );
2809  if (!url.isLocalFile()) {
2810  return;
2811  }
2812  QString path = url.toLocalFile();
2813 
2814  QFile f( path );
2815  if ( !f.open( QIODevice::ReadOnly ) )
2816  return;
2817  f.close();
2818 
2819  const KDesktopFile _config( path );
2820  const KConfigGroup config = _config.desktopGroup();
2821  QString deviceStr = config.readEntry( "Dev" );
2822  QString mountPointStr = config.readEntry( "MountPoint" );
2823  bool ro = config.readEntry( "ReadOnly", false );
2824 
2825  fileSystem->setText(config.readEntry("FSType"));
2826 
2827  d->device->setEditText( deviceStr );
2828  if ( !deviceStr.isEmpty() ) {
2829  // Set default options for this device (first matching entry)
2830  int index = d->m_devicelist.indexOf(deviceStr);
2831  if (index != -1)
2832  {
2833  //kDebug(250) << "found it" << index;
2834  slotActivated( index );
2835  }
2836  }
2837 
2838  if ( !mountPointStr.isEmpty() )
2839  {
2840  d->mountpoint->setText( mountPointStr );
2841  updateInfo();
2842  }
2843 
2844  d->readonly->setChecked( ro );
2845 
2846  connect( d->device, SIGNAL(activated(int)),
2847  this, SIGNAL(changed()) );
2848  connect( d->device, SIGNAL(textChanged(QString)),
2849  this, SIGNAL(changed()) );
2850  connect( d->readonly, SIGNAL(toggled(bool)),
2851  this, SIGNAL(changed()) );
2852 
2853  connect( d->device, SIGNAL(textChanged(QString)),
2854  this, SLOT(slotDeviceChanged()) );
2855 }
2856 
2857 KDevicePropsPlugin::~KDevicePropsPlugin()
2858 {
2859  delete d;
2860 }
2861 
2862 // QString KDevicePropsPlugin::tabName () const
2863 // {
2864 // return i18n ("De&vice");
2865 // }
2866 
2867 void KDevicePropsPlugin::updateInfo()
2868 {
2869  // we show it in the slot when we know the values
2870  d->m_freeSpaceText->hide();
2871  d->m_freeSpaceLabel->hide();
2872  d->m_freeSpaceBar->hide();
2873 
2874  if (!d->mountpoint->text().isEmpty() && d->isMounted()) {
2875  KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo( d->mountpoint->text() );
2876  slotFoundMountPoint( info.mountPoint(), info.size()/1024, info.used()/1024, info.available()/1024);
2877  }
2878 }
2879 
2880 void KDevicePropsPlugin::slotActivated( int index )
2881 {
2882  // index can be more than the number of known devices, when the user types
2883  // a "custom" device.
2884  if (index < d->m_devicelist.count()) {
2885  // Update mountpoint so that it matches the device that was selected in the combo
2886  d->device->setEditText(d->m_devicelist[index]);
2887  d->mountpoint->setText(d->mountpointlist[index]);
2888  }
2889 
2890  updateInfo();
2891 }
2892 
2893 void KDevicePropsPlugin::slotDeviceChanged()
2894 {
2895  // Update mountpoint so that it matches the typed device
2896  int index = d->m_devicelist.indexOf( d->device->currentText() );
2897  if ( index != -1 )
2898  d->mountpoint->setText( d->mountpointlist[index] );
2899  else
2900  d->mountpoint->setText( QString() );
2901 
2902  updateInfo();
2903 }
2904 
2905 void KDevicePropsPlugin::slotFoundMountPoint( const QString&,
2906  quint64 kibSize,
2907  quint64 /*kibUsed*/,
2908  quint64 kibAvail )
2909 {
2910  d->m_freeSpaceText->show();
2911  d->m_freeSpaceLabel->show();
2912 
2913  const int percUsed = kibSize != 0 ? (100 - (int)(100.0 * kibAvail / kibSize)) : 100;
2914 
2915  d->m_freeSpaceLabel->setText(
2916  i18nc("Available space out of total partition size (percent used)", "%1 free of %2 (%3% used)",
2917  KIO::convertSizeFromKiB(kibAvail),
2918  KIO::convertSizeFromKiB(kibSize),
2919  percUsed ));
2920 
2921  d->m_freeSpaceBar->setRange(0, 100);
2922  d->m_freeSpaceBar->setValue(percUsed);
2923  d->m_freeSpaceBar->show();
2924 }
2925 
2926 bool KDevicePropsPlugin::supports( const KFileItemList& _items )
2927 {
2928  if ( _items.count() != 1 )
2929  return false;
2930  const KFileItem item = _items.first();
2931  // check if desktop file
2932  if (!item.isDesktopFile())
2933  return false;
2934 
2935  // open file and check type
2936  bool isLocal;
2937  KUrl url = item.mostLocalUrl(isLocal);
2938  if (!isLocal) {
2939  return false;
2940  }
2941 
2942  KDesktopFile config(url.toLocalFile());
2943  return config.hasDeviceType();
2944 }
2945 
2946 void KDevicePropsPlugin::applyChanges()
2947 {
2948  KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
2949  if ( !url.isLocalFile() )
2950  return;
2951  QString path = url.toLocalFile();
2952 
2953  QFile f( path );
2954  if ( !f.open( QIODevice::ReadWrite ) )
2955  {
2956  KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have sufficient "
2957  "access to write to <b>%1</b>.</qt>", path));
2958  return;
2959  }
2960  f.close();
2961 
2962  KDesktopFile _config( path );
2963  KConfigGroup config = _config.desktopGroup();
2964  config.writeEntry( "Type", QString::fromLatin1("FSDevice") );
2965 
2966  config.writeEntry( "Dev", d->device->currentText() );
2967  config.writeEntry( "MountPoint", d->mountpoint->text() );
2968 
2969  config.writeEntry( "ReadOnly", d->readonly->isChecked() );
2970 
2971  config.sync();
2972 }
2973 
2974 
2975 /* ----------------------------------------------------
2976  *
2977  * KDesktopPropsPlugin
2978  *
2979  * -------------------------------------------------- */
2980 
2981 class KDesktopPropsPlugin::KDesktopPropsPluginPrivate
2982 {
2983 public:
2984  KDesktopPropsPluginPrivate()
2985  : w( new Ui_KPropertiesDesktopBase )
2986  , m_frame( new QFrame() )
2987  {
2988  }
2989  ~KDesktopPropsPluginPrivate()
2990  {
2991  delete w;
2992  }
2993  Ui_KPropertiesDesktopBase* w;
2994  QWidget *m_frame;
2995 
2996  QString m_origCommandStr;
2997  QString m_terminalOptionStr;
2998  QString m_suidUserStr;
2999  QString m_dbusStartupType;
3000  QString m_dbusServiceName;
3001  QString m_origDesktopFile;
3002  bool m_terminalBool;
3003  bool m_suidBool;
3004  bool m_startupBool;
3005  bool m_systrayBool;
3006 };
3007 
3008 KDesktopPropsPlugin::KDesktopPropsPlugin( KPropertiesDialog *_props )
3009  : KPropertiesDialogPlugin( _props ), d( new KDesktopPropsPluginPrivate )
3010 {
3011  d->w->setupUi(d->m_frame);
3012 
3013  properties->addPage(d->m_frame, i18n("&Application"));
3014 
3015  bool bKDesktopMode = properties->kurl().protocol() == QLatin1String("desktop") ||
3016  properties->currentDir().protocol() == QLatin1String("desktop");
3017 
3018  if (bKDesktopMode)
3019  {
3020  // Hide Name entry
3021  d->w->nameEdit->hide();
3022  d->w->nameLabel->hide();
3023  }
3024 
3025  d->w->pathEdit->setMode(KFile::Directory | KFile::LocalOnly);
3026  d->w->pathEdit->lineEdit()->setAcceptDrops(false);
3027 
3028  connect( d->w->nameEdit, SIGNAL(textChanged(QString)), this, SIGNAL(changed()) );
3029  connect( d->w->genNameEdit, SIGNAL(textChanged(QString)), this, SIGNAL(changed()) );
3030  connect( d->w->commentEdit, SIGNAL(textChanged(QString)), this, SIGNAL(changed()) );
3031  connect( d->w->commandEdit, SIGNAL(textChanged(QString)), this, SIGNAL(changed()) );
3032  connect( d->w->pathEdit, SIGNAL(textChanged(QString)), this, SIGNAL(changed()) );
3033 
3034  connect( d->w->browseButton, SIGNAL(clicked()), this, SLOT(slotBrowseExec()) );
3035  connect( d->w->addFiletypeButton, SIGNAL(clicked()), this, SLOT(slotAddFiletype()) );
3036  connect( d->w->delFiletypeButton, SIGNAL(clicked()), this, SLOT(slotDelFiletype()) );
3037  connect( d->w->advancedButton, SIGNAL(clicked()), this, SLOT(slotAdvanced()) );
3038 
3039  // now populate the page
3040 
3041  KUrl url = KIO::NetAccess::mostLocalUrl( _props->kurl(), _props );
3042  if (!url.isLocalFile()) {
3043  return;
3044  }
3045 
3046  d->m_origDesktopFile = url.toLocalFile();
3047 
3048  QFile f( d->m_origDesktopFile );
3049  if ( !f.open( QIODevice::ReadOnly ) )
3050  return;
3051  f.close();
3052 
3053  KDesktopFile _config( d->m_origDesktopFile );
3054  KConfigGroup config = _config.desktopGroup();
3055  QString nameStr = _config.readName();
3056  QString genNameStr = _config.readGenericName();
3057  QString commentStr = _config.readComment();
3058  QString commandStr = config.readEntry( "Exec", QString() );
3059  if (commandStr.startsWith(QLatin1String("ksystraycmd ")))
3060  {
3061  commandStr.remove(0, 12);
3062  d->m_systrayBool = true;
3063  }
3064  else
3065  d->m_systrayBool = false;
3066 
3067  d->m_origCommandStr = commandStr;
3068  QString pathStr = config.readEntry( "Path", QString() ); // not readPathEntry, see kservice.cpp
3069  d->m_terminalBool = config.readEntry( "Terminal", false );
3070  d->m_terminalOptionStr = config.readEntry( "TerminalOptions" );
3071  d->m_suidBool = config.readEntry( "X-KDE-SubstituteUID", false );
3072  d->m_suidUserStr = config.readEntry( "X-KDE-Username" );
3073  if( config.hasKey( "StartupNotify" ))
3074  d->m_startupBool = config.readEntry( "StartupNotify", true );
3075  else
3076  d->m_startupBool = config.readEntry( "X-KDE-StartupNotify", true );
3077  d->m_dbusStartupType = config.readEntry("X-DBUS-StartupType").toLower();
3078  // ### should there be a GUI for this setting?
3079  // At least we're copying it over to the local file, to avoid side effects (#157853)
3080  d->m_dbusServiceName = config.readEntry("X-DBUS-ServiceName");
3081 
3082  const QStringList mimeTypes = config.readXdgListEntry( "MimeType" );
3083 
3084  if ( nameStr.isEmpty() || bKDesktopMode ) {
3085  // We'll use the file name if no name is specified
3086  // because we _need_ a Name for a valid file.
3087  // But let's do it in apply, not here, so that we pick up the right name.
3088  setDirty();
3089  }
3090  if ( !bKDesktopMode )
3091  d->w->nameEdit->setText(nameStr);
3092 
3093  d->w->genNameEdit->setText( genNameStr );
3094  d->w->commentEdit->setText( commentStr );
3095  d->w->commandEdit->setText( commandStr );
3096  d->w->pathEdit->lineEdit()->setText( pathStr );
3097 
3098  // was: d->w->filetypeList->setFullWidth(true);
3099  // d->w->filetypeList->header()->setStretchEnabled(true, d->w->filetypeList->columns()-1);
3100 
3101  KMimeType::Ptr defaultMimetype = KMimeType::defaultMimeTypePtr();
3102  for(QStringList::ConstIterator it = mimeTypes.begin();
3103  it != mimeTypes.end(); )
3104  {
3105  KMimeType::Ptr p = KMimeType::mimeType(*it, KMimeType::ResolveAliases);
3106  ++it;
3107  QString preference;
3108  if (it != mimeTypes.end())
3109  {
3110  bool numeric;
3111  (*it).toInt(&numeric);
3112  if (numeric)
3113  {
3114  preference = *it;
3115  ++it;
3116  }
3117  }
3118  if (p)
3119  {
3120  QTreeWidgetItem *item = new QTreeWidgetItem();
3121  item->setText(0, p->name());
3122  item->setText(1, p->comment());
3123  item->setText(2, preference);
3124  d->w->filetypeList->addTopLevelItem(item);
3125  }
3126  }
3127  d->w->filetypeList->resizeColumnToContents(0);
3128 
3129 }
3130 
3131 KDesktopPropsPlugin::~KDesktopPropsPlugin()
3132 {
3133  delete d;
3134 }
3135 
3136 void KDesktopPropsPlugin::slotAddFiletype()
3137 {
3138  KMimeTypeChooserDialog dlg( i18n("Add File Type for %1", properties->kurl().fileName()),
3139  i18n("Select one or more file types to add:"),
3140  QStringList(), // no preselected mimetypes
3141  QString(),
3142  QStringList(),
3143  KMimeTypeChooser::Comments|KMimeTypeChooser::Patterns,
3144  d->m_frame );
3145 
3146  if (dlg.exec() == KDialog::Accepted)
3147  {
3148  foreach(const QString &mimetype, dlg.chooser()->mimeTypes())
3149  {
3150  KMimeType::Ptr p = KMimeType::mimeType(mimetype);
3151  if (!p)
3152  continue;
3153 
3154  bool found = false;
3155  int count = d->w->filetypeList->topLevelItemCount();
3156  for (int i = 0; !found && i < count; ++i) {
3157  if (d->w->filetypeList->topLevelItem(i)->text(0) == mimetype) {
3158  found = true;
3159  }
3160  }
3161  if (!found) {
3162  QTreeWidgetItem *item = new QTreeWidgetItem();
3163  item->setText(0, p->name());
3164  item->setText(1, p->comment());
3165  d->w->filetypeList->addTopLevelItem(item);
3166  }
3167  d->w->filetypeList->resizeColumnToContents(0);
3168  }
3169  }
3170  emit changed();
3171 }
3172 
3173 void KDesktopPropsPlugin::slotDelFiletype()
3174 {
3175  QTreeWidgetItem *cur = d->w->filetypeList->currentItem();
3176  if (cur) {
3177  delete cur;
3178  emit changed();
3179  }
3180 }
3181 
3182 void KDesktopPropsPlugin::checkCommandChanged()
3183 {
3184  if (KRun::binaryName(d->w->commandEdit->text(), true) !=
3185  KRun::binaryName(d->m_origCommandStr, true))
3186  {
3187  d->m_origCommandStr = d->w->commandEdit->text();
3188  d->m_dbusStartupType.clear(); // Reset
3189  d->m_dbusServiceName.clear();
3190  }
3191 }
3192 
3193 void KDesktopPropsPlugin::applyChanges()
3194 {
3195  kDebug(250) << "KDesktopPropsPlugin::applyChanges";
3196 
3197  KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
3198  if (!url.isLocalFile()) {
3199  //FIXME: 4.2 add this: KMessageBox::sorry(0, i18n("Could not save properties. Only entries on local file systems are supported."));
3200  return;
3201  }
3202 
3203  const QString path (url.toLocalFile());
3204 
3205  QFile f( path );
3206  if ( !f.open( QIODevice::ReadWrite ) ) {
3207  KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
3208  "sufficient access to write to <b>%1</b>.</qt>", path));
3209  return;
3210  }
3211  f.close();
3212 
3213  // If the command is changed we reset certain settings that are strongly
3214  // coupled to the command.
3215  checkCommandChanged();
3216 
3217  KDesktopFile origConfig (d->m_origDesktopFile);
3218  QScopedPointer<KDesktopFile> _config (origConfig.copyTo(path));
3219  KConfigGroup config = _config->desktopGroup();
3220  config.writeEntry( "Type", QString::fromLatin1("Application"));
3221  config.writeEntry( "Comment", d->w->commentEdit->text() );
3222  config.writeEntry( "Comment", d->w->commentEdit->text(), KConfigGroup::Persistent|KConfigGroup::Localized ); // for compat
3223  config.writeEntry( "GenericName", d->w->genNameEdit->text() );
3224  config.writeEntry( "GenericName", d->w->genNameEdit->text(), KConfigGroup::Persistent|KConfigGroup::Localized ); // for compat
3225 
3226  if (d->m_systrayBool)
3227  config.writeEntry( "Exec", d->w->commandEdit->text().prepend("ksystraycmd ") );
3228  else
3229  config.writeEntry( "Exec", d->w->commandEdit->text() );
3230  config.writeEntry( "Path", d->w->pathEdit->lineEdit()->text() ); // not writePathEntry, see kservice.cpp
3231 
3232  // Write mimeTypes
3233  QStringList mimeTypes;
3234  int count = d->w->filetypeList->topLevelItemCount();
3235  for (int i = 0; i < count; ++i) {
3236  QTreeWidgetItem *item = d->w->filetypeList->topLevelItem(i);
3237  QString preference = item->text(2);
3238  mimeTypes.append(item->text(0));
3239  if (!preference.isEmpty())
3240  mimeTypes.append(preference);
3241  }
3242 
3243  kDebug() << mimeTypes;
3244  config.writeXdgListEntry( "MimeType", mimeTypes );
3245 
3246  if ( !d->w->nameEdit->isHidden() ) {
3247  QString nameStr = d->w->nameEdit->text();
3248  config.writeEntry( "Name", nameStr );
3249  config.writeEntry( "Name", nameStr, KConfigGroup::Persistent|KConfigGroup::Localized );
3250  }
3251 
3252  config.writeEntry("Terminal", d->m_terminalBool);
3253  config.writeEntry("TerminalOptions", d->m_terminalOptionStr);
3254  config.writeEntry("X-KDE-SubstituteUID", d->m_suidBool);
3255  config.writeEntry("X-KDE-Username", d->m_suidUserStr);
3256  config.writeEntry("StartupNotify", d->m_startupBool);
3257  config.writeEntry("X-DBUS-StartupType", d->m_dbusStartupType);
3258  config.writeEntry("X-DBUS-ServiceName", d->m_dbusServiceName);
3259  config.sync();
3260 
3261  // KSycoca update needed?
3262  QString sycocaPath = KGlobal::dirs()->relativeLocation("apps", path);
3263  bool updateNeeded = !sycocaPath.startsWith('/');
3264  if (!updateNeeded)
3265  {
3266  sycocaPath = KGlobal::dirs()->relativeLocation("xdgdata-apps", path);
3267  updateNeeded = !sycocaPath.startsWith('/');
3268  }
3269  if (updateNeeded)
3270  KBuildSycocaProgressDialog::rebuildKSycoca(d->m_frame);
3271 }
3272 
3273 
3274 void KDesktopPropsPlugin::slotBrowseExec()
3275 {
3276  KUrl f = KFileDialog::getOpenUrl( KUrl(),
3277  QString(), d->m_frame );
3278  if ( f.isEmpty() )
3279  return;
3280 
3281  if ( !f.isLocalFile()) {
3282  KMessageBox::sorry(d->m_frame, i18n("Only executables on local file systems are supported."));
3283  return;
3284  }
3285 
3286  QString path = f.toLocalFile();
3287  path = KShell::quoteArg( path );
3288  d->w->commandEdit->setText( path );
3289 }
3290 
3291 void KDesktopPropsPlugin::slotAdvanced()
3292 {
3293  KDialog dlg( d->m_frame );
3294  dlg.setObjectName( "KPropertiesDesktopAdv" );
3295  dlg.setModal( true );
3296  dlg.setCaption( i18n("Advanced Options for %1", properties->kurl().fileName()) );
3297  dlg.setButtons( KDialog::Ok | KDialog::Cancel );
3298  dlg.setDefaultButton( KDialog::Ok );
3299  Ui_KPropertiesDesktopAdvBase w;
3300  w.setupUi(dlg.mainWidget());
3301 
3302  // If the command is changed we reset certain settings that are strongly
3303  // coupled to the command.
3304  checkCommandChanged();
3305 
3306  // check to see if we use konsole if not do not add the nocloseonexit
3307  // because we don't know how to do this on other terminal applications
3308  KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") );
3309  QString preferredTerminal = confGroup.readPathEntry("TerminalApplication",
3310  QString::fromLatin1("konsole"));
3311 
3312  bool terminalCloseBool = false;
3313 
3314  if (preferredTerminal == "konsole")
3315  {
3316  terminalCloseBool = (d->m_terminalOptionStr.contains( "--noclose" ) > 0);
3317  w.terminalCloseCheck->setChecked(terminalCloseBool);
3318  d->m_terminalOptionStr.remove( "--noclose");
3319  }
3320  else
3321  {
3322  w.terminalCloseCheck->hide();
3323  }
3324 
3325  w.terminalCheck->setChecked(d->m_terminalBool);
3326  w.terminalEdit->setText(d->m_terminalOptionStr);
3327  w.terminalCloseCheck->setEnabled(d->m_terminalBool);
3328  w.terminalEdit->setEnabled(d->m_terminalBool);
3329  w.terminalEditLabel->setEnabled(d->m_terminalBool);
3330 
3331  w.suidCheck->setChecked(d->m_suidBool);
3332  w.suidEdit->setText(d->m_suidUserStr);
3333  w.suidEdit->setEnabled(d->m_suidBool);
3334  w.suidEditLabel->setEnabled(d->m_suidBool);
3335 
3336  w.startupInfoCheck->setChecked(d->m_startupBool);
3337  w.systrayCheck->setChecked(d->m_systrayBool);
3338 
3339  if (d->m_dbusStartupType == "unique")
3340  w.dbusCombo->setCurrentIndex(2);
3341  else if (d->m_dbusStartupType == "multi")
3342  w.dbusCombo->setCurrentIndex(1);
3343  else if (d->m_dbusStartupType == "wait")
3344  w.dbusCombo->setCurrentIndex(3);
3345  else
3346  w.dbusCombo->setCurrentIndex(0);
3347 
3348  // Provide username completion up to 1000 users.
3349  KCompletion *kcom = new KCompletion;
3350  kcom->setOrder(KCompletion::Sorted);
3351  struct passwd *pw;
3352  int i, maxEntries = 1000;
3353  setpwent();
3354  for (i=0; ((pw = getpwent()) != 0L) && (i < maxEntries); i++)
3355  kcom->addItem(QString::fromLatin1(pw->pw_name));
3356  endpwent();
3357  if (i < maxEntries)
3358  {
3359  w.suidEdit->setCompletionObject(kcom, true);
3360  w.suidEdit->setAutoDeleteCompletionObject( true );
3361  w.suidEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
3362  }
3363  else
3364  {
3365  delete kcom;
3366  }
3367 
3368  connect( w.terminalEdit, SIGNAL(textChanged(QString)),
3369  this, SIGNAL(changed()) );
3370  connect( w.terminalCloseCheck, SIGNAL(toggled(bool)),
3371  this, SIGNAL(changed()) );
3372  connect( w.terminalCheck, SIGNAL(toggled(bool)),
3373  this, SIGNAL(changed()) );
3374  connect( w.suidCheck, SIGNAL(toggled(bool)),
3375  this, SIGNAL(changed()) );
3376  connect( w.suidEdit, SIGNAL(textChanged(QString)),
3377  this, SIGNAL(changed()) );
3378  connect( w.startupInfoCheck, SIGNAL(toggled(bool)),
3379  this, SIGNAL(changed()) );
3380  connect( w.systrayCheck, SIGNAL(toggled(bool)),
3381  this, SIGNAL(changed()) );
3382  connect( w.dbusCombo, SIGNAL(activated(int)),
3383  this, SIGNAL(changed()) );
3384 
3385  if ( dlg.exec() == QDialog::Accepted )
3386  {
3387  d->m_terminalOptionStr = w.terminalEdit->text().trimmed();
3388  d->m_terminalBool = w.terminalCheck->isChecked();
3389  d->m_suidBool = w.suidCheck->isChecked();
3390  d->m_suidUserStr = w.suidEdit->text().trimmed();
3391  d->m_startupBool = w.startupInfoCheck->isChecked();
3392  d->m_systrayBool = w.systrayCheck->isChecked();
3393 
3394  if (w.terminalCloseCheck->isChecked())
3395  {
3396  d->m_terminalOptionStr.append(" --noclose");
3397  }
3398 
3399  switch(w.dbusCombo->currentIndex())
3400  {
3401  case 1: d->m_dbusStartupType = "multi"; break;
3402  case 2: d->m_dbusStartupType = "unique"; break;
3403  case 3: d->m_dbusStartupType = "wait"; break;
3404  default: d->m_dbusStartupType = "none"; break;
3405  }
3406  }
3407 }
3408 
3409 bool KDesktopPropsPlugin::supports( const KFileItemList& _items )
3410 {
3411  if ( _items.count() != 1 ) {
3412  return false;
3413  }
3414 
3415  const KFileItem item = _items.first();
3416 
3417  // check if desktop file
3418  if (!item.isDesktopFile()) {
3419  return false;
3420  }
3421 
3422  // open file and check type
3423  bool isLocal;
3424  KUrl url = item.mostLocalUrl( isLocal );
3425  if (!isLocal) {
3426  return false;
3427  }
3428 
3429  KDesktopFile config(url.toLocalFile());
3430  return config.hasApplicationType() &&
3431  KAuthorized::authorize("run_desktop_files") &&
3432  KAuthorized::authorize("shell_access");
3433 }
3434 
3435 #include "kpropertiesdialog.moc"
3436 #include "kpropertiesdialog_p.moc"
3437 
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Tue Apr 16 2013 21:04:56 by doxygen 1.8.3.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.10.2 API Reference

Skip menu "kdelibs-4.10.2 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal