• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.5 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 
949  grid->addWidget(l, curRow, 0, Qt::AlignRight);
950 
951  KHBox *box = new KHBox(d->m_frame);
952  box->setSpacing(20); // ### why 20?
953  l = new QLabel(mimeComment, box );
954 
955  QPushButton *button = new QPushButton(box);
956 
957  button->setIcon( KIcon(QString::fromLatin1("configure")) );
958  const int pixmapSize = button->style()->pixelMetric(QStyle::PM_SmallIconSize);
959  button->setFixedSize( pixmapSize+8, pixmapSize+8 );
960  if ( d->mimeType == KMimeType::defaultMimeType() )
961  button->setToolTip(i18n("Create new file type"));
962  else
963  button->setToolTip(i18n("Edit file type"));
964 
965  connect( button, SIGNAL(clicked()), SLOT(slotEditFileType()));
966 
967  if (!KAuthorized::authorizeKAction("editfiletype"))
968  button->hide();
969 
970  grid->addWidget(box, curRow++, 2);
971  }
972 
973  if ( !magicMimeComment.isEmpty() && magicMimeComment != mimeComment )
974  {
975  l = new QLabel(i18n("Contents:"), d->m_frame );
976  grid->addWidget(l, curRow, 0, Qt::AlignRight);
977 
978  l = new QLabel(magicMimeComment, d->m_frame );
979  grid->addWidget(l, curRow++, 2);
980  }
981 
982  if ( !directory.isEmpty() )
983  {
984  l = new QLabel( i18n("Location:"), d->m_frame );
985  grid->addWidget(l, curRow, 0, Qt::AlignRight);
986 
987  l = new KSqueezedTextLabel( directory, d->m_frame );
988  // force the layout direction to be always LTR
989  l->setLayoutDirection(Qt::LeftToRight);
990  // but if we are in RTL mode, align the text to the right
991  // otherwise the text is on the wrong side of the dialog
992  if (properties->layoutDirection() == Qt::RightToLeft)
993  l->setAlignment( Qt::AlignRight );
994  l->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard);
995  grid->addWidget(l, curRow++, 2);
996  }
997 
998  l = new QLabel(i18n("Size:"), d->m_frame );
999  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1000 
1001  d->m_sizeLabel = new QLabel( d->m_frame );
1002  grid->addWidget( d->m_sizeLabel, curRow++, 2 );
1003 
1004  if ( !hasDirs ) // Only files [and symlinks]
1005  {
1006  d->m_sizeLabel->setText(QString::fromLatin1("%1 (%2)").arg(KIO::convertSize(totalSize))
1007  .arg(KGlobal::locale()->formatNumber(totalSize, 0)));
1008  d->m_sizeDetermineButton = 0L;
1009  d->m_sizeStopButton = 0L;
1010  }
1011  else // Directory
1012  {
1013  QHBoxLayout * sizelay = new QHBoxLayout();
1014  grid->addLayout( sizelay, curRow++, 2 );
1015 
1016  // buttons
1017  d->m_sizeDetermineButton = new QPushButton( i18n("Calculate"), d->m_frame );
1018  d->m_sizeStopButton = new QPushButton( i18n("Stop"), d->m_frame );
1019  connect( d->m_sizeDetermineButton, SIGNAL(clicked()), this, SLOT(slotSizeDetermine()) );
1020  connect( d->m_sizeStopButton, SIGNAL(clicked()), this, SLOT(slotSizeStop()) );
1021  sizelay->addWidget(d->m_sizeDetermineButton, 0);
1022  sizelay->addWidget(d->m_sizeStopButton, 0);
1023  sizelay->addStretch(10); // so that the buttons don't grow horizontally
1024 
1025  // auto-launch for local dirs only, and not for '/'
1026  if ( isLocal && !hasRoot )
1027  {
1028  d->m_sizeDetermineButton->setText( i18n("Refresh") );
1029  slotSizeDetermine();
1030  }
1031  else
1032  d->m_sizeStopButton->setEnabled( false );
1033  }
1034 
1035  if (!d->bMultiple && item.isLink()) {
1036  l = new QLabel(i18n("Points to:"), d->m_frame );
1037  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1038 
1039  d->m_linkTargetLineEdit = new KLineEdit(item.linkDest(), d->m_frame );
1040  grid->addWidget(d->m_linkTargetLineEdit, curRow++, 2);
1041  connect(d->m_linkTargetLineEdit, SIGNAL(textChanged(QString)), this, SLOT(setDirty()));
1042  }
1043 
1044  if (!d->bMultiple) // Dates for multiple don't make much sense...
1045  {
1046  KDateTime dt = item.time(KFileItem::CreationTime);
1047  if ( !dt.isNull() )
1048  {
1049  l = new QLabel(i18n("Created:"), d->m_frame );
1050  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1051 
1052  l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
1053  grid->addWidget(l, curRow++, 2);
1054  }
1055 
1056  dt = item.time(KFileItem::ModificationTime);
1057  if ( !dt.isNull() )
1058  {
1059  l = new QLabel(i18n("Modified:"), d->m_frame );
1060  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1061 
1062  l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
1063  grid->addWidget(l, curRow++, 2);
1064  }
1065 
1066  dt = item.time(KFileItem::AccessTime);
1067  if ( !dt.isNull() )
1068  {
1069  l = new QLabel(i18n("Accessed:"), d->m_frame );
1070  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1071 
1072  l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
1073  grid->addWidget(l, curRow++, 2);
1074  }
1075  }
1076 
1077  if ( isLocal && hasDirs ) // only for directories
1078  {
1079 
1080  KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(url.toLocalFile());
1081  if (mp) {
1082  KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo( mp->mountPoint() );
1083  if(info.size() != 0 )
1084  {
1085  sep = new KSeparator( Qt::Horizontal, d->m_frame);
1086  grid->addWidget(sep, curRow, 0, 1, 3);
1087  ++curRow;
1088  if (mp->mountPoint() != "/")
1089  {
1090  l = new QLabel(i18n("Mounted on:"), d->m_frame );
1091  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1092 
1093  l = new KSqueezedTextLabel( mp->mountPoint(), d->m_frame );
1094  l->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard);
1095  grid->addWidget( l, curRow++, 2 );
1096  }
1097 
1098  l = new QLabel(i18n("Device usage:"), d->m_frame );
1099  grid->addWidget(l, curRow, 0, Qt::AlignRight);
1100 
1101  d->m_capacityBar = new KCapacityBar( KCapacityBar::DrawTextOutline, d->m_frame );
1102  grid->addWidget( d->m_capacityBar, curRow++, 2);
1103 
1104  slotFoundMountPoint( info.mountPoint(), info.size()/1024, info.used()/1024, info.available()/1024);
1105  }
1106  }
1107  }
1108 
1109  vbl->addStretch(1);
1110 }
1111 
1112 bool KFilePropsPlugin::enableIconButton() const
1113 {
1114  bool iconEnabled = false;
1115  const KFileItem item = properties->item();
1116  // If the current item is a directory, check if it's writable,
1117  // so we can create/update a .directory
1118  // Current item is a file, same thing: check if it is writable
1119  if (item.isWritable()) {
1120  iconEnabled = true;
1121  }
1122  return iconEnabled;
1123 }
1124 
1125 // QString KFilePropsPlugin::tabName () const
1126 // {
1127 // return i18n ("&General");
1128 // }
1129 
1130 void KFilePropsPlugin::setFileNameReadOnly( bool ro )
1131 {
1132  if ( d->m_lined && !d->m_bFromTemplate )
1133  {
1134  d->m_lined->setReadOnly( ro );
1135  if (ro)
1136  {
1137  // Don't put the initial focus on the line edit when it is ro
1138  properties->setButtonFocus(KDialog::Ok);
1139  }
1140  }
1141 }
1142 
1143 void KFilePropsPlugin::slotEditFileType()
1144 {
1145  QString mime;
1146  if (d->mimeType == KMimeType::defaultMimeType()) {
1147  const int pos = d->oldFileName.lastIndexOf('.');
1148  if (pos != -1)
1149  mime = '*' + d->oldFileName.mid(pos);
1150  else
1151  mime = '*';
1152  } else {
1153  mime = d->mimeType;
1154  }
1155  QString keditfiletype = QString::fromLatin1("keditfiletype");
1156  KRun::runCommand( keditfiletype
1157 #ifdef Q_WS_X11
1158  + " --parent " + QString::number( (ulong)properties->window()->winId())
1159 #endif
1160  + ' ' + KShell::quoteArg(mime),
1161  keditfiletype, keditfiletype /*unused*/, properties->window());
1162 }
1163 
1164 void KFilePropsPlugin::slotIconChanged()
1165 {
1166  d->bIconChanged = true;
1167  emit changed();
1168 }
1169 
1170 void KFilePropsPlugin::nameFileChanged(const QString &text )
1171 {
1172  properties->enableButtonOk(!text.isEmpty());
1173  emit changed();
1174 }
1175 
1176 void KFilePropsPlugin::determineRelativePath( const QString & path )
1177 {
1178  // now let's make it relative
1179  d->m_sRelativePath = KGlobal::dirs()->relativeLocation("apps", path);
1180  if (d->m_sRelativePath.startsWith('/'))
1181  {
1182  d->m_sRelativePath =KGlobal::dirs()->relativeLocation("xdgdata-apps", path);
1183  if (d->m_sRelativePath.startsWith('/'))
1184  d->m_sRelativePath.clear();
1185  else
1186  d->m_sRelativePath = path;
1187  }
1188 }
1189 
1190 void KFilePropsPlugin::slotFoundMountPoint( const QString&,
1191  quint64 kibSize,
1192  quint64 /*kibUsed*/,
1193  quint64 kibAvail )
1194 {
1195  d->m_capacityBar->setText(
1196  i18nc("Available space out of total partition size (percent used)", "%1 free of %2 (%3% used)",
1197  KIO::convertSizeFromKiB(kibAvail),
1198  KIO::convertSizeFromKiB(kibSize),
1199  100 - (int)(100.0 * kibAvail / kibSize) ));
1200 
1201  d->m_capacityBar->setValue(100 - (int)(100.0 * kibAvail / kibSize));
1202 }
1203 
1204 void KFilePropsPlugin::slotDirSizeUpdate()
1205 {
1206  KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
1207  KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles();
1208  KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs();
1209  d->m_sizeLabel->setText(
1210  i18n("Calculating... %1 (%2)\n%3, %4",
1211  KIO::convertSize(totalSize),
1212  totalSize,
1213  i18np("1 file", "%1 files", totalFiles),
1214  i18np("1 sub-folder", "%1 sub-folders", totalSubdirs)));
1215 }
1216 
1217 void KFilePropsPlugin::slotDirSizeFinished( KJob * job )
1218 {
1219  if (job->error())
1220  d->m_sizeLabel->setText( job->errorString() );
1221  else
1222  {
1223  KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
1224  KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles();
1225  KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs();
1226  d->m_sizeLabel->setText( QString::fromLatin1("%1 (%2)\n%3, %4")
1227  .arg(KIO::convertSize(totalSize))
1228  .arg(KGlobal::locale()->formatNumber(totalSize, 0))
1229  .arg(i18np("1 file","%1 files",totalFiles))
1230  .arg(i18np("1 sub-folder","%1 sub-folders",totalSubdirs)));
1231  }
1232  d->m_sizeStopButton->setEnabled(false);
1233  // just in case you change something and try again :)
1234  d->m_sizeDetermineButton->setText( i18n("Refresh") );
1235  d->m_sizeDetermineButton->setEnabled(true);
1236  d->dirSizeJob = 0;
1237  delete d->dirSizeUpdateTimer;
1238  d->dirSizeUpdateTimer = 0;
1239 }
1240 
1241 void KFilePropsPlugin::slotSizeDetermine()
1242 {
1243  d->m_sizeLabel->setText( i18n("Calculating...") );
1244  kDebug(250) << " KFilePropsPlugin::slotSizeDetermine() properties->item()=" << properties->item();
1245  kDebug(250) << " URL=" << properties->item().url().url();
1246 
1247  d->dirSizeJob = KIO::directorySize( properties->items() );
1248  d->dirSizeUpdateTimer = new QTimer(this);
1249  connect( d->dirSizeUpdateTimer, SIGNAL(timeout()),
1250  SLOT(slotDirSizeUpdate()) );
1251  d->dirSizeUpdateTimer->start(500);
1252  connect( d->dirSizeJob, SIGNAL(result(KJob*)),
1253  SLOT(slotDirSizeFinished(KJob*)) );
1254  d->m_sizeStopButton->setEnabled(true);
1255  d->m_sizeDetermineButton->setEnabled(false);
1256 
1257  // also update the "Free disk space" display
1258  if ( d->m_capacityBar )
1259  {
1260  bool isLocal;
1261  const KFileItem item = properties->item();
1262  KUrl url = item.mostLocalUrl( isLocal );
1263  if (isLocal) {
1264  KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath(url.toLocalFile());
1265  if (mp) {
1266  KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo( mp->mountPoint() );
1267  slotFoundMountPoint( info.mountPoint(), info.size()/1024, info.used()/1024, info.available()/1024);
1268  }
1269  }
1270  }
1271 }
1272 
1273 void KFilePropsPlugin::slotSizeStop()
1274 {
1275  if ( d->dirSizeJob )
1276  {
1277  KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
1278  d->m_sizeLabel->setText(i18n("At least %1",
1279  KIO::convertSize(totalSize)));
1280  d->dirSizeJob->kill();
1281  d->dirSizeJob = 0;
1282  }
1283  if ( d->dirSizeUpdateTimer )
1284  d->dirSizeUpdateTimer->stop();
1285 
1286  d->m_sizeStopButton->setEnabled(false);
1287  d->m_sizeDetermineButton->setEnabled(true);
1288 }
1289 
1290 KFilePropsPlugin::~KFilePropsPlugin()
1291 {
1292  delete d;
1293 }
1294 
1295 bool KFilePropsPlugin::supports( const KFileItemList& /*_items*/ )
1296 {
1297  return true;
1298 }
1299 
1300 void KFilePropsPlugin::applyChanges()
1301 {
1302  if ( d->dirSizeJob )
1303  slotSizeStop();
1304 
1305  kDebug(250) << "KFilePropsPlugin::applyChanges";
1306 
1307  if (qobject_cast<QLineEdit*>(d->nameArea))
1308  {
1309  QString n = ((QLineEdit *) d->nameArea)->text();
1310  // Remove trailing spaces (#4345)
1311  while ( ! n.isEmpty() && n[n.length()-1].isSpace() )
1312  n.truncate( n.length() - 1 );
1313  if ( n.isEmpty() )
1314  {
1315  KMessageBox::sorry( properties, i18n("The new file name is empty."));
1316  properties->abortApplying();
1317  return;
1318  }
1319 
1320  // Do we need to rename the file ?
1321  kDebug(250) << "oldname = " << d->oldName;
1322  kDebug(250) << "newname = " << n;
1323  if ( d->oldName != n || d->m_bFromTemplate ) { // true for any from-template file
1324  KIO::Job * job = 0L;
1325  KUrl oldurl = properties->kurl();
1326 
1327  QString newFileName = KIO::encodeFileName(n);
1328  if (d->bDesktopFile && !newFileName.endsWith(QLatin1String(".desktop")) &&
1329  !newFileName.endsWith(QLatin1String(".kdelnk")))
1330  newFileName += ".desktop";
1331 
1332  // Tell properties. Warning, this changes the result of properties->kurl() !
1333  properties->rename( newFileName );
1334 
1335  // Update also relative path (for apps and mimetypes)
1336  if ( !d->m_sRelativePath.isEmpty() )
1337  determineRelativePath( properties->kurl().toLocalFile() );
1338 
1339  kDebug(250) << "New URL = " << properties->kurl().url();
1340  kDebug(250) << "old = " << oldurl.url();
1341 
1342  // Don't remove the template !!
1343  if ( !d->m_bFromTemplate ) // (normal renaming)
1344  job = KIO::moveAs( oldurl, properties->kurl() );
1345  else // Copying a template
1346  job = KIO::copyAs( oldurl, properties->kurl() );
1347 
1348  connect( job, SIGNAL(result(KJob*)),
1349  SLOT(slotCopyFinished(KJob*)) );
1350  connect( job, SIGNAL(renamed(KIO::Job*,KUrl,KUrl)),
1351  SLOT(slotFileRenamed(KIO::Job*,KUrl,KUrl)) );
1352  // wait for job
1353  QEventLoop eventLoop;
1354  connect(this, SIGNAL(leaveModality()),
1355  &eventLoop, SLOT(quit()));
1356  eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
1357  return;
1358  }
1359  properties->updateUrl(properties->kurl());
1360  // Update also relative path (for apps and mimetypes)
1361  if ( !d->m_sRelativePath.isEmpty() )
1362  determineRelativePath( properties->kurl().toLocalFile() );
1363  }
1364 
1365  // No job, keep going
1366  slotCopyFinished( 0L );
1367 }
1368 
1369 void KFilePropsPlugin::slotCopyFinished( KJob * job )
1370 {
1371  kDebug(250) << "KFilePropsPlugin::slotCopyFinished";
1372  if (job)
1373  {
1374  // allow apply() to return
1375  emit leaveModality();
1376  if ( job->error() )
1377  {
1378  job->uiDelegate()->showErrorMessage();
1379  // Didn't work. Revert the URL to the old one
1380  properties->updateUrl( static_cast<KIO::CopyJob*>(job)->srcUrls().first() );
1381  properties->abortApplying(); // Don't apply the changes to the wrong file !
1382  return;
1383  }
1384  }
1385 
1386  Q_ASSERT( !properties->item().isNull() );
1387  Q_ASSERT( !properties->item().url().isEmpty() );
1388 
1389  // Save the file where we can -> usually in ~/.kde/...
1390  if (d->bDesktopFile && !d->m_sRelativePath.isEmpty())
1391  {
1392  kDebug(250) << "KFilePropsPlugin::slotCopyFinished " << d->m_sRelativePath;
1393  KUrl newURL;
1394  newURL.setPath( KDesktopFile::locateLocal(d->m_sRelativePath) );
1395  kDebug(250) << "KFilePropsPlugin::slotCopyFinished path=" << newURL.path();
1396  properties->updateUrl( newURL );
1397  }
1398 
1399  if ( d->bKDesktopMode && d->bDesktopFile ) {
1400  // Renamed? Update Name field
1401  // Note: The desktop ioslave does this as well, but not when
1402  // the file is copied from a template.
1403  if ( d->m_bFromTemplate ) {
1404  KIO::UDSEntry entry;
1405  KIO::NetAccess::stat( properties->kurl(), entry, 0 );
1406  KFileItem item( entry, properties->kurl() );
1407  KDesktopFile config( item.localPath() );
1408  KConfigGroup cg = config.desktopGroup();
1409  QString nameStr = nameFromFileName(properties->kurl().fileName());
1410  cg.writeEntry( "Name", nameStr );
1411  cg.writeEntry( "Name", nameStr, KConfigGroup::Persistent|KConfigGroup::Localized);
1412  }
1413  }
1414 
1415  if (d->m_linkTargetLineEdit && !d->bMultiple) {
1416  const KFileItem item = properties->item();
1417  const QString newTarget = d->m_linkTargetLineEdit->text();
1418  if (newTarget != item.linkDest()) {
1419  kDebug(250) << "Updating target of symlink to" << newTarget;
1420  KIO::Job* job = KIO::symlink(newTarget, item.url(), KIO::Overwrite);
1421  job->ui()->setAutoErrorHandlingEnabled(true);
1422  job->exec();
1423  }
1424  }
1425 
1426  // "Link to Application" templates need to be made executable
1427  // Instead of matching against a filename we check if the destination
1428  // is an Application now.
1429  if ( d->m_bFromTemplate ) {
1430  // destination is not necessarily local, use the src template
1431  KDesktopFile templateResult ( static_cast<KIO::CopyJob*>(job)->srcUrls().first().toLocalFile() );
1432  if ( templateResult.hasApplicationType() ) {
1433  // We can either stat the file and add the +x bit or use the larger chmod() job
1434  // with a umask designed to only touch u+x. This is only one KIO job, so let's
1435  // do that.
1436 
1437  KFileItem appLink ( properties->item() );
1438  KFileItemList fileItemList;
1439  fileItemList << appLink;
1440 
1441  // first 0100 adds u+x, second 0100 only allows chmod to change u+x
1442  KIO::Job* chmodJob = KIO::chmod( fileItemList, 0100, 0100, QString(), QString(), KIO::HideProgressInfo );
1443  chmodJob->exec();
1444  }
1445  }
1446 }
1447 
1448 void KFilePropsPlugin::applyIconChanges()
1449 {
1450  KIconButton *iconButton = qobject_cast<KIconButton*>(d->iconArea);
1451  if ( !iconButton || !d->bIconChanged )
1452  return;
1453  // handle icon changes - only local files (or pseudo-local) for now
1454  // TODO: Use KTempFile and KIO::file_copy with overwrite = true
1455  KUrl url = properties->kurl();
1456  url = KIO::NetAccess::mostLocalUrl( url, properties );
1457  if ( url.isLocalFile()) {
1458  QString path;
1459 
1460  if (S_ISDIR(properties->item().mode()))
1461  {
1462  path = url.toLocalFile(KUrl::AddTrailingSlash) + QString::fromLatin1(".directory");
1463  // don't call updateUrl because the other tabs (i.e. permissions)
1464  // apply to the directory, not the .directory file.
1465  }
1466  else
1467  path = url.toLocalFile();
1468 
1469  // Get the default image
1470  QString str = KMimeType::findByUrl( url,
1471  properties->item().mode(),
1472  true )->iconName();
1473  // Is it another one than the default ?
1474  QString sIcon;
1475  if ( str != iconButton->icon() )
1476  sIcon = iconButton->icon();
1477  // (otherwise write empty value)
1478 
1479  kDebug(250) << "**" << path << "**";
1480 
1481  // If default icon and no .directory file -> don't create one
1482  if ( !sIcon.isEmpty() || QFile::exists(path) )
1483  {
1484  KDesktopFile cfg(path);
1485  kDebug(250) << "sIcon = " << (sIcon);
1486  kDebug(250) << "str = " << (str);
1487  cfg.desktopGroup().writeEntry( "Icon", sIcon );
1488  cfg.sync();
1489 
1490  cfg.reparseConfiguration();
1491  if ( cfg.desktopGroup().readEntry("Icon") != sIcon ) {
1492  KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not "
1493  "have sufficient access to write to <b>%1</b>.</qt>", path));
1494  }
1495  }
1496  }
1497 }
1498 
1499 void KFilePropsPlugin::slotFileRenamed( KIO::Job *, const KUrl &, const KUrl & newUrl )
1500 {
1501  // This is called in case of an existing local file during the copy/move operation,
1502  // if the user chooses Rename.
1503  properties->updateUrl( newUrl );
1504 }
1505 
1506 void KFilePropsPlugin::postApplyChanges()
1507 {
1508  // Save the icon only after applying the permissions changes (#46192)
1509  applyIconChanges();
1510 
1511  const KFileItemList items = properties->items();
1512  const KUrl::List lst = items.urlList();
1513  org::kde::KDirNotify::emitFilesChanged( lst.toStringList() );
1514 }
1515 
1516 class KFilePermissionsPropsPlugin::KFilePermissionsPropsPluginPrivate
1517 {
1518 public:
1519  KFilePermissionsPropsPluginPrivate()
1520  {
1521  }
1522  ~KFilePermissionsPropsPluginPrivate()
1523  {
1524  }
1525 
1526  QFrame *m_frame;
1527  QCheckBox *cbRecursive;
1528  QLabel *explanationLabel;
1529  KComboBox *ownerPermCombo, *groupPermCombo, *othersPermCombo;
1530  QCheckBox *extraCheckbox;
1531  mode_t partialPermissions;
1532  KFilePermissionsPropsPlugin::PermissionsMode pmode;
1533  bool canChangePermissions;
1534  bool isIrregular;
1535  bool hasExtendedACL;
1536  KACL extendedACL;
1537  KACL defaultACL;
1538  bool fileSystemSupportsACLs;
1539 
1540  KComboBox *grpCombo;
1541 
1542  KLineEdit *usrEdit;
1543  KLineEdit *grpEdit;
1544 
1545  // Old permissions
1546  mode_t permissions;
1547  // Old group
1548  QString strGroup;
1549  // Old owner
1550  QString strOwner;
1551 };
1552 
1553 #define UniOwner (S_IRUSR|S_IWUSR|S_IXUSR)
1554 #define UniGroup (S_IRGRP|S_IWGRP|S_IXGRP)
1555 #define UniOthers (S_IROTH|S_IWOTH|S_IXOTH)
1556 #define UniRead (S_IRUSR|S_IRGRP|S_IROTH)
1557 #define UniWrite (S_IWUSR|S_IWGRP|S_IWOTH)
1558 #define UniExec (S_IXUSR|S_IXGRP|S_IXOTH)
1559 #define UniSpecial (S_ISUID|S_ISGID|S_ISVTX)
1560 
1561 // synced with PermissionsTarget
1562 const mode_t KFilePermissionsPropsPlugin::permissionsMasks[3] = {UniOwner, UniGroup, UniOthers};
1563 const mode_t KFilePermissionsPropsPlugin::standardPermissions[4] = { 0, UniRead, UniRead|UniWrite, (mode_t)-1 };
1564 
1565 // synced with PermissionsMode and standardPermissions
1566 const char *KFilePermissionsPropsPlugin::permissionsTexts[4][4] = {
1567  { I18N_NOOP("Forbidden"),
1568  I18N_NOOP("Can Read"),
1569  I18N_NOOP("Can Read & Write"),
1570  0 },
1571 { I18N_NOOP("Forbidden"),
1572  I18N_NOOP("Can View Content"),
1573  I18N_NOOP("Can View & Modify Content"),
1574  0 },
1575 { 0, 0, 0, 0}, // no texts for links
1576 { I18N_NOOP("Forbidden"),
1577  I18N_NOOP("Can View Content & Read"),
1578  I18N_NOOP("Can View/Read & Modify/Write"),
1579  0 }
1580 };
1581 
1582 
1583 KFilePermissionsPropsPlugin::KFilePermissionsPropsPlugin( KPropertiesDialog *_props )
1584  : KPropertiesDialogPlugin( _props ),d(new KFilePermissionsPropsPluginPrivate)
1585 {
1586  d->cbRecursive = 0L;
1587  d->grpCombo = 0L; d->grpEdit = 0;
1588  d->usrEdit = 0L;
1589  QString path = properties->kurl().path(KUrl::RemoveTrailingSlash);
1590  QString fname = properties->kurl().fileName();
1591  bool isLocal = properties->kurl().isLocalFile();
1592  bool isTrash = ( properties->kurl().protocol().toLower() == "trash" );
1593  bool IamRoot = (geteuid() == 0);
1594 
1595  const KFileItem item = properties->item();
1596  bool isLink = item.isLink();
1597  bool isDir = item.isDir(); // all dirs
1598  bool hasDir = item.isDir(); // at least one dir
1599  d->permissions = item.permissions(); // common permissions to all files
1600  d->partialPermissions = d->permissions; // permissions that only some files have (at first we take everything)
1601  d->isIrregular = isIrregular(d->permissions, isDir, isLink);
1602  d->strOwner = item.user();
1603  d->strGroup = item.group();
1604  d->hasExtendedACL = item.ACL().isExtended() || item.defaultACL().isValid();
1605  d->extendedACL = item.ACL();
1606  d->defaultACL = item.defaultACL();
1607  d->fileSystemSupportsACLs = false;
1608 
1609  if ( properties->items().count() > 1 )
1610  {
1611  // Multiple items: see what they have in common
1612  const KFileItemList items = properties->items();
1613  KFileItemList::const_iterator it = items.begin();
1614  const KFileItemList::const_iterator kend = items.end();
1615  for ( ++it /*no need to check the first one again*/ ; it != kend; ++it )
1616  {
1617  const KUrl url = (*it).url();
1618  if (!d->isIrregular)
1619  d->isIrregular |= isIrregular((*it).permissions(),
1620  (*it).isDir() == isDir,
1621  (*it).isLink() == isLink);
1622  d->hasExtendedACL = d->hasExtendedACL || (*it).hasExtendedACL();
1623  if ( (*it).isLink() != isLink )
1624  isLink = false;
1625  if ( (*it).isDir() != isDir )
1626  isDir = false;
1627  hasDir |= (*it).isDir();
1628  if ( (*it).permissions() != d->permissions )
1629  {
1630  d->permissions &= (*it).permissions();
1631  d->partialPermissions |= (*it).permissions();
1632  }
1633  if ( (*it).user() != d->strOwner )
1634  d->strOwner.clear();
1635  if ( (*it).group() != d->strGroup )
1636  d->strGroup.clear();
1637  }
1638  }
1639 
1640  if (isLink)
1641  d->pmode = PermissionsOnlyLinks;
1642  else if (isDir)
1643  d->pmode = PermissionsOnlyDirs;
1644  else if (hasDir)
1645  d->pmode = PermissionsMixed;
1646  else
1647  d->pmode = PermissionsOnlyFiles;
1648 
1649  // keep only what's not in the common permissions
1650  d->partialPermissions = d->partialPermissions & ~d->permissions;
1651 
1652  bool isMyFile = false;
1653 
1654  if (isLocal && !d->strOwner.isEmpty()) { // local files, and all owned by the same person
1655  struct passwd *myself = getpwuid( geteuid() );
1656  if ( myself != 0L )
1657  {
1658  isMyFile = (d->strOwner == QString::fromLocal8Bit(myself->pw_name));
1659  } else
1660  kWarning() << "I don't exist ?! geteuid=" << geteuid();
1661  } else {
1662  //We don't know, for remote files, if they are ours or not.
1663  //So we let the user change permissions, and
1664  //KIO::chmod will tell, if he had no right to do it.
1665  isMyFile = true;
1666  }
1667 
1668  d->canChangePermissions = (isMyFile || IamRoot) && (!isLink);
1669 
1670 
1671  // create GUI
1672 
1673  d->m_frame = new QFrame();
1674  properties->addPage( d->m_frame, i18n("&Permissions") );
1675 
1676  QBoxLayout *box = new QVBoxLayout( d->m_frame );
1677  box->setMargin( 0 );
1678 
1679  QWidget *l;
1680  QLabel *lbl;
1681  QGroupBox *gb;
1682  QGridLayout *gl;
1683  QPushButton* pbAdvancedPerm = 0;
1684 
1685  /* Group: Access Permissions */
1686  gb = new QGroupBox ( i18n("Access Permissions"), d->m_frame );
1687  box->addWidget (gb);
1688 
1689  gl = new QGridLayout (gb);
1690  gl->setColumnStretch(1, 1);
1691 
1692  l = d->explanationLabel = new QLabel( "", gb );
1693  if (isLink)
1694  d->explanationLabel->setText(i18np("This file is a link and does not have permissions.",
1695  "All files are links and do not have permissions.",
1696  properties->items().count()));
1697  else if (!d->canChangePermissions)
1698  d->explanationLabel->setText(i18n("Only the owner can change permissions."));
1699  gl->addWidget(l, 0, 0, 1, 2);
1700 
1701  lbl = new QLabel( i18n("O&wner:"), gb);
1702  gl->addWidget(lbl, 1, 0, Qt::AlignRight);
1703  l = d->ownerPermCombo = new KComboBox(gb);
1704  lbl->setBuddy(l);
1705  gl->addWidget(l, 1, 1);
1706  connect(l, SIGNAL(activated(int)), this, SIGNAL(changed()));
1707  l->setWhatsThis(i18n("Specifies the actions that the owner is allowed to do."));
1708 
1709  lbl = new QLabel( i18n("Gro&up:"), gb);
1710  gl->addWidget(lbl, 2, 0, Qt::AlignRight);
1711  l = d->groupPermCombo = new KComboBox(gb);
1712  lbl->setBuddy(l);
1713  gl->addWidget(l, 2, 1);
1714  connect(l, SIGNAL(activated(int)), this, SIGNAL(changed()));
1715  l->setWhatsThis(i18n("Specifies the actions that the members of the group are allowed to do."));
1716 
1717  lbl = new QLabel( i18n("O&thers:"), gb);
1718  gl->addWidget(lbl, 3, 0, Qt::AlignRight);
1719  l = d->othersPermCombo = new KComboBox(gb);
1720  lbl->setBuddy(l);
1721  gl->addWidget(l, 3, 1);
1722  connect(l, SIGNAL(activated(int)), this, SIGNAL(changed()));
1723  l->setWhatsThis(i18n("Specifies the actions that all users, who are neither "
1724  "owner nor in the group, are allowed to do."));
1725 
1726  if (!isLink) {
1727  l = d->extraCheckbox = new QCheckBox(hasDir ?
1728  i18n("Only own&er can rename and delete folder content") :
1729  i18n("Is &executable"),
1730  gb );
1731  connect( d->extraCheckbox, SIGNAL(clicked()), this, SIGNAL(changed()) );
1732  gl->addWidget(l, 4, 1);
1733  l->setWhatsThis(hasDir ? i18n("Enable this option to allow only the folder's owner to "
1734  "delete or rename the contained files and folders. Other "
1735  "users can only add new files, which requires the 'Modify "
1736  "Content' permission.")
1737  : i18n("Enable this option to mark the file as executable. This only makes "
1738  "sense for programs and scripts. It is required when you want to "
1739  "execute them."));
1740 
1741  QLayoutItem *spacer = new QSpacerItem(0, 20, QSizePolicy::Minimum, QSizePolicy::Expanding);
1742  gl->addItem(spacer, 5, 0, 1, 3);
1743 
1744  pbAdvancedPerm = new QPushButton(i18n("A&dvanced Permissions"), gb);
1745  gl->addWidget(pbAdvancedPerm, 6, 0, 1, 2, Qt::AlignRight);
1746  connect(pbAdvancedPerm, SIGNAL(clicked()), this, SLOT(slotShowAdvancedPermissions()));
1747  }
1748  else
1749  d->extraCheckbox = 0;
1750 
1751 
1752  /**** Group: Ownership ****/
1753  gb = new QGroupBox ( i18n("Ownership"), d->m_frame );
1754  box->addWidget (gb);
1755 
1756  gl = new QGridLayout (gb);
1757  gl->addItem(new QSpacerItem(0, 10), 0, 0);
1758 
1759  /*** Set Owner ***/
1760  l = new QLabel( i18n("User:"), gb );
1761  gl->addWidget (l, 1, 0, Qt::AlignRight);
1762 
1763  /* GJ: Don't autocomplete more than 1000 users. This is a kind of random
1764  * value. Huge sites having 10.000+ user have a fair chance of using NIS,
1765  * (possibly) making this unacceptably slow.
1766  * OTOH, it is nice to offer this functionality for the standard user.
1767  */
1768  int i, maxEntries = 1000;
1769  struct passwd *user;
1770 
1771  /* File owner: For root, offer a KLineEdit with autocompletion.
1772  * For a user, who can never chown() a file, offer a QLabel.
1773  */
1774  if (IamRoot && isLocal)
1775  {
1776  d->usrEdit = new KLineEdit( gb );
1777  KCompletion *kcom = d->usrEdit->completionObject();
1778  kcom->setOrder(KCompletion::Sorted);
1779  setpwent();
1780  for (i=0; ((user = getpwent()) != 0L) && (i < maxEntries); ++i)
1781  kcom->addItem(QString::fromLatin1(user->pw_name));
1782  endpwent();
1783  d->usrEdit->setCompletionMode((i < maxEntries) ? KGlobalSettings::CompletionAuto :
1784  KGlobalSettings::CompletionNone);
1785  d->usrEdit->setText(d->strOwner);
1786  gl->addWidget(d->usrEdit, 1, 1);
1787  connect( d->usrEdit, SIGNAL(textChanged(QString)),
1788  this, SIGNAL(changed()) );
1789  }
1790  else
1791  {
1792  l = new QLabel(d->strOwner, gb);
1793  gl->addWidget(l, 1, 1);
1794  }
1795 
1796  /*** Set Group ***/
1797 
1798  QStringList groupList;
1799  QByteArray strUser;
1800  user = getpwuid(geteuid());
1801  if (user != 0L)
1802  strUser = user->pw_name;
1803 
1804 #ifdef HAVE_GETGROUPLIST
1805  // pick the groups to which the user belongs
1806  int groupCount = 0;
1807 #ifdef Q_OS_MAC
1808  QVarLengthArray<int> groups;
1809 #else
1810  QVarLengthArray<gid_t> groups;
1811 #endif
1812  if (getgrouplist(strUser, user->pw_gid, NULL, &groupCount) < 0) {
1813  groups.resize(groupCount);
1814  if (groups.data())
1815  getgrouplist(strUser, user->pw_gid, groups.data(), &groupCount);
1816  else
1817  groupCount = 0;
1818  }
1819 
1820  for (i = 0; i < groupCount; i++) {
1821  struct group *mygroup = getgrgid(groups[i]);
1822  if (mygroup)
1823  groupList += QString::fromLocal8Bit(mygroup->gr_name);
1824  }
1825 #endif // HAVE_GETGROUPLIST
1826 
1827  bool isMyGroup = groupList.contains(d->strGroup);
1828 
1829  /* add the group the file currently belongs to ..
1830  * .. if it is not there already
1831  */
1832  if (!isMyGroup)
1833  groupList += d->strGroup;
1834 
1835  l = new QLabel( i18n("Group:"), gb );
1836  gl->addWidget (l, 2, 0, Qt::AlignRight);
1837 
1838  /* Set group: if possible to change:
1839  * - Offer a KLineEdit for root, since he can change to any group.
1840  * - Offer a KComboBox for a normal user, since he can change to a fixed
1841  * (small) set of groups only.
1842  * If not changeable: offer a QLabel.
1843  */
1844  if (IamRoot && isLocal)
1845  {
1846  d->grpEdit = new KLineEdit(gb);
1847  KCompletion *kcom = new KCompletion;
1848  kcom->setItems(groupList);
1849  d->grpEdit->setCompletionObject(kcom, true);
1850  d->grpEdit->setAutoDeleteCompletionObject( true );
1851  d->grpEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
1852  d->grpEdit->setText(d->strGroup);
1853  gl->addWidget(d->grpEdit, 2, 1);
1854  connect( d->grpEdit, SIGNAL(textChanged(QString)),
1855  this, SIGNAL(changed()) );
1856  }
1857  else if ((groupList.count() > 1) && isMyFile && isLocal)
1858  {
1859  d->grpCombo = new KComboBox(gb);
1860  d->grpCombo->setObjectName(QLatin1String("combogrouplist"));
1861  d->grpCombo->addItems(groupList);
1862  d->grpCombo->setCurrentIndex(groupList.indexOf(d->strGroup));
1863  gl->addWidget(d->grpCombo, 2, 1);
1864  connect( d->grpCombo, SIGNAL(activated(int)),
1865  this, SIGNAL(changed()) );
1866  }
1867  else
1868  {
1869  l = new QLabel(d->strGroup, gb);
1870  gl->addWidget(l, 2, 1);
1871  }
1872 
1873  gl->setColumnStretch(2, 10);
1874 
1875  // "Apply recursive" checkbox
1876  if ( hasDir && !isLink && !isTrash )
1877  {
1878  d->cbRecursive = new QCheckBox( i18n("Apply changes to all subfolders and their contents"), d->m_frame );
1879  connect( d->cbRecursive, SIGNAL(clicked()), this, SIGNAL(changed()) );
1880  box->addWidget( d->cbRecursive );
1881  }
1882 
1883  updateAccessControls();
1884 
1885 
1886  if ( isTrash )
1887  {
1888  //don't allow to change properties for file into trash
1889  enableAccessControls(false);
1890  if ( pbAdvancedPerm)
1891  pbAdvancedPerm->setEnabled(false);
1892  }
1893 
1894  box->addStretch (10);
1895 }
1896 
1897 #ifdef HAVE_POSIX_ACL
1898 static bool fileSystemSupportsACL( const QByteArray& path )
1899 {
1900  bool fileSystemSupportsACLs = false;
1901 #ifdef Q_OS_FREEBSD
1902  struct statfs buf;
1903  fileSystemSupportsACLs = ( statfs( path.data(), &buf ) == 0 ) && ( buf.f_flags & MNT_ACLS );
1904 #else
1905  fileSystemSupportsACLs =
1906  getxattr( path.data(), "system.posix_acl_access", NULL, 0 ) >= 0 || errno == ENODATA;
1907 #endif
1908  return fileSystemSupportsACLs;
1909 }
1910 #endif
1911 
1912 
1913 void KFilePermissionsPropsPlugin::slotShowAdvancedPermissions() {
1914 
1915  bool isDir = (d->pmode == PermissionsOnlyDirs) || (d->pmode == PermissionsMixed);
1916  KDialog dlg( properties );
1917  dlg.setModal( true );
1918  dlg.setCaption( i18n("Advanced Permissions") );
1919  dlg.setButtons( KDialog::Ok | KDialog::Cancel );
1920 
1921  QLabel *l, *cl[3];
1922  QGroupBox *gb;
1923  QGridLayout *gl;
1924 
1925  QWidget *mainw = new QWidget( &dlg );
1926  QVBoxLayout *vbox = new QVBoxLayout(mainw);
1927  // Group: Access Permissions
1928  gb = new QGroupBox ( i18n("Access Permissions"), mainw );
1929  vbox->addWidget(gb);
1930 
1931  gl = new QGridLayout (gb);
1932  gl->addItem(new QSpacerItem(0, 10), 0, 0);
1933 
1934  QVector<QWidget*> theNotSpecials;
1935 
1936  l = new QLabel(i18n("Class"), gb );
1937  gl->addWidget(l, 1, 0);
1938  theNotSpecials.append( l );
1939 
1940  if (isDir)
1941  l = new QLabel( i18n("Show\nEntries"), gb );
1942  else
1943  l = new QLabel( i18n("Read"), gb );
1944  gl->addWidget (l, 1, 1);
1945  theNotSpecials.append( l );
1946  QString readWhatsThis;
1947  if (isDir)
1948  readWhatsThis = i18n("This flag allows viewing the content of the folder.");
1949  else
1950  readWhatsThis = i18n("The Read flag allows viewing the content of the file.");
1951  l->setWhatsThis(readWhatsThis);
1952 
1953  if (isDir)
1954  l = new QLabel( i18n("Write\nEntries"), gb );
1955  else
1956  l = new QLabel( i18n("Write"), gb );
1957  gl->addWidget (l, 1, 2);
1958  theNotSpecials.append( l );
1959  QString writeWhatsThis;
1960  if (isDir)
1961  writeWhatsThis = i18n("This flag allows adding, renaming and deleting of files. "
1962  "Note that deleting and renaming can be limited using the Sticky flag.");
1963  else
1964  writeWhatsThis = i18n("The Write flag allows modifying the content of the file.");
1965  l->setWhatsThis(writeWhatsThis);
1966 
1967  QString execWhatsThis;
1968  if (isDir) {
1969  l = new QLabel( i18nc("Enter folder", "Enter"), gb );
1970  execWhatsThis = i18n("Enable this flag to allow entering the folder.");
1971  }
1972  else {
1973  l = new QLabel( i18n("Exec"), gb );
1974  execWhatsThis = i18n("Enable this flag to allow executing the file as a program.");
1975  }
1976  l->setWhatsThis(execWhatsThis);
1977  theNotSpecials.append( l );
1978  // GJ: Add space between normal and special modes
1979  QSize size = l->sizeHint();
1980  size.setWidth(size.width() + 15);
1981  l->setFixedSize(size);
1982  gl->addWidget (l, 1, 3);
1983 
1984  l = new QLabel( i18n("Special"), gb );
1985  gl->addWidget(l, 1, 4, 1, 2);
1986  QString specialWhatsThis;
1987  if (isDir)
1988  specialWhatsThis = i18n("Special flag. Valid for the whole folder, the exact "
1989  "meaning of the flag can be seen in the right hand column.");
1990  else
1991  specialWhatsThis = i18n("Special flag. The exact meaning of the flag can be seen "
1992  "in the right hand column.");
1993  l->setWhatsThis(specialWhatsThis);
1994 
1995  cl[0] = new QLabel( i18n("User"), gb );
1996  gl->addWidget (cl[0], 2, 0);
1997  theNotSpecials.append( cl[0] );
1998 
1999  cl[1] = new QLabel( i18n("Group"), gb );
2000  gl->addWidget (cl[1], 3, 0);
2001  theNotSpecials.append( cl[1] );
2002 
2003  cl[2] = new QLabel( i18n("Others"), gb );
2004  gl->addWidget (cl[2], 4, 0);
2005  theNotSpecials.append( cl[2] );
2006 
2007  l = new QLabel(i18n("Set UID"), gb);
2008  gl->addWidget(l, 2, 5);
2009  QString setUidWhatsThis;
2010  if (isDir)
2011  setUidWhatsThis = i18n("If this flag is set, the owner of this folder will be "
2012  "the owner of all new files.");
2013  else
2014  setUidWhatsThis = i18n("If this file is an executable and the flag is set, it will "
2015  "be executed with the permissions of the owner.");
2016  l->setWhatsThis(setUidWhatsThis);
2017 
2018  l = new QLabel(i18n("Set GID"), gb);
2019  gl->addWidget(l, 3, 5);
2020  QString setGidWhatsThis;
2021  if (isDir)
2022  setGidWhatsThis = i18n("If this flag is set, the group of this folder will be "
2023  "set for all new files.");
2024  else
2025  setGidWhatsThis = i18n("If this file is an executable and the flag is set, it will "
2026  "be executed with the permissions of the group.");
2027  l->setWhatsThis(setGidWhatsThis);
2028 
2029  l = new QLabel(i18nc("File permission", "Sticky"), gb);
2030  gl->addWidget(l, 4, 5);
2031  QString stickyWhatsThis;
2032  if (isDir)
2033  stickyWhatsThis = i18n("If the Sticky flag is set on a folder, only the owner "
2034  "and root can delete or rename files. Otherwise everybody "
2035  "with write permissions can do this.");
2036  else
2037  stickyWhatsThis = i18n("The Sticky flag on a file is ignored on Linux, but may "
2038  "be used on some systems");
2039  l->setWhatsThis(stickyWhatsThis);
2040 
2041  mode_t aPermissions, aPartialPermissions;
2042  mode_t dummy1, dummy2;
2043 
2044  if (!d->isIrregular) {
2045  switch (d->pmode) {
2046  case PermissionsOnlyFiles:
2047  getPermissionMasks(aPartialPermissions,
2048  dummy1,
2049  aPermissions,
2050  dummy2);
2051  break;
2052  case PermissionsOnlyDirs:
2053  case PermissionsMixed:
2054  getPermissionMasks(dummy1,
2055  aPartialPermissions,
2056  dummy2,
2057  aPermissions);
2058  break;
2059  case PermissionsOnlyLinks:
2060  aPermissions = UniRead | UniWrite | UniExec | UniSpecial;
2061  aPartialPermissions = 0;
2062  break;
2063  }
2064  }
2065  else {
2066  aPermissions = d->permissions;
2067  aPartialPermissions = d->partialPermissions;
2068  }
2069 
2070  // Draw Checkboxes
2071  QCheckBox *cba[3][4];
2072  for (int row = 0; row < 3 ; ++row) {
2073  for (int col = 0; col < 4; ++col) {
2074  QCheckBox *cb = new QCheckBox(gb);
2075  if ( col != 3 ) theNotSpecials.append( cb );
2076  cba[row][col] = cb;
2077  cb->setChecked(aPermissions & fperm[row][col]);
2078  if ( aPartialPermissions & fperm[row][col] )
2079  {
2080  cb->setTristate();
2081  cb->setCheckState(Qt::PartiallyChecked);
2082  }
2083  else if (d->cbRecursive && d->cbRecursive->isChecked())
2084  cb->setTristate();
2085 
2086  cb->setEnabled( d->canChangePermissions );
2087  gl->addWidget (cb, row+2, col+1);
2088  switch(col) {
2089  case 0:
2090  cb->setWhatsThis(readWhatsThis);
2091  break;
2092  case 1:
2093  cb->setWhatsThis(writeWhatsThis);
2094  break;
2095  case 2:
2096  cb->setWhatsThis(execWhatsThis);
2097  break;
2098  case 3:
2099  switch(row) {
2100  case 0:
2101  cb->setWhatsThis(setUidWhatsThis);
2102  break;
2103  case 1:
2104  cb->setWhatsThis(setGidWhatsThis);
2105  break;
2106  case 2:
2107  cb->setWhatsThis(stickyWhatsThis);
2108  break;
2109  }
2110  break;
2111  }
2112  }
2113  }
2114  gl->setColumnStretch(6, 10);
2115 
2116 #ifdef HAVE_POSIX_ACL
2117  KACLEditWidget *extendedACLs = 0;
2118 
2119  // FIXME make it work with partial entries
2120  if ( properties->items().count() == 1 ) {
2121  QByteArray path = QFile::encodeName( properties->item().url().toLocalFile() );
2122  d->fileSystemSupportsACLs = fileSystemSupportsACL( path );
2123  }
2124  if ( d->fileSystemSupportsACLs ) {
2125  std::for_each( theNotSpecials.begin(), theNotSpecials.end(), std::mem_fun( &QWidget::hide ) );
2126  extendedACLs = new KACLEditWidget( mainw );
2127  vbox->addWidget(extendedACLs);
2128  if ( d->extendedACL.isValid() && d->extendedACL.isExtended() )
2129  extendedACLs->setACL( d->extendedACL );
2130  else
2131  extendedACLs->setACL( KACL( aPermissions ) );
2132 
2133  if ( d->defaultACL.isValid() )
2134  extendedACLs->setDefaultACL( d->defaultACL );
2135 
2136  if ( properties->items().first().isDir() )
2137  extendedACLs->setAllowDefaults( true );
2138  }
2139 #endif
2140  dlg.setMainWidget( mainw );
2141  if (dlg.exec() != KDialog::Accepted)
2142  return;
2143 
2144  mode_t andPermissions = mode_t(~0);
2145  mode_t orPermissions = 0;
2146  for (int row = 0; row < 3; ++row)
2147  for (int col = 0; col < 4; ++col) {
2148  switch (cba[row][col]->checkState())
2149  {
2150  case Qt::Checked:
2151  orPermissions |= fperm[row][col];
2152  //fall through
2153  case Qt::Unchecked:
2154  andPermissions &= ~fperm[row][col];
2155  break;
2156  default: // NoChange
2157  break;
2158  }
2159  }
2160 
2161  d->isIrregular = false;
2162  const KFileItemList items = properties->items();
2163  KFileItemList::const_iterator it = items.begin();
2164  const KFileItemList::const_iterator kend = items.end();
2165  for ( ; it != kend; ++it ) {
2166  if (isIrregular(((*it).permissions() & andPermissions) | orPermissions,
2167  (*it).isDir(), (*it).isLink())) {
2168  d->isIrregular = true;
2169  break;
2170  }
2171  }
2172 
2173  d->permissions = orPermissions;
2174  d->partialPermissions = andPermissions;
2175 
2176 #ifdef HAVE_POSIX_ACL
2177  // override with the acls, if present
2178  if ( extendedACLs ) {
2179  d->extendedACL = extendedACLs->getACL();
2180  d->defaultACL = extendedACLs->getDefaultACL();
2181  d->hasExtendedACL = d->extendedACL.isExtended() || d->defaultACL.isValid();
2182  d->permissions = d->extendedACL.basePermissions();
2183  d->permissions |= ( andPermissions | orPermissions ) & ( S_ISUID|S_ISGID|S_ISVTX );
2184  }
2185 #endif
2186 
2187  updateAccessControls();
2188  emit changed();
2189 }
2190 
2191 // QString KFilePermissionsPropsPlugin::tabName () const
2192 // {
2193 // return i18n ("&Permissions");
2194 // }
2195 
2196 KFilePermissionsPropsPlugin::~KFilePermissionsPropsPlugin()
2197 {
2198  delete d;
2199 }
2200 
2201 bool KFilePermissionsPropsPlugin::supports( const KFileItemList& /*_items*/ )
2202 {
2203  return true;
2204 }
2205 
2206 // sets a combo box in the Access Control frame
2207 void KFilePermissionsPropsPlugin::setComboContent(QComboBox *combo, PermissionsTarget target,
2208  mode_t permissions, mode_t partial) {
2209  combo->clear();
2210  if (d->isIrregular) //#176876
2211  return;
2212 
2213  if (d->pmode == PermissionsOnlyLinks) {
2214  combo->addItem(i18n("Link"));
2215  combo->setCurrentIndex(0);
2216  return;
2217  }
2218 
2219  mode_t tMask = permissionsMasks[target];
2220  int textIndex;
2221  for (textIndex = 0; standardPermissions[textIndex] != (mode_t)-1; textIndex++) {
2222  if ((standardPermissions[textIndex]&tMask) == (permissions&tMask&(UniRead|UniWrite)))
2223  break;
2224  }
2225  Q_ASSERT(standardPermissions[textIndex] != (mode_t)-1); // must not happen, would be irreglar
2226 
2227  for (int i = 0; permissionsTexts[(int)d->pmode][i]; i++)
2228  combo->addItem(i18n(permissionsTexts[(int)d->pmode][i]));
2229 
2230  if (partial & tMask & ~UniExec) {
2231  combo->addItem(i18n("Varying (No Change)"));
2232  combo->setCurrentIndex(3);
2233  }
2234  else {
2235  combo->setCurrentIndex(textIndex);
2236  }
2237 }
2238 
2239 // permissions are irregular if they cant be displayed in a combo box.
2240 bool KFilePermissionsPropsPlugin::isIrregular(mode_t permissions, bool isDir, bool isLink) {
2241  if (isLink) // links are always ok
2242  return false;
2243 
2244  mode_t p = permissions;
2245  if (p & (S_ISUID | S_ISGID)) // setuid/setgid -> irregular
2246  return true;
2247  if (isDir) {
2248  p &= ~S_ISVTX; // ignore sticky on dirs
2249 
2250  // check supported flag combinations
2251  mode_t p0 = p & UniOwner;
2252  if ((p0 != 0) && (p0 != (S_IRUSR | S_IXUSR)) && (p0 != UniOwner))
2253  return true;
2254  p0 = p & UniGroup;
2255  if ((p0 != 0) && (p0 != (S_IRGRP | S_IXGRP)) && (p0 != UniGroup))
2256  return true;
2257  p0 = p & UniOthers;
2258  if ((p0 != 0) && (p0 != (S_IROTH | S_IXOTH)) && (p0 != UniOthers))
2259  return true;
2260  return false;
2261  }
2262  if (p & S_ISVTX) // sticky on file -> irregular
2263  return true;
2264 
2265  // check supported flag combinations
2266  mode_t p0 = p & UniOwner;
2267  bool usrXPossible = !p0; // true if this file could be an executable
2268  if (p0 & S_IXUSR) {
2269  if ((p0 == S_IXUSR) || (p0 == (S_IWUSR | S_IXUSR)))
2270  return true;
2271  usrXPossible = true;
2272  }
2273  else if (p0 == S_IWUSR)
2274  return true;
2275 
2276  p0 = p & UniGroup;
2277  bool grpXPossible = !p0; // true if this file could be an executable
2278  if (p0 & S_IXGRP) {
2279  if ((p0 == S_IXGRP) || (p0 == (S_IWGRP | S_IXGRP)))
2280  return true;
2281  grpXPossible = true;
2282  }
2283  else if (p0 == S_IWGRP)
2284  return true;
2285  if (p0 == 0)
2286  grpXPossible = true;
2287 
2288  p0 = p & UniOthers;
2289  bool othXPossible = !p0; // true if this file could be an executable
2290  if (p0 & S_IXOTH) {
2291  if ((p0 == S_IXOTH) || (p0 == (S_IWOTH | S_IXOTH)))
2292  return true;
2293  othXPossible = true;
2294  }
2295  else if (p0 == S_IWOTH)
2296  return true;
2297 
2298  // check that there either all targets are executable-compatible, or none
2299  return (p & UniExec) && !(usrXPossible && grpXPossible && othXPossible);
2300 }
2301 
2302 // enables/disabled the widgets in the Access Control frame
2303 void KFilePermissionsPropsPlugin::enableAccessControls(bool enable) {
2304  d->ownerPermCombo->setEnabled(enable);
2305  d->groupPermCombo->setEnabled(enable);
2306  d->othersPermCombo->setEnabled(enable);
2307  if (d->extraCheckbox)
2308  d->extraCheckbox->setEnabled(enable);
2309  if ( d->cbRecursive )
2310  d->cbRecursive->setEnabled(enable);
2311 }
2312 
2313 // updates all widgets in the Access Control frame
2314 void KFilePermissionsPropsPlugin::updateAccessControls() {
2315  setComboContent(d->ownerPermCombo, PermissionsOwner,
2316  d->permissions, d->partialPermissions);
2317  setComboContent(d->groupPermCombo, PermissionsGroup,
2318  d->permissions, d->partialPermissions);
2319  setComboContent(d->othersPermCombo, PermissionsOthers,
2320  d->permissions, d->partialPermissions);
2321 
2322  switch(d->pmode) {
2323  case PermissionsOnlyLinks:
2324  enableAccessControls(false);
2325  break;
2326  case PermissionsOnlyFiles:
2327  enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
2328  if (d->canChangePermissions)
2329  d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
2330  i18np("This file uses advanced permissions",
2331  "These files use advanced permissions.",
2332  properties->items().count()) : "");
2333  if (d->partialPermissions & UniExec) {
2334  d->extraCheckbox->setTristate();
2335  d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
2336  }
2337  else {
2338  d->extraCheckbox->setTristate(false);
2339  d->extraCheckbox->setChecked(d->permissions & UniExec);
2340  }
2341  break;
2342  case PermissionsOnlyDirs:
2343  enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
2344  // if this is a dir, and we can change permissions, don't dis-allow
2345  // recursive, we can do that for ACL setting.
2346  if ( d->cbRecursive )
2347  d->cbRecursive->setEnabled( d->canChangePermissions && !d->isIrregular );
2348 
2349  if (d->canChangePermissions)
2350  d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
2351  i18np("This folder uses advanced permissions.",
2352  "These folders use advanced permissions.",
2353  properties->items().count()) : "");
2354  if (d->partialPermissions & S_ISVTX) {
2355  d->extraCheckbox->setTristate();
2356  d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
2357  }
2358  else {
2359  d->extraCheckbox->setTristate(false);
2360  d->extraCheckbox->setChecked(d->permissions & S_ISVTX);
2361  }
2362  break;
2363  case PermissionsMixed:
2364  enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
2365  if (d->canChangePermissions)
2366  d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
2367  i18n("These files use advanced permissions.") : "");
2368  break;
2369  if (d->partialPermissions & S_ISVTX) {
2370  d->extraCheckbox->setTristate();
2371  d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
2372  }
2373  else {
2374  d->extraCheckbox->setTristate(false);
2375  d->extraCheckbox->setChecked(d->permissions & S_ISVTX);
2376  }
2377  break;
2378  }
2379 }
2380 
2381 // gets masks for files and dirs from the Access Control frame widgets
2382 void KFilePermissionsPropsPlugin::getPermissionMasks(mode_t &andFilePermissions,
2383  mode_t &andDirPermissions,
2384  mode_t &orFilePermissions,
2385  mode_t &orDirPermissions) {
2386  andFilePermissions = mode_t(~UniSpecial);
2387  andDirPermissions = mode_t(~(S_ISUID|S_ISGID));
2388  orFilePermissions = 0;
2389  orDirPermissions = 0;
2390  if (d->isIrregular)
2391  return;
2392 
2393  mode_t m = standardPermissions[d->ownerPermCombo->currentIndex()];
2394  if (m != (mode_t) -1) {
2395  orFilePermissions |= m & UniOwner;
2396  if ((m & UniOwner) &&
2397  ((d->pmode == PermissionsMixed) ||
2398  ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked))))
2399  andFilePermissions &= ~(S_IRUSR | S_IWUSR);
2400  else {
2401  andFilePermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
2402  if ((m & S_IRUSR) && (d->extraCheckbox->checkState() == Qt::Checked))
2403  orFilePermissions |= S_IXUSR;
2404  }
2405 
2406  orDirPermissions |= m & UniOwner;
2407  if (m & S_IRUSR)
2408  orDirPermissions |= S_IXUSR;
2409  andDirPermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
2410  }
2411 
2412  m = standardPermissions[d->groupPermCombo->currentIndex()];
2413  if (m != (mode_t) -1) {
2414  orFilePermissions |= m & UniGroup;
2415  if ((m & UniGroup) &&
2416  ((d->pmode == PermissionsMixed) ||
2417  ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked))))
2418  andFilePermissions &= ~(S_IRGRP | S_IWGRP);
2419  else {
2420  andFilePermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
2421  if ((m & S_IRGRP) && (d->extraCheckbox->checkState() == Qt::Checked))
2422  orFilePermissions |= S_IXGRP;
2423  }
2424 
2425  orDirPermissions |= m & UniGroup;
2426  if (m & S_IRGRP)
2427  orDirPermissions |= S_IXGRP;
2428  andDirPermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
2429  }
2430 
2431  m = d->othersPermCombo->currentIndex() >= 0 ? standardPermissions[d->othersPermCombo->currentIndex()] : (mode_t)-1;
2432  if (m != (mode_t) -1) {
2433  orFilePermissions |= m & UniOthers;
2434  if ((m & UniOthers) &&
2435  ((d->pmode == PermissionsMixed) ||
2436  ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked))))
2437  andFilePermissions &= ~(S_IROTH | S_IWOTH);
2438  else {
2439  andFilePermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH);
2440  if ((m & S_IROTH) && (d->extraCheckbox->checkState() == Qt::Checked))
2441  orFilePermissions |= S_IXOTH;
2442  }
2443 
2444  orDirPermissions |= m & UniOthers;
2445  if (m & S_IROTH)
2446  orDirPermissions |= S_IXOTH;
2447  andDirPermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH);
2448  }
2449 
2450  if (((d->pmode == PermissionsMixed) || (d->pmode == PermissionsOnlyDirs)) &&
2451  (d->extraCheckbox->checkState() != Qt::PartiallyChecked)) {
2452  andDirPermissions &= ~S_ISVTX;
2453  if (d->extraCheckbox->checkState() == Qt::Checked)
2454  orDirPermissions |= S_ISVTX;
2455  }
2456 }
2457 
2458 void KFilePermissionsPropsPlugin::applyChanges()
2459 {
2460  mode_t orFilePermissions;
2461  mode_t orDirPermissions;
2462  mode_t andFilePermissions;
2463  mode_t andDirPermissions;
2464 
2465  if (!d->canChangePermissions)
2466  return;
2467 
2468  if (!d->isIrregular)
2469  getPermissionMasks(andFilePermissions,
2470  andDirPermissions,
2471  orFilePermissions,
2472  orDirPermissions);
2473  else {
2474  orFilePermissions = d->permissions;
2475  andFilePermissions = d->partialPermissions;
2476  orDirPermissions = d->permissions;
2477  andDirPermissions = d->partialPermissions;
2478  }
2479 
2480  QString owner, group;
2481  if (d->usrEdit)
2482  owner = d->usrEdit->text();
2483  if (d->grpEdit)
2484  group = d->grpEdit->text();
2485  else if (d->grpCombo)
2486  group = d->grpCombo->currentText();
2487 
2488  if (owner == d->strOwner)
2489  owner.clear(); // no change
2490 
2491  if (group == d->strGroup)
2492  group.clear();
2493 
2494  bool recursive = d->cbRecursive && d->cbRecursive->isChecked();
2495  bool permissionChange = false;
2496 
2497  KFileItemList files, dirs;
2498  const KFileItemList items = properties->items();
2499  KFileItemList::const_iterator it = items.begin();
2500  const KFileItemList::const_iterator kend = items.end();
2501  for ( ; it != kend; ++it ) {
2502  if ((*it).isDir()) {
2503  dirs.append(*it);
2504  if ((*it).permissions() != (((*it).permissions() & andDirPermissions) | orDirPermissions))
2505  permissionChange = true;
2506  }
2507  else if ((*it).isFile()) {
2508  files.append(*it);
2509  if ((*it).permissions() != (((*it).permissions() & andFilePermissions) | orFilePermissions))
2510  permissionChange = true;
2511  }
2512  }
2513 
2514  const bool ACLChange = ( d->extendedACL != properties->item().ACL() );
2515  const bool defaultACLChange = ( d->defaultACL != properties->item().defaultACL() );
2516 
2517  if (owner.isEmpty() && group.isEmpty() && !recursive
2518  && !permissionChange && !ACLChange && !defaultACLChange)
2519  return;
2520 
2521  KIO::Job * job;
2522  if (files.count() > 0) {
2523  job = KIO::chmod( files, orFilePermissions, ~andFilePermissions,
2524  owner, group, false );
2525  if ( ACLChange && d->fileSystemSupportsACLs )
2526  job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" );
2527  if ( defaultACLChange && d->fileSystemSupportsACLs )
2528  job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" );
2529 
2530  connect( job, SIGNAL(result(KJob*)),
2531  SLOT(slotChmodResult(KJob*)) );
2532  QEventLoop eventLoop;
2533  connect(this, SIGNAL(leaveModality()),
2534  &eventLoop, SLOT(quit()));
2535  eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
2536  }
2537  if (dirs.count() > 0) {
2538  job = KIO::chmod( dirs, orDirPermissions, ~andDirPermissions,
2539  owner, group, recursive );
2540  if ( ACLChange && d->fileSystemSupportsACLs )
2541  job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" );
2542  if ( defaultACLChange && d->fileSystemSupportsACLs )
2543  job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" );
2544 
2545  connect( job, SIGNAL(result(KJob*)),
2546  SLOT(slotChmodResult(KJob*)) );
2547  QEventLoop eventLoop;
2548  connect(this, SIGNAL(leaveModality()),
2549  &eventLoop, SLOT(quit()));
2550  eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
2551  }
2552 }
2553 
2554 void KFilePermissionsPropsPlugin::slotChmodResult( KJob * job )
2555 {
2556  kDebug(250) << "KFilePermissionsPropsPlugin::slotChmodResult";
2557  if (job->error())
2558  job->uiDelegate()->showErrorMessage();
2559  // allow apply() to return
2560  emit leaveModality();
2561 }
2562 
2563 
2564 
2565 
2566 class KUrlPropsPlugin::KUrlPropsPluginPrivate
2567 {
2568 public:
2569  KUrlPropsPluginPrivate()
2570  {
2571  }
2572  ~KUrlPropsPluginPrivate()
2573  {
2574  }
2575 
2576  QFrame *m_frame;
2577  KUrlRequester *URLEdit;
2578  QString URLStr;
2579 };
2580 
2581 KUrlPropsPlugin::KUrlPropsPlugin( KPropertiesDialog *_props )
2582  : KPropertiesDialogPlugin( _props ),d(new KUrlPropsPluginPrivate)
2583 {
2584  d->m_frame = new QFrame();
2585  properties->addPage(d->m_frame, i18n("U&RL"));
2586  QVBoxLayout *layout = new QVBoxLayout(d->m_frame);
2587  layout->setMargin(0);
2588 
2589  QLabel *l;
2590  l = new QLabel( d->m_frame );
2591  l->setObjectName( QLatin1String( "Label_1" ) );
2592  l->setText( i18n("URL:") );
2593  layout->addWidget(l, Qt::AlignRight);
2594 
2595  d->URLEdit = new KUrlRequester( d->m_frame );
2596  layout->addWidget(d->URLEdit);
2597 
2598  KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
2599  if (url.isLocalFile()) {
2600  QString path = url.toLocalFile();
2601 
2602  QFile f( path );
2603  if ( !f.open( QIODevice::ReadOnly ) ) {
2604  return;
2605  }
2606  f.close();
2607 
2608  KDesktopFile config( path );
2609  const KConfigGroup dg = config.desktopGroup();
2610  d->URLStr = dg.readPathEntry( "URL", QString() );
2611 
2612  if (!d->URLStr.isEmpty()) {
2613  d->URLEdit->setUrl( KUrl(d->URLStr) );
2614  }
2615  }
2616 
2617  connect( d->URLEdit, SIGNAL(textChanged(QString)),
2618  this, SIGNAL(changed()) );
2619 
2620  layout->addStretch (1);
2621 }
2622 
2623 KUrlPropsPlugin::~KUrlPropsPlugin()
2624 {
2625  delete d;
2626 }
2627 
2628 // QString KUrlPropsPlugin::tabName () const
2629 // {
2630 // return i18n ("U&RL");
2631 // }
2632 
2633 bool KUrlPropsPlugin::supports( const KFileItemList& _items )
2634 {
2635  if ( _items.count() != 1 )
2636  return false;
2637  const KFileItem item = _items.first();
2638  // check if desktop file
2639  if (!item.isDesktopFile())
2640  return false;
2641 
2642  // open file and check type
2643  bool isLocal;
2644  KUrl url = item.mostLocalUrl(isLocal);
2645  if (!isLocal) {
2646  return false;
2647  }
2648 
2649  KDesktopFile config(url.toLocalFile());
2650  return config.hasLinkType();
2651 }
2652 
2653 void KUrlPropsPlugin::applyChanges()
2654 {
2655  KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
2656  if (!url.isLocalFile()) {
2657  //FIXME: 4.2 add this: KMessageBox::sorry(0, i18n("Could not save properties. Only entries on local file systems are supported."));
2658  return;
2659  }
2660 
2661  QString path = url.toLocalFile();
2662  QFile f( path );
2663  if ( !f.open( QIODevice::ReadWrite ) ) {
2664  KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
2665  "sufficient access to write to <b>%1</b>.</qt>", path));
2666  return;
2667  }
2668  f.close();
2669 
2670  KDesktopFile config( path );
2671  KConfigGroup dg = config.desktopGroup();
2672  dg.writeEntry( "Type", QString::fromLatin1("Link"));
2673  dg.writePathEntry( "URL", d->URLEdit->url().url() );
2674  // Users can't create a Link .desktop file with a Name field,
2675  // but distributions can. Update the Name field in that case.
2676  if ( dg.hasKey("Name") )
2677  {
2678  QString nameStr = nameFromFileName(properties->kurl().fileName());
2679  dg.writeEntry( "Name", nameStr );
2680  dg.writeEntry( "Name", nameStr, KConfigBase::Persistent|KConfigBase::Localized );
2681 
2682  }
2683 }
2684 
2685 
2686 /* ----------------------------------------------------
2687  *
2688  * KDevicePropsPlugin
2689  *
2690  * -------------------------------------------------- */
2691 
2692 class KDevicePropsPlugin::KDevicePropsPluginPrivate
2693 {
2694 public:
2695  KDevicePropsPluginPrivate()
2696  {
2697  }
2698  ~KDevicePropsPluginPrivate()
2699  {
2700  }
2701 
2702  bool isMounted() const {
2703  const QString dev = device->currentText();
2704  return !dev.isEmpty() && KMountPoint::currentMountPoints().findByDevice(dev);
2705  }
2706 
2707  QFrame *m_frame;
2708  QStringList mountpointlist;
2709  QLabel *m_freeSpaceText;
2710  QLabel *m_freeSpaceLabel;
2711  QProgressBar *m_freeSpaceBar;
2712 
2713  KComboBox* device;
2714  QLabel* mountpoint;
2715  QCheckBox* readonly;
2716 
2717  QStringList m_devicelist;
2718 };
2719 
2720 KDevicePropsPlugin::KDevicePropsPlugin( KPropertiesDialog *_props ) : KPropertiesDialogPlugin( _props ),d(new KDevicePropsPluginPrivate)
2721 {
2722  d->m_frame = new QFrame();
2723  properties->addPage(d->m_frame, i18n("De&vice"));
2724 
2725  QStringList devices;
2726  const KMountPoint::List mountPoints = KMountPoint::possibleMountPoints();
2727 
2728  for(KMountPoint::List::ConstIterator it = mountPoints.begin();
2729  it != mountPoints.end(); ++it)
2730  {
2731  const KMountPoint::Ptr mp = (*it);
2732  QString mountPoint = mp->mountPoint();
2733  QString device = mp->mountedFrom();
2734  kDebug()<<"mountPoint :"<<mountPoint<<" device :"<<device<<" mp->mountType() :"<<mp->mountType();
2735 
2736  if ((mountPoint != "-") && (mountPoint != "none") && !mountPoint.isEmpty()
2737  && device != "none")
2738  {
2739  devices.append( device + QString::fromLatin1(" (")
2740  + mountPoint + QString::fromLatin1(")") );
2741  d->m_devicelist.append(device);
2742  d->mountpointlist.append(mountPoint);
2743  }
2744  }
2745 
2746  QGridLayout *layout = new QGridLayout( d->m_frame );
2747 
2748  layout->setMargin(0);
2749  layout->setColumnStretch(1, 1);
2750 
2751  QLabel* label;
2752  label = new QLabel( d->m_frame );
2753  label->setText( devices.count() == 0 ?
2754  i18n("Device (/dev/fd0):") : // old style
2755  i18n("Device:") ); // new style (combobox)
2756  layout->addWidget(label, 0, 0, Qt::AlignRight);
2757 
2758  d->device = new KComboBox( d->m_frame );
2759  d->device->setObjectName( QLatin1String( "ComboBox_device" ) );
2760  d->device->setEditable( true );
2761  d->device->addItems( devices );
2762  layout->addWidget(d->device, 0, 1);
2763  connect( d->device, SIGNAL(activated(int)),
2764  this, SLOT(slotActivated(int)) );
2765 
2766  d->readonly = new QCheckBox( d->m_frame );
2767  d->readonly->setObjectName( QLatin1String( "CheckBox_readonly" ) );
2768  d->readonly->setText( i18n("Read only") );
2769  layout->addWidget(d->readonly, 1, 1);
2770 
2771  label = new QLabel( d->m_frame );
2772  label->setText( i18n("File system:") );
2773  layout->addWidget(label, 2, 0, Qt::AlignRight);
2774 
2775  QLabel *fileSystem = new QLabel( d->m_frame );
2776  layout->addWidget(fileSystem, 2, 1);
2777 
2778  label = new QLabel( d->m_frame );
2779  label->setText( devices.count()==0 ?
2780  i18n("Mount point (/mnt/floppy):") : // old style
2781  i18n("Mount point:")); // new style (combobox)
2782  layout->addWidget(label, 3, 0, Qt::AlignRight);
2783 
2784  d->mountpoint = new QLabel( d->m_frame );
2785  d->mountpoint->setObjectName( QLatin1String( "LineEdit_mountpoint" ) );
2786 
2787  layout->addWidget(d->mountpoint, 3, 1);
2788 
2789  // show disk free
2790  d->m_freeSpaceText = new QLabel(i18n("Device usage:"), d->m_frame );
2791  layout->addWidget(d->m_freeSpaceText, 4, 0, Qt::AlignRight);
2792 
2793  d->m_freeSpaceLabel = new QLabel( d->m_frame );
2794  layout->addWidget( d->m_freeSpaceLabel, 4, 1 );
2795 
2796  d->m_freeSpaceBar = new QProgressBar( d->m_frame );
2797  d->m_freeSpaceBar->setObjectName( "freeSpaceBar" );
2798  layout->addWidget(d->m_freeSpaceBar, 5, 0, 1, 2);
2799 
2800  // we show it in the slot when we know the values
2801  d->m_freeSpaceText->hide();
2802  d->m_freeSpaceLabel->hide();
2803  d->m_freeSpaceBar->hide();
2804 
2805  KSeparator* sep = new KSeparator( Qt::Horizontal, d->m_frame);
2806  layout->addWidget(sep, 6, 0, 1, 2);
2807 
2808  layout->setRowStretch(7, 1);
2809 
2810  KUrl url = KIO::NetAccess::mostLocalUrl( _props->kurl(), _props );
2811  if (!url.isLocalFile()) {
2812  return;
2813  }
2814  QString path = url.toLocalFile();
2815 
2816  QFile f( path );
2817  if ( !f.open( QIODevice::ReadOnly ) )
2818  return;
2819  f.close();
2820 
2821  const KDesktopFile _config( path );
2822  const KConfigGroup config = _config.desktopGroup();
2823  QString deviceStr = config.readEntry( "Dev" );
2824  QString mountPointStr = config.readEntry( "MountPoint" );
2825  bool ro = config.readEntry( "ReadOnly", false );
2826 
2827  fileSystem->setText(config.readEntry("FSType"));
2828 
2829  d->device->setEditText( deviceStr );
2830  if ( !deviceStr.isEmpty() ) {
2831  // Set default options for this device (first matching entry)
2832  int index = d->m_devicelist.indexOf(deviceStr);
2833  if (index != -1)
2834  {
2835  //kDebug(250) << "found it" << index;
2836  slotActivated( index );
2837  }
2838  }
2839 
2840  if ( !mountPointStr.isEmpty() )
2841  {
2842  d->mountpoint->setText( mountPointStr );
2843  updateInfo();
2844  }
2845 
2846  d->readonly->setChecked( ro );
2847 
2848  connect( d->device, SIGNAL(activated(int)),
2849  this, SIGNAL(changed()) );
2850  connect( d->device, SIGNAL(textChanged(QString)),
2851  this, SIGNAL(changed()) );
2852  connect( d->readonly, SIGNAL(toggled(bool)),
2853  this, SIGNAL(changed()) );
2854 
2855  connect( d->device, SIGNAL(textChanged(QString)),
2856  this, SLOT(slotDeviceChanged()) );
2857 }
2858 
2859 KDevicePropsPlugin::~KDevicePropsPlugin()
2860 {
2861  delete d;
2862 }
2863 
2864 // QString KDevicePropsPlugin::tabName () const
2865 // {
2866 // return i18n ("De&vice");
2867 // }
2868 
2869 void KDevicePropsPlugin::updateInfo()
2870 {
2871  // we show it in the slot when we know the values
2872  d->m_freeSpaceText->hide();
2873  d->m_freeSpaceLabel->hide();
2874  d->m_freeSpaceBar->hide();
2875 
2876  if (!d->mountpoint->text().isEmpty() && d->isMounted()) {
2877  KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo( d->mountpoint->text() );
2878  slotFoundMountPoint( info.mountPoint(), info.size()/1024, info.used()/1024, info.available()/1024);
2879  }
2880 }
2881 
2882 void KDevicePropsPlugin::slotActivated( int index )
2883 {
2884  // index can be more than the number of known devices, when the user types
2885  // a "custom" device.
2886  if (index < d->m_devicelist.count()) {
2887  // Update mountpoint so that it matches the device that was selected in the combo
2888  d->device->setEditText(d->m_devicelist[index]);
2889  d->mountpoint->setText(d->mountpointlist[index]);
2890  }
2891 
2892  updateInfo();
2893 }
2894 
2895 void KDevicePropsPlugin::slotDeviceChanged()
2896 {
2897  // Update mountpoint so that it matches the typed device
2898  int index = d->m_devicelist.indexOf( d->device->currentText() );
2899  if ( index != -1 )
2900  d->mountpoint->setText( d->mountpointlist[index] );
2901  else
2902  d->mountpoint->setText( QString() );
2903 
2904  updateInfo();
2905 }
2906 
2907 void KDevicePropsPlugin::slotFoundMountPoint( const QString&,
2908  quint64 kibSize,
2909  quint64 /*kibUsed*/,
2910  quint64 kibAvail )
2911 {
2912  d->m_freeSpaceText->show();
2913  d->m_freeSpaceLabel->show();
2914 
2915  const int percUsed = kibSize != 0 ? (100 - (int)(100.0 * kibAvail / kibSize)) : 100;
2916 
2917  d->m_freeSpaceLabel->setText(
2918  i18nc("Available space out of total partition size (percent used)", "%1 free of %2 (%3% used)",
2919  KIO::convertSizeFromKiB(kibAvail),
2920  KIO::convertSizeFromKiB(kibSize),
2921  percUsed ));
2922 
2923  d->m_freeSpaceBar->setRange(0, 100);
2924  d->m_freeSpaceBar->setValue(percUsed);
2925  d->m_freeSpaceBar->show();
2926 }
2927 
2928 bool KDevicePropsPlugin::supports( const KFileItemList& _items )
2929 {
2930  if ( _items.count() != 1 )
2931  return false;
2932  const KFileItem item = _items.first();
2933  // check if desktop file
2934  if (!item.isDesktopFile())
2935  return false;
2936 
2937  // open file and check type
2938  bool isLocal;
2939  KUrl url = item.mostLocalUrl(isLocal);
2940  if (!isLocal) {
2941  return false;
2942  }
2943 
2944  KDesktopFile config(url.toLocalFile());
2945  return config.hasDeviceType();
2946 }
2947 
2948 void KDevicePropsPlugin::applyChanges()
2949 {
2950  KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
2951  if ( !url.isLocalFile() )
2952  return;
2953  QString path = url.toLocalFile();
2954 
2955  QFile f( path );
2956  if ( !f.open( QIODevice::ReadWrite ) )
2957  {
2958  KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have sufficient "
2959  "access to write to <b>%1</b>.</qt>", path));
2960  return;
2961  }
2962  f.close();
2963 
2964  KDesktopFile _config( path );
2965  KConfigGroup config = _config.desktopGroup();
2966  config.writeEntry( "Type", QString::fromLatin1("FSDevice") );
2967 
2968  config.writeEntry( "Dev", d->device->currentText() );
2969  config.writeEntry( "MountPoint", d->mountpoint->text() );
2970 
2971  config.writeEntry( "ReadOnly", d->readonly->isChecked() );
2972 
2973  config.sync();
2974 }
2975 
2976 
2977 /* ----------------------------------------------------
2978  *
2979  * KDesktopPropsPlugin
2980  *
2981  * -------------------------------------------------- */
2982 
2983 class KDesktopPropsPlugin::KDesktopPropsPluginPrivate
2984 {
2985 public:
2986  KDesktopPropsPluginPrivate()
2987  : w( new Ui_KPropertiesDesktopBase )
2988  , m_frame( new QFrame() )
2989  {
2990  }
2991  ~KDesktopPropsPluginPrivate()
2992  {
2993  delete w;
2994  }
2995  Ui_KPropertiesDesktopBase* w;
2996  QWidget *m_frame;
2997 
2998  QString m_origCommandStr;
2999  QString m_terminalOptionStr;
3000  QString m_suidUserStr;
3001  QString m_dbusStartupType;
3002  QString m_dbusServiceName;
3003  bool m_terminalBool;
3004  bool m_suidBool;
3005  bool m_startupBool;
3006  bool m_systrayBool;
3007 };
3008 
3009 KDesktopPropsPlugin::KDesktopPropsPlugin( KPropertiesDialog *_props )
3010  : KPropertiesDialogPlugin( _props ), d( new KDesktopPropsPluginPrivate )
3011 {
3012  d->w->setupUi(d->m_frame);
3013 
3014  properties->addPage(d->m_frame, i18n("&Application"));
3015 
3016  bool bKDesktopMode = properties->kurl().protocol() == QLatin1String("desktop") ||
3017  properties->currentDir().protocol() == QLatin1String("desktop");
3018 
3019  if (bKDesktopMode)
3020  {
3021  // Hide Name entry
3022  d->w->nameEdit->hide();
3023  d->w->nameLabel->hide();
3024  }
3025 
3026  d->w->pathEdit->setMode(KFile::Directory | KFile::LocalOnly);
3027  d->w->pathEdit->lineEdit()->setAcceptDrops(false);
3028 
3029  connect( d->w->nameEdit, SIGNAL(textChanged(QString)), this, SIGNAL(changed()) );
3030  connect( d->w->genNameEdit, SIGNAL(textChanged(QString)), this, SIGNAL(changed()) );
3031  connect( d->w->commentEdit, SIGNAL(textChanged(QString)), this, SIGNAL(changed()) );
3032  connect( d->w->commandEdit, SIGNAL(textChanged(QString)), this, SIGNAL(changed()) );
3033  connect( d->w->pathEdit, SIGNAL(textChanged(QString)), this, SIGNAL(changed()) );
3034 
3035  connect( d->w->browseButton, SIGNAL(clicked()), this, SLOT(slotBrowseExec()) );
3036  connect( d->w->addFiletypeButton, SIGNAL(clicked()), this, SLOT(slotAddFiletype()) );
3037  connect( d->w->delFiletypeButton, SIGNAL(clicked()), this, SLOT(slotDelFiletype()) );
3038  connect( d->w->advancedButton, SIGNAL(clicked()), this, SLOT(slotAdvanced()) );
3039 
3040  // now populate the page
3041 
3042  KUrl url = KIO::NetAccess::mostLocalUrl( _props->kurl(), _props );
3043  if (!url.isLocalFile()) {
3044  return;
3045  }
3046  QString path = url.toLocalFile();
3047 
3048  QFile f( path );
3049  if ( !f.open( QIODevice::ReadOnly ) )
3050  return;
3051  f.close();
3052 
3053  KDesktopFile _config( path );
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  QString path = url.toLocalFile();
3203 
3204  QFile f( path );
3205 
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 _config( path );
3218  KConfigGroup config = _config.desktopGroup();
3219  config.writeEntry( "Type", QString::fromLatin1("Application"));
3220  config.writeEntry( "Comment", d->w->commentEdit->text() );
3221  config.writeEntry( "Comment", d->w->commentEdit->text(), KConfigGroup::Persistent|KConfigGroup::Localized ); // for compat
3222  config.writeEntry( "GenericName", d->w->genNameEdit->text() );
3223  config.writeEntry( "GenericName", d->w->genNameEdit->text(), KConfigGroup::Persistent|KConfigGroup::Localized ); // for compat
3224 
3225  if (d->m_systrayBool)
3226  config.writeEntry( "Exec", d->w->commandEdit->text().prepend("ksystraycmd ") );
3227  else
3228  config.writeEntry( "Exec", d->w->commandEdit->text() );
3229  config.writeEntry( "Path", d->w->pathEdit->lineEdit()->text() ); // not writePathEntry, see kservice.cpp
3230 
3231  // Write mimeTypes
3232  QStringList mimeTypes;
3233  int count = d->w->filetypeList->topLevelItemCount();
3234  for (int i = 0; i < count; ++i) {
3235  QTreeWidgetItem *item = d->w->filetypeList->topLevelItem(i);
3236  QString preference = item->text(2);
3237  mimeTypes.append(item->text(0));
3238  if (!preference.isEmpty())
3239  mimeTypes.append(preference);
3240  }
3241 
3242  kDebug() << mimeTypes;
3243  config.writeXdgListEntry( "MimeType", mimeTypes );
3244 
3245  if ( !d->w->nameEdit->isHidden() ) {
3246  QString nameStr = d->w->nameEdit->text();
3247  config.writeEntry( "Name", nameStr );
3248  config.writeEntry( "Name", nameStr, KConfigGroup::Persistent|KConfigGroup::Localized );
3249  }
3250 
3251  config.writeEntry("Terminal", d->m_terminalBool);
3252  config.writeEntry("TerminalOptions", d->m_terminalOptionStr);
3253  config.writeEntry("X-KDE-SubstituteUID", d->m_suidBool);
3254  config.writeEntry("X-KDE-Username", d->m_suidUserStr);
3255  config.writeEntry("StartupNotify", d->m_startupBool);
3256  config.writeEntry("X-DBUS-StartupType", d->m_dbusStartupType);
3257  config.writeEntry("X-DBUS-ServiceName", d->m_dbusServiceName);
3258  config.sync();
3259 
3260  // KSycoca update needed?
3261  QString sycocaPath = KGlobal::dirs()->relativeLocation("apps", path);
3262  bool updateNeeded = !sycocaPath.startsWith('/');
3263  if (!updateNeeded)
3264  {
3265  sycocaPath = KGlobal::dirs()->relativeLocation("xdgdata-apps", path);
3266  updateNeeded = !sycocaPath.startsWith('/');
3267  }
3268  if (updateNeeded)
3269  KBuildSycocaProgressDialog::rebuildKSycoca(d->m_frame);
3270 }
3271 
3272 
3273 void KDesktopPropsPlugin::slotBrowseExec()
3274 {
3275  KUrl f = KFileDialog::getOpenUrl( KUrl(),
3276  QString(), d->m_frame );
3277  if ( f.isEmpty() )
3278  return;
3279 
3280  if ( !f.isLocalFile()) {
3281  KMessageBox::sorry(d->m_frame, i18n("Only executables on local file systems are supported."));
3282  return;
3283  }
3284 
3285  QString path = f.toLocalFile();
3286  path = KShell::quoteArg( path );
3287  d->w->commandEdit->setText( path );
3288 }
3289 
3290 void KDesktopPropsPlugin::slotAdvanced()
3291 {
3292  KDialog dlg( d->m_frame );
3293  dlg.setObjectName( "KPropertiesDesktopAdv" );
3294  dlg.setModal( true );
3295  dlg.setCaption( i18n("Advanced Options for %1", properties->kurl().fileName()) );
3296  dlg.setButtons( KDialog::Ok | KDialog::Cancel );
3297  dlg.setDefaultButton( KDialog::Ok );
3298  Ui_KPropertiesDesktopAdvBase w;
3299  w.setupUi(dlg.mainWidget());
3300 
3301  // If the command is changed we reset certain settings that are strongly
3302  // coupled to the command.
3303  checkCommandChanged();
3304 
3305  // check to see if we use konsole if not do not add the nocloseonexit
3306  // because we don't know how to do this on other terminal applications
3307  KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") );
3308  QString preferredTerminal = confGroup.readPathEntry("TerminalApplication",
3309  QString::fromLatin1("konsole"));
3310 
3311  bool terminalCloseBool = false;
3312 
3313  if (preferredTerminal == "konsole")
3314  {
3315  terminalCloseBool = (d->m_terminalOptionStr.contains( "--noclose" ) > 0);
3316  w.terminalCloseCheck->setChecked(terminalCloseBool);
3317  d->m_terminalOptionStr.remove( "--noclose");
3318  }
3319  else
3320  {
3321  w.terminalCloseCheck->hide();
3322  }
3323 
3324  w.terminalCheck->setChecked(d->m_terminalBool);
3325  w.terminalEdit->setText(d->m_terminalOptionStr);
3326  w.terminalCloseCheck->setEnabled(d->m_terminalBool);
3327  w.terminalEdit->setEnabled(d->m_terminalBool);
3328  w.terminalEditLabel->setEnabled(d->m_terminalBool);
3329 
3330  w.suidCheck->setChecked(d->m_suidBool);
3331  w.suidEdit->setText(d->m_suidUserStr);
3332  w.suidEdit->setEnabled(d->m_suidBool);
3333  w.suidEditLabel->setEnabled(d->m_suidBool);
3334 
3335  w.startupInfoCheck->setChecked(d->m_startupBool);
3336  w.systrayCheck->setChecked(d->m_systrayBool);
3337 
3338  if (d->m_dbusStartupType == "unique")
3339  w.dbusCombo->setCurrentIndex(2);
3340  else if (d->m_dbusStartupType == "multi")
3341  w.dbusCombo->setCurrentIndex(1);
3342  else if (d->m_dbusStartupType == "wait")
3343  w.dbusCombo->setCurrentIndex(3);
3344  else
3345  w.dbusCombo->setCurrentIndex(0);
3346 
3347  // Provide username completion up to 1000 users.
3348  KCompletion *kcom = new KCompletion;
3349  kcom->setOrder(KCompletion::Sorted);
3350  struct passwd *pw;
3351  int i, maxEntries = 1000;
3352  setpwent();
3353  for (i=0; ((pw = getpwent()) != 0L) && (i < maxEntries); i++)
3354  kcom->addItem(QString::fromLatin1(pw->pw_name));
3355  endpwent();
3356  if (i < maxEntries)
3357  {
3358  w.suidEdit->setCompletionObject(kcom, true);
3359  w.suidEdit->setAutoDeleteCompletionObject( true );
3360  w.suidEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
3361  }
3362  else
3363  {
3364  delete kcom;
3365  }
3366 
3367  connect( w.terminalEdit, SIGNAL(textChanged(QString)),
3368  this, SIGNAL(changed()) );
3369  connect( w.terminalCloseCheck, SIGNAL(toggled(bool)),
3370  this, SIGNAL(changed()) );
3371  connect( w.terminalCheck, SIGNAL(toggled(bool)),
3372  this, SIGNAL(changed()) );
3373  connect( w.suidCheck, SIGNAL(toggled(bool)),
3374  this, SIGNAL(changed()) );
3375  connect( w.suidEdit, SIGNAL(textChanged(QString)),
3376  this, SIGNAL(changed()) );
3377  connect( w.startupInfoCheck, SIGNAL(toggled(bool)),
3378  this, SIGNAL(changed()) );
3379  connect( w.systrayCheck, SIGNAL(toggled(bool)),
3380  this, SIGNAL(changed()) );
3381  connect( w.dbusCombo, SIGNAL(activated(int)),
3382  this, SIGNAL(changed()) );
3383 
3384  if ( dlg.exec() == QDialog::Accepted )
3385  {
3386  d->m_terminalOptionStr = w.terminalEdit->text().trimmed();
3387  d->m_terminalBool = w.terminalCheck->isChecked();
3388  d->m_suidBool = w.suidCheck->isChecked();
3389  d->m_suidUserStr = w.suidEdit->text().trimmed();
3390  d->m_startupBool = w.startupInfoCheck->isChecked();
3391  d->m_systrayBool = w.systrayCheck->isChecked();
3392 
3393  if (w.terminalCloseCheck->isChecked())
3394  {
3395  d->m_terminalOptionStr.append(" --noclose");
3396  }
3397 
3398  switch(w.dbusCombo->currentIndex())
3399  {
3400  case 1: d->m_dbusStartupType = "multi"; break;
3401  case 2: d->m_dbusStartupType = "unique"; break;
3402  case 3: d->m_dbusStartupType = "wait"; break;
3403  default: d->m_dbusStartupType = "none"; break;
3404  }
3405  }
3406 }
3407 
3408 bool KDesktopPropsPlugin::supports( const KFileItemList& _items )
3409 {
3410  if ( _items.count() != 1 ) {
3411  return false;
3412  }
3413 
3414  const KFileItem item = _items.first();
3415 
3416  // check if desktop file
3417  if (!item.isDesktopFile()) {
3418  return false;
3419  }
3420 
3421  // open file and check type
3422  bool isLocal;
3423  KUrl url = item.mostLocalUrl( isLocal );
3424  if (!isLocal) {
3425  return false;
3426  }
3427 
3428  KDesktopFile config(url.toLocalFile());
3429  return config.hasApplicationType() &&
3430  KAuthorized::authorize("run_desktop_files") &&
3431  KAuthorized::authorize("shell_access");
3432 }
3433 
3434 #include "kpropertiesdialog.moc"
3435 #include "kpropertiesdialog_p.moc"
3436 
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Fri Dec 7 2012 16:08:40 by doxygen 1.8.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.8.5 API Reference

Skip menu "kdelibs-4.8.5 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