• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KIO

kpropertiesdialog.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002 
00003    Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
00004    Copyright (c) 1999, 2000 Preston Brown <pbrown@kde.org>
00005    Copyright (c) 2000 Simon Hausmann <hausmann@kde.org>
00006    Copyright (c) 2000 David Faure <faure@kde.org>
00007    Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
00008 
00009    This library is free software; you can redistribute it and/or
00010    modify it under the terms of the GNU Library General Public
00011    License as published by the Free Software Foundation; either
00012    version 2 of the License, or (at your option) any later version.
00013 
00014    This library is distributed in the hope that it will be useful,
00015    but WITHOUT ANY WARRANTY; without even the implied warranty of
00016    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017    Library General Public License for more details.
00018 
00019    You should have received a copy of the GNU Library General Public License
00020    along with this library; see the file COPYING.LIB.  If not, write to
00021    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00022    Boston, MA 02110-1301, USA.
00023 */
00024 
00025 /*
00026  * kpropertiesdialog.cpp
00027  * View/Edit Properties of files, locally or remotely
00028  *
00029  * some FilePermissionsPropsPlugin-changes by
00030  *  Henner Zeller <zeller@think.de>
00031  * some layout management by
00032  *  Bertrand Leconte <B.Leconte@mail.dotcom.fr>
00033  * the rest of the layout management, bug fixes, adaptation to libkio,
00034  * template feature by
00035  *  David Faure <faure@kde.org>
00036  * More layout, cleanups, and fixes by
00037  *  Preston Brown <pbrown@kde.org>
00038  * Plugin capability, cleanups and port to KDialog by
00039  *  Simon Hausmann <hausmann@kde.org>
00040  * KDesktopPropsPlugin by
00041  *  Waldo Bastian <bastian@kde.org>
00042  */
00043 
00044 #include "kpropertiesdialog.h"
00045 #include "kpropertiesdialog_p.h"
00046 
00047 
00048 #include <config.h>
00049 #include <config-acl.h>
00050 extern "C" {
00051 #include <pwd.h>
00052 #include <grp.h>
00053 #include <time.h>
00054 #include <sys/stat.h>
00055 #include <sys/types.h>
00056 }
00057 #include <unistd.h>
00058 #include <errno.h>
00059 #include <assert.h>
00060 #include <algorithm>
00061 #include <functional>
00062 
00063 #include <QtCore/QFile>
00064 #include <QtCore/QDir>
00065 #include <QtGui/QLabel>
00066 #include <QtGui/QPushButton>
00067 #include <QtGui/QCheckBox>
00068 #include <QtCore/QMutableStringListIterator>
00069 #include <QtCore/QTextIStream>
00070 #include <QtGui/QPainter>
00071 #include <QtGui/QLayout>
00072 #include <QtGui/QStyle>
00073 #include <QtGui/QProgressBar>
00074 #include <QVector>
00075 
00076 #ifdef HAVE_POSIX_ACL
00077 extern "C" {
00078 #  include <sys/xattr.h>
00079 }
00080 #endif
00081 
00082 #include <kauthorized.h>
00083 #include <kdialog.h>
00084 #include <kdirwatch.h>
00085 #include <kdirnotify.h>
00086 #include <kdiskfreespace.h>
00087 #include <kdebug.h>
00088 #include <kdesktopfile.h>
00089 #include <kicondialog.h>
00090 #include <kurl.h>
00091 #include <kurlrequester.h>
00092 #include <klocale.h>
00093 #include <kglobal.h>
00094 #include <kglobalsettings.h>
00095 #include <kstandarddirs.h>
00096 #include <kjobuidelegate.h>
00097 #include <kio/job.h>
00098 #include <kio/copyjob.h>
00099 #include <kio/chmodjob.h>
00100 #include <kio/directorysizejob.h>
00101 #include <kio/renamedialog.h>
00102 #include <kio/netaccess.h>
00103 #include <kfiledialog.h>
00104 #include <kmimetype.h>
00105 #include <kmountpoint.h>
00106 #include <kiconloader.h>
00107 #include <kmessagebox.h>
00108 #include <kservice.h>
00109 #include <kcombobox.h>
00110 #include <kcompletion.h>
00111 #include <klineedit.h>
00112 #include <kseparator.h>
00113 #include <ksqueezedtextlabel.h>
00114 #include <klibloader.h>
00115 #include <kmimetypetrader.h>
00116 #include <kmetaprops.h>
00117 #include <kpreviewprops.h>
00118 #include <krun.h>
00119 #include <kvbox.h>
00120 #include <kacl.h>
00121 #include <kconfiggroup.h>
00122 #include <kshell.h>
00123 #ifndef Q_OS_WIN
00124 #include "kfilesharedialog.h"
00125 #endif
00126 
00127 #include "ui_kpropertiesdesktopbase.h"
00128 #include "ui_kpropertiesdesktopadvbase.h"
00129 #ifdef HAVE_POSIX_ACL
00130 #include "kacleditwidget.h"
00131 #endif
00132 
00133 #include <kbuildsycocaprogressdialog.h>
00134 #include <kmimetypechooser.h>
00135 
00136 #ifdef Q_WS_WIN
00137 # include <kkernel_win.h>
00138 #ifdef __GNUC__
00139 # warning TODO: port completely to win32
00140 #endif
00141 #endif
00142 
00143 using namespace KDEPrivate;
00144 
00145 static QString nameFromFileName(QString nameStr)
00146 {
00147    if ( nameStr.endsWith(".desktop") )
00148       nameStr.truncate( nameStr.length() - 8 );
00149    if ( nameStr.endsWith(".kdelnk") )
00150       nameStr.truncate( nameStr.length() - 7 );
00151    // Make it human-readable (%2F => '/', ...)
00152    nameStr = KIO::decodeFileName( nameStr );
00153    return nameStr;
00154 }
00155 
00156 mode_t KFilePermissionsPropsPlugin::fperm[3][4] = {
00157         {S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID},
00158         {S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID},
00159         {S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX}
00160     };
00161 
00162 class KPropertiesDialog::KPropertiesDialogPrivate
00163 {
00164 public:
00165   KPropertiesDialogPrivate(KPropertiesDialog *qq)
00166   {
00167         q = qq;
00168     m_aborted = false;
00169     fileSharePage = 0;
00170   }
00171   ~KPropertiesDialogPrivate()
00172   {
00173   }
00174 
00178     void init();
00182     void insertPages();
00183 
00184     KPropertiesDialog *q;
00185   bool m_aborted:1;
00186   QWidget* fileSharePage;
00190     KUrl m_singleUrl;
00194     KFileItemList m_items;
00198     QString m_defaultName;
00199     KUrl m_currentDir;
00203     QList<KPropertiesDialogPlugin*> m_pageList;
00204 };
00205 
00206 KPropertiesDialog::KPropertiesDialog (const KFileItem& item,
00207                                       QWidget* parent)
00208     : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
00209 {
00210   setCaption( i18n( "Properties for %1" , KIO::decodeFileName(item.url().fileName())) );
00211 
00212   assert( !item.isNull() );
00213     d->m_items.append(item);
00214 
00215     d->m_singleUrl = item.url();
00216     assert(!d->m_singleUrl.isEmpty());
00217 
00218     d->init();
00219 }
00220 
00221 KPropertiesDialog::KPropertiesDialog (const QString& title,
00222                                       QWidget* parent)
00223     : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
00224 {
00225   setCaption( i18n( "Properties for %1", title ) );
00226 
00227     d->init();
00228 }
00229 
00230 KPropertiesDialog::KPropertiesDialog(const KFileItemList& _items,
00231                                      QWidget* parent)
00232     : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
00233 {
00234   if ( _items.count() > 1 )
00235     setCaption( i18np( "Properties for 1 item", "Properties for %1 Selected Items", _items.count() ) );
00236   else
00237     setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_items.first().url().fileName())) );
00238 
00239   assert( !_items.isEmpty() );
00240     d->m_singleUrl = _items.first().url();
00241     assert(!d->m_singleUrl.isEmpty());
00242 
00243     d->m_items = _items;
00244 
00245     d->init();
00246 }
00247 
00248 KPropertiesDialog::KPropertiesDialog (const KUrl& _url,
00249                                       QWidget* parent)
00250     : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
00251 {
00252   setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_url.fileName()))  );
00253 
00254     d->m_singleUrl = _url;
00255 
00256   KIO::UDSEntry entry;
00257   KIO::NetAccess::stat(_url, entry, parent);
00258 
00259     d->m_items.append(KFileItem(entry, _url));
00260     d->init();
00261 }
00262 
00263 KPropertiesDialog::KPropertiesDialog (const KUrl& _tempUrl, const KUrl& _currentDir,
00264                                       const QString& _defaultName,
00265                                       QWidget* parent)
00266     : KPageDialog(parent), d(new KPropertiesDialogPrivate(this))
00267 {
00268   setCaption( i18n( "Properties for %1" , KIO::decodeFileName(_tempUrl.fileName()))  );
00269 
00270     d->m_singleUrl = _tempUrl;
00271     d->m_defaultName = _defaultName;
00272     d->m_currentDir = _currentDir;
00273     assert(!d->m_singleUrl.isEmpty());
00274 
00275   // Create the KFileItem for the _template_ file, in order to read from it.
00276     d->m_items.append(KFileItem(KFileItem::Unknown, KFileItem::Unknown, d->m_singleUrl));
00277     d->init();
00278 }
00279 
00280 bool KPropertiesDialog::showDialog(const KFileItem& item, QWidget* parent,
00281                                    bool modal)
00282 {
00283   // TODO: do we really want to show the win32 property dialog?
00284   // This means we lose metainfo, support for .desktop files, etc. (DF)
00285 #ifdef Q_WS_WIN
00286   QString localPath = item.localPath();
00287   if (!localPath.isEmpty())
00288     return showWin32FilePropertyDialog(localPath);
00289 #endif
00290   KPropertiesDialog* dlg = new KPropertiesDialog(item, parent);
00291   if (modal) {
00292       dlg->exec();
00293   } else {
00294       dlg->show();
00295   }
00296 
00297   return true;
00298 }
00299 
00300 bool KPropertiesDialog::showDialog(const KUrl& _url, QWidget* parent,
00301                                    bool modal)
00302 {
00303 #ifdef Q_WS_WIN
00304   if (_url.isLocalFile())
00305     return showWin32FilePropertyDialog( _url.path() );
00306 #endif
00307   KPropertiesDialog* dlg = new KPropertiesDialog(_url, parent);
00308   if (modal) {
00309       dlg->exec();
00310   } else {
00311       dlg->show();
00312   }
00313 
00314   return true;
00315 }
00316 
00317 bool KPropertiesDialog::showDialog(const KFileItemList& _items, QWidget* parent,
00318                                    bool modal)
00319 {
00320   if (_items.count()==1) {
00321       const KFileItem item = _items.first();
00322       if (item.entry().count() == 0 && item.localPath().isEmpty()) // this remote item wasn't listed by a slave
00323          // Let's stat to get more info on the file
00324           return KPropertiesDialog::showDialog(item.url(), parent, modal);
00325       else
00326           return KPropertiesDialog::showDialog(_items.first(), parent, modal);
00327   }
00328   KPropertiesDialog* dlg = new KPropertiesDialog(_items, parent);
00329   if (modal) {
00330       dlg->exec();
00331   } else {
00332       dlg->show();
00333   }
00334   return true;
00335 }
00336 
00337 void KPropertiesDialog::KPropertiesDialogPrivate::init()
00338 {
00339     q->setFaceType(KPageDialog::Tabbed);
00340     q->setButtons(KDialog::Ok | KDialog::Cancel);
00341     q->setDefaultButton(KDialog::Ok);
00342 
00343     connect(q, SIGNAL(okClicked()), q, SLOT(slotOk()));
00344     connect(q, SIGNAL(cancelClicked()), q, SLOT(slotCancel()));
00345 
00346     insertPages();
00347 
00348     KConfigGroup group(KGlobal::config(), "KPropertiesDialog");
00349     q->restoreDialogSize(group);
00350 }
00351 
00352 void KPropertiesDialog::showFileSharingPage()
00353 {
00354   if (d->fileSharePage) {
00355     // FIXME: this showFileSharingPage thingy looks broken! (tokoe)
00356     // showPage( pageIndex( d->fileSharePage));
00357   }
00358 }
00359 
00360 void KPropertiesDialog::setFileSharingPage(QWidget* page) {
00361   d->fileSharePage = page;
00362 }
00363 
00364 
00365 void KPropertiesDialog::setFileNameReadOnly( bool ro )
00366 {
00367     foreach(KPropertiesDialogPlugin *it, d->m_pageList) {
00368         KFilePropsPlugin* plugin = dynamic_cast<KFilePropsPlugin*>(it);
00369         if ( plugin ) {
00370             plugin->setFileNameReadOnly( ro );
00371             break;
00372         }
00373     }
00374 }
00375 
00376 KPropertiesDialog::~KPropertiesDialog()
00377 {
00378     qDeleteAll(d->m_pageList);
00379     delete d;
00380 
00381     KConfigGroup group(KGlobal::config(), "KPropertiesDialog");
00382     saveDialogSize(group, KConfigBase::Persistent);
00383 }
00384 
00385 void KPropertiesDialog::insertPlugin (KPropertiesDialogPlugin* plugin)
00386 {
00387   connect (plugin, SIGNAL (changed ()),
00388            plugin, SLOT (setDirty ()));
00389 
00390     d->m_pageList.append(plugin);
00391 }
00392 
00393 KUrl KPropertiesDialog::kurl() const
00394 {
00395     return d->m_singleUrl;
00396 }
00397 
00398 KFileItem& KPropertiesDialog::item()
00399 {
00400     return d->m_items.first();
00401 }
00402 
00403 KFileItemList KPropertiesDialog::items() const
00404 {
00405     return d->m_items;
00406 }
00407 
00408 KUrl KPropertiesDialog::currentDir() const
00409 {
00410     return d->m_currentDir;
00411 }
00412 
00413 QString KPropertiesDialog::defaultName() const
00414 {
00415     return d->m_defaultName;
00416 }
00417 
00418 bool KPropertiesDialog::canDisplay( const KFileItemList& _items )
00419 {
00420   // TODO: cache the result of those calls. Currently we parse .desktop files far too many times
00421   return KFilePropsPlugin::supports( _items ) ||
00422          KFilePermissionsPropsPlugin::supports( _items ) ||
00423          KDesktopPropsPlugin::supports( _items ) ||
00424          KUrlPropsPlugin::supports( _items ) ||
00425          KDevicePropsPlugin::supports( _items ) ||
00426          KFileMetaPropsPlugin::supports( _items ) ||
00427          KPreviewPropsPlugin::supports( _items );
00428 }
00429 
00430 void KPropertiesDialog::slotOk()
00431 {
00432   QList<KPropertiesDialogPlugin*>::const_iterator pageListIt;
00433   d->m_aborted = false;
00434 
00435   KFilePropsPlugin * filePropsPlugin = 0L;
00436     if (qobject_cast<KFilePropsPlugin*>(d->m_pageList.first()))
00437         filePropsPlugin = static_cast<KFilePropsPlugin *>(d->m_pageList.first());
00438 
00439   // If any page is dirty, then set the main one (KFilePropsPlugin) as
00440   // dirty too. This is what makes it possible to save changes to a global
00441   // desktop file into a local one. In other cases, it doesn't hurt.
00442   for (pageListIt = d->m_pageList.constBegin(); pageListIt != d->m_pageList.constEnd(); ++pageListIt) {
00443     if ( (*pageListIt)->isDirty() && filePropsPlugin )
00444     {
00445         filePropsPlugin->setDirty();
00446         break;
00447     }
00448   }
00449 
00450   // Apply the changes in the _normal_ order of the tabs now
00451   // This is because in case of renaming a file, KFilePropsPlugin will call
00452   // KPropertiesDialog::rename, so other tab will be ok with whatever order
00453   // BUT for file copied from templates, we need to do the renaming first !
00454   for (pageListIt = d->m_pageList.constBegin(); pageListIt != d->m_pageList.constEnd() && !d->m_aborted; ++pageListIt) {
00455     if ( (*pageListIt)->isDirty() )
00456     {
00457       kDebug( 250 ) << "applying changes for " << (*pageListIt)->metaObject()->className();
00458       (*pageListIt)->applyChanges();
00459       // applyChanges may change d->m_aborted.
00460     }
00461     else {
00462       kDebug( 250 ) << "skipping page " << (*pageListIt)->metaObject()->className();
00463     }
00464   }
00465 
00466   if ( !d->m_aborted && filePropsPlugin )
00467     filePropsPlugin->postApplyChanges();
00468 
00469   if ( !d->m_aborted )
00470   {
00471     emit applied();
00472     emit propertiesClosed();
00473     deleteLater(); // somewhat like Qt::WA_DeleteOnClose would do.
00474     accept();
00475   } // else, keep dialog open for user to fix the problem.
00476 }
00477 
00478 void KPropertiesDialog::slotCancel()
00479 {
00480   emit canceled();
00481   emit propertiesClosed();
00482 
00483   deleteLater();
00484   done( Rejected );
00485 }
00486 
00487 void KPropertiesDialog::KPropertiesDialogPrivate::insertPages()
00488 {
00489   if (m_items.isEmpty())
00490     return;
00491 
00492   if ( KFilePropsPlugin::supports( m_items ) )
00493   {
00494         KPropertiesDialogPlugin *p = new KFilePropsPlugin(q);
00495         q->insertPlugin(p);
00496   }
00497 
00498   if ( KFilePermissionsPropsPlugin::supports( m_items ) )
00499   {
00500         KPropertiesDialogPlugin *p = new KFilePermissionsPropsPlugin(q);
00501         q->insertPlugin(p);
00502   }
00503 
00504   if ( KDesktopPropsPlugin::supports( m_items ) )
00505   {
00506         KPropertiesDialogPlugin *p = new KDesktopPropsPlugin(q);
00507         q->insertPlugin(p);
00508   }
00509 
00510   if ( KUrlPropsPlugin::supports( m_items ) )
00511   {
00512         KPropertiesDialogPlugin *p = new KUrlPropsPlugin(q);
00513         q->insertPlugin(p);
00514   }
00515 
00516   if ( KDevicePropsPlugin::supports( m_items ) )
00517   {
00518         KPropertiesDialogPlugin *p = new KDevicePropsPlugin(q);
00519         q->insertPlugin(p);
00520   }
00521 
00522   if ( KFileMetaPropsPlugin::supports( m_items ) )
00523   {
00524         KPropertiesDialogPlugin *p = new KFileMetaPropsPlugin(q);
00525         q->insertPlugin(p);
00526   }
00527 
00528   if ( KPreviewPropsPlugin::supports( m_items ) )
00529   {
00530         KPropertiesDialogPlugin *p = new KPreviewPropsPlugin(q);
00531         q->insertPlugin(p);
00532   }
00533 
00534 #ifndef Q_OS_WIN
00535   if ( KAuthorized::authorizeKAction("sharefile") &&
00536        KFileSharePropsPlugin::supports( m_items ) )
00537   {
00538         KPropertiesDialogPlugin *p = new KFileSharePropsPlugin(q);
00539         q->insertPlugin(p);
00540   }
00541 #endif
00542 
00543   //plugins
00544 
00545   if ( m_items.count() != 1 )
00546     return;
00547 
00548   const KFileItem item = m_items.first();
00549   const QString mimetype = item.mimetype();
00550 
00551   if ( mimetype.isEmpty() )
00552     return;
00553 
00554   QString query = QString::fromLatin1(
00555       "((not exist [X-KDE-Protocol]) or "
00556       " ([X-KDE-Protocol] == '%1'  )   )"
00557       ).arg(item.url().protocol());
00558 
00559   kDebug( 250 ) << "trader query: " << query;
00560   KService::List offers = KMimeTypeTrader::self()->query( mimetype, "KPropertiesDialog/Plugin", query );
00561   foreach (const KService::Ptr &ptr, offers) {
00562     KPropertiesDialogPlugin *plugin = ptr->createInstance<KPropertiesDialogPlugin>(q);
00563     if (!plugin)
00564         continue;
00565     plugin->setObjectName(ptr->name());
00566 
00567     q->insertPlugin(plugin);
00568   }
00569 }
00570 
00571 void KPropertiesDialog::updateUrl( const KUrl& _newUrl )
00572 {
00573     Q_ASSERT(d->m_items.count() == 1);
00574   kDebug(250) << "KPropertiesDialog::updateUrl (pre)" << _newUrl.url();
00575   KUrl newUrl = _newUrl;
00576     emit saveAs(d->m_singleUrl, newUrl);
00577   kDebug(250) << "KPropertiesDialog::updateUrl (post)" << newUrl.url();
00578 
00579     d->m_singleUrl = newUrl;
00580     d->m_items.first().setUrl(newUrl);
00581     assert(!d->m_singleUrl.isEmpty());
00582   // If we have an Desktop page, set it dirty, so that a full file is saved locally
00583   // Same for a URL page (because of the Name= hack)
00584     foreach (KPropertiesDialogPlugin *it, d->m_pageList) {
00585    if ( qobject_cast<KUrlPropsPlugin*>(it) ||
00586         qobject_cast<KDesktopPropsPlugin*>(it) )
00587    {
00588      //kDebug(250) << "Setting page dirty";
00589      it->setDirty();
00590      break;
00591    }
00592   }
00593 }
00594 
00595 void KPropertiesDialog::rename( const QString& _name )
00596 {
00597     Q_ASSERT(d->m_items.count() == 1);
00598     kDebug(250) << "KPropertiesDialog::rename " << _name;
00599     KUrl newUrl;
00600     // if we're creating from a template : use currentdir
00601     if (!d->m_currentDir.isEmpty()) {
00602         newUrl = d->m_currentDir;
00603         newUrl.addPath(_name);
00604     } else {
00605         QString tmpurl = d->m_singleUrl.url();
00606         if (!tmpurl.isEmpty() && tmpurl.at(tmpurl.length() - 1) == '/') {
00607             // It's a directory, so strip the trailing slash first
00608             tmpurl.truncate(tmpurl.length() - 1);
00609         }
00610 
00611         newUrl = tmpurl;
00612         newUrl.setFileName(_name);
00613     }
00614     updateUrl(newUrl);
00615 }
00616 
00617 void KPropertiesDialog::abortApplying()
00618 {
00619   d->m_aborted = true;
00620 }
00621 
00622 class KPropertiesDialogPlugin::KPropertiesDialogPluginPrivate
00623 {
00624 public:
00625   KPropertiesDialogPluginPrivate()
00626   {
00627   }
00628   ~KPropertiesDialogPluginPrivate()
00629   {
00630   }
00631 
00632   bool m_bDirty;
00633   int fontHeight;
00634 };
00635 
00636 KPropertiesDialogPlugin::KPropertiesDialogPlugin( KPropertiesDialog *_props )
00637 : QObject( _props ),d(new KPropertiesDialogPluginPrivate)
00638 {
00639   properties = _props;
00640   d->fontHeight = 2*properties->fontMetrics().height();
00641   d->m_bDirty = false;
00642 }
00643 
00644 KPropertiesDialogPlugin::~KPropertiesDialogPlugin()
00645 {
00646   delete d;
00647 }
00648 
00649 bool KPropertiesDialogPlugin::isDesktopFile( const KFileItem& _item )
00650 {
00651     return _item.isDesktopFile();
00652 }
00653 
00654 void KPropertiesDialogPlugin::setDirty( bool b )
00655 {
00656   d->m_bDirty = b;
00657 }
00658 
00659 void KPropertiesDialogPlugin::setDirty()
00660 {
00661   d->m_bDirty = true;
00662 }
00663 
00664 bool KPropertiesDialogPlugin::isDirty() const
00665 {
00666   return d->m_bDirty;
00667 }
00668 
00669 void KPropertiesDialogPlugin::applyChanges()
00670 {
00671   kWarning(250) << "applyChanges() not implemented in page !";
00672 }
00673 
00674 int KPropertiesDialogPlugin::fontHeight() const
00675 {
00676   return d->fontHeight;
00677 }
00678 
00680 
00681 class KFilePropsPlugin::KFilePropsPluginPrivate
00682 {
00683 public:
00684   KFilePropsPluginPrivate()
00685   {
00686     dirSizeJob = 0L;
00687     dirSizeUpdateTimer = 0L;
00688     m_lined = 0;
00689     m_freeSpaceLabel = 0;
00690   }
00691   ~KFilePropsPluginPrivate()
00692   {
00693     if ( dirSizeJob )
00694       dirSizeJob->kill();
00695   }
00696 
00697   KIO::DirectorySizeJob * dirSizeJob;
00698   QTimer *dirSizeUpdateTimer;
00699   QFrame *m_frame;
00700   bool bMultiple;
00701   bool bIconChanged;
00702   bool bKDesktopMode;
00703   bool bDesktopFile;
00704   QLabel *m_freeSpaceLabel;
00705   QString mimeType;
00706   QString oldFileName;
00707   KLineEdit* m_lined;
00708 
00709   QWidget *iconArea;
00710   QWidget *nameArea;
00711 
00712   QLabel *m_sizeLabel;
00713   QPushButton *m_sizeDetermineButton;
00714   QPushButton *m_sizeStopButton;
00715 
00716   QString m_sRelativePath;
00717   bool m_bFromTemplate;
00718 
00722   QString oldName;
00723 };
00724 
00725 KFilePropsPlugin::KFilePropsPlugin( KPropertiesDialog *_props )
00726   : KPropertiesDialogPlugin( _props ),d(new KFilePropsPluginPrivate)
00727 {
00728   d->bMultiple = (properties->items().count() > 1);
00729   d->bIconChanged = false;
00730   d->bKDesktopMode = (qApp->objectName() == "kdesktop");
00731   d->bDesktopFile = KDesktopPropsPlugin::supports(properties->items());
00732   kDebug(250) << "KFilePropsPlugin::KFilePropsPlugin bMultiple=" << d->bMultiple;
00733 
00734   // We set this data from the first item, and we'll
00735   // check that the other items match against it, resetting when not.
00736   bool isLocal;
00737   const KFileItem item = properties->item();
00738   KUrl url = item.mostLocalUrl( isLocal );
00739   bool isReallyLocal = item.url().isLocalFile();
00740   bool bDesktopFile = item.isDesktopFile();
00741   mode_t mode = item.mode();
00742   bool hasDirs = item.isDir() && !item.isLink();
00743   bool hasRoot = url.path() == QLatin1String("/");
00744   QString iconStr = KMimeType::iconNameForUrl(url, mode);
00745   QString directory = properties->kurl().directory();
00746   QString protocol = properties->kurl().protocol();
00747   QString mimeComment = item.mimeComment();
00748   d->mimeType = item.mimetype();
00749   KIO::filesize_t totalSize = item.size();
00750   QString magicMimeComment;
00751   if ( isLocal ) {
00752       KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() );
00753       if ( magicMimeType->name() != KMimeType::defaultMimeType() )
00754           magicMimeComment = magicMimeType->comment();
00755   }
00756 
00757   // Those things only apply to 'single file' mode
00758   QString filename= QString();
00759   bool isTrash = false;
00760   bool isDevice = false;
00761   d->m_bFromTemplate = false;
00762 
00763   // And those only to 'multiple' mode
00764   uint iDirCount = hasDirs ? 1 : 0;
00765   uint iFileCount = 1-iDirCount;
00766 
00767   d->m_frame = new QFrame();
00768   properties->addPage(d->m_frame, i18n("&General"));
00769 
00770   QVBoxLayout *vbl = new QVBoxLayout( d->m_frame );
00771   vbl->setMargin( 0 );
00772   vbl->setSpacing( KDialog::spacingHint() );
00773   vbl->setObjectName( QLatin1String( "vbl" ) );
00774   QGridLayout *grid = new QGridLayout(); // unknown rows
00775   grid->setColumnStretch(0, 0);
00776   grid->setColumnStretch(1, 0);
00777   grid->setColumnStretch(2, 1);
00778   grid->addItem(new QSpacerItem(KDialog::spacingHint(),0), 0, 1);
00779   vbl->addLayout(grid);
00780   int curRow = 0;
00781 
00782   if ( !d->bMultiple )
00783   {
00784     QString path;
00785     if ( !d->m_bFromTemplate ) {
00786       isTrash = ( properties->kurl().protocol().toLower() == "trash" );
00787       isDevice = ( properties->kurl().protocol().toLower() == "device" );
00788       // Extract the full name, but without file: for local files
00789       if ( isReallyLocal )
00790         path = properties->kurl().path();
00791       else
00792         path = properties->kurl().prettyUrl();
00793     } else {
00794       path = properties->currentDir().path(KUrl::AddTrailingSlash) + properties->defaultName();
00795       directory = properties->currentDir().prettyUrl();
00796     }
00797 
00798     if (d->bDesktopFile) {
00799       determineRelativePath( path );
00800     }
00801 
00802     // Extract the file name only
00803     filename = properties->defaultName();
00804     if ( filename.isEmpty() ) { // no template
00805       filename = item.name(); // this gives support for UDS_NAME, e.g. for kio_trash or kio_system
00806     } else {
00807       d->m_bFromTemplate = true;
00808       setDirty(); // to enforce that the copy happens
00809     }
00810     d->oldFileName = filename;
00811 
00812     // Make it human-readable
00813     filename = nameFromFileName( filename );
00814 
00815     if ( d->bKDesktopMode && d->bDesktopFile ) {
00816         KDesktopFile config( url.path() );
00817         if ( config.desktopGroup().hasKey( "Name" ) ) {
00818             filename = config.readName();
00819         }
00820     }
00821 
00822     d->oldName = filename;
00823   }
00824   else
00825   {
00826     // Multiple items: see what they have in common
00827     const KFileItemList items = properties->items();
00828     KFileItemList::const_iterator kit = items.begin();
00829     const KFileItemList::const_iterator kend = items.end();
00830     for ( ++kit /*no need to check the first one again*/ ; kit != kend; ++kit )
00831     {
00832       const KUrl url = (*kit).url();
00833       kDebug(250) << "KFilePropsPlugin::KFilePropsPlugin " << url.prettyUrl();
00834       // The list of things we check here should match the variables defined
00835       // at the beginning of this method.
00836       if ( url.isLocalFile() != isLocal )
00837         isLocal = false; // not all local
00838       if ( bDesktopFile && (*kit).isDesktopFile() != bDesktopFile )
00839         bDesktopFile = false; // not all desktop files
00840       if ( (*kit).mode() != mode )
00841         mode = (mode_t)0;
00842       if ( KMimeType::iconNameForUrl(url, mode) != iconStr )
00843         iconStr = "document-multiple";
00844       if ( url.directory() != directory )
00845         directory.clear();
00846       if ( url.protocol() != protocol )
00847         protocol.clear();
00848       if ( !mimeComment.isNull() && (*kit).mimeComment() != mimeComment )
00849         mimeComment.clear();
00850       if ( isLocal && !magicMimeComment.isNull() ) {
00851           KMimeType::Ptr magicMimeType = KMimeType::findByFileContent( url.path() );
00852           if ( magicMimeType->comment() != magicMimeComment )
00853               magicMimeComment.clear();
00854       }
00855 
00856       if ( isLocal && url.path() == QLatin1String("/") )
00857         hasRoot = true;
00858       if ( (*kit).isDir() && !(*kit).isLink() )
00859       {
00860         iDirCount++;
00861         hasDirs = true;
00862       }
00863       else
00864       {
00865         iFileCount++;
00866         totalSize += (*kit).size();
00867       }
00868     }
00869   }
00870 
00871   if (!isReallyLocal && !protocol.isEmpty())
00872   {
00873     directory += ' ';
00874     directory += '(';
00875     directory += protocol;
00876     directory += ')';
00877   }
00878 
00879   if ( !isDevice && !isTrash && (bDesktopFile || S_ISDIR(mode)) && !d->bMultiple /*not implemented for multiple*/ )
00880   {
00881     KIconButton *iconButton = new KIconButton( d->m_frame );
00882     int bsize = 66 + 2 * iconButton->style()->pixelMetric(QStyle::PM_ButtonMargin);
00883     iconButton->setFixedSize(bsize, bsize);
00884     iconButton->setIconSize(48);
00885     iconButton->setStrictIconSize(false);
00886     // This works for everything except Device icons on unmounted devices
00887     // So we have to really open .desktop files
00888     QString iconStr = KMimeType::findByUrl( url, mode )->iconName( url );
00889     if ( bDesktopFile && isLocal )
00890     {
00891       KDesktopFile config( url.path() );
00892       KConfigGroup group = config.desktopGroup();
00893       iconStr = group.readEntry( "Icon" );
00894       if ( config.hasDeviceType() )
00895     iconButton->setIconType( KIconLoader::Desktop, KIconLoader::Device );
00896       else
00897     iconButton->setIconType( KIconLoader::Desktop, KIconLoader::Application );
00898     } else
00899       iconButton->setIconType( KIconLoader::Desktop, KIconLoader::Place );
00900     iconButton->setIcon(iconStr);
00901     d->iconArea = iconButton;
00902     connect( iconButton, SIGNAL( iconChanged(const QString&) ),
00903              this, SLOT( slotIconChanged() ) );
00904   } else {
00905     QLabel *iconLabel = new QLabel( d->m_frame );
00906     int bsize = 66 + 2 * iconLabel->style()->pixelMetric(QStyle::PM_ButtonMargin);
00907     iconLabel->setFixedSize(bsize, bsize);
00908     iconLabel->setPixmap( KIconLoader::global()->loadIcon( iconStr, KIconLoader::Desktop, 48) );
00909     d->iconArea = iconLabel;
00910   }
00911   grid->addWidget(d->iconArea, curRow, 0, Qt::AlignLeft);
00912 
00913   if (d->bMultiple || isTrash || isDevice || hasRoot)
00914   {
00915     QLabel *lab = new QLabel(d->m_frame );
00916     if ( d->bMultiple )
00917       lab->setText( KIO::itemsSummaryString( iFileCount + iDirCount, iFileCount, iDirCount, 0, false ) );
00918     else
00919       lab->setText( filename );
00920     d->nameArea = lab;
00921   } else
00922   {
00923     d->m_lined = new KLineEdit( d->m_frame );
00924     d->m_lined->setText(filename);
00925     d->nameArea = d->m_lined;
00926     d->m_lined->setFocus();
00927 
00928     // Enhanced rename: Don't highlight the file extension.
00929     QString extension = KMimeType::extractKnownExtension( filename );
00930     if ( !extension.isEmpty() )
00931       d->m_lined->setSelection( 0, filename.length() - extension.length() - 1 );
00932     else
00933     {
00934       int lastDot = filename.lastIndexOf('.');
00935       if (lastDot > 0)
00936         d->m_lined->setSelection(0, lastDot);
00937     }
00938 
00939     connect( d->m_lined, SIGNAL( textChanged( const QString & ) ),
00940              this, SLOT( nameFileChanged(const QString & ) ) );
00941   }
00942 
00943   grid->addWidget(d->nameArea, curRow++, 2);
00944 
00945   KSeparator* sep = new KSeparator( Qt::Horizontal, d->m_frame);
00946   grid->addWidget(sep, curRow, 0, 1, 3);
00947   ++curRow;
00948 
00949   QLabel *l;
00950   if ( !mimeComment.isEmpty() && !isDevice && !isTrash)
00951   {
00952     l = new QLabel(i18n("Type:"), d->m_frame );
00953 
00954     grid->addWidget(l, curRow, 0);
00955 
00956     KHBox *box = new KHBox(d->m_frame);
00957     box->setSpacing(20);
00958     l = new QLabel(mimeComment, box );
00959 
00960 #ifdef Q_WS_X11
00961     //TODO: wrap for win32 or mac?
00962     QPushButton *button = new QPushButton(box);
00963 
00964     button->setIcon( KIcon(QString::fromLatin1("configure")) );
00965     const int pixmapSize = button->style()->pixelMetric(QStyle::PM_SmallIconSize);
00966     button->setFixedSize( pixmapSize+8, pixmapSize+8 );
00967     if ( d->mimeType == KMimeType::defaultMimeType() )
00968        button->setToolTip(i18n("Create new file type"));
00969     else
00970        button->setToolTip(i18n("Edit file type"));
00971 
00972     connect( button, SIGNAL( clicked() ), SLOT( slotEditFileType() ));
00973 
00974     if (!KAuthorized::authorizeKAction("editfiletype"))
00975        button->hide();
00976 #endif
00977 
00978     grid->addWidget(box, curRow++, 2);
00979   }
00980 
00981   if ( !magicMimeComment.isEmpty() && magicMimeComment != mimeComment )
00982   {
00983     l = new QLabel(i18n("Contents:"), d->m_frame );
00984     grid->addWidget(l, curRow, 0);
00985 
00986     l = new QLabel(magicMimeComment, d->m_frame );
00987     grid->addWidget(l, curRow++, 2);
00988   }
00989 
00990   if ( !directory.isEmpty() )
00991   {
00992     l = new QLabel( i18n("Location:"), d->m_frame );
00993     grid->addWidget(l, curRow, 0);
00994 
00995     l = new KSqueezedTextLabel( directory, d->m_frame );
00996     l->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard);
00997     grid->addWidget(l, curRow++, 2);
00998   }
00999 
01000   l = new QLabel(i18n("Size:"), d->m_frame );
01001   grid->addWidget(l, curRow, 0);
01002 
01003   d->m_sizeLabel = new QLabel( d->m_frame );
01004   grid->addWidget( d->m_sizeLabel, curRow++, 2 );
01005 
01006   if ( !hasDirs ) // Only files [and symlinks]
01007   {
01008     d->m_sizeLabel->setText(QString::fromLatin1("%1 (%2)").arg(KIO::convertSize(totalSize))
01009              .arg(KGlobal::locale()->formatNumber(totalSize, 0)));
01010     d->m_sizeDetermineButton = 0L;
01011     d->m_sizeStopButton = 0L;
01012   }
01013   else // Directory
01014   {
01015     QHBoxLayout * sizelay = new QHBoxLayout();
01016     sizelay->setSpacing(KDialog::spacingHint());
01017     grid->addLayout( sizelay, curRow++, 2 );
01018 
01019     // buttons
01020     d->m_sizeDetermineButton = new QPushButton( i18n("Calculate"), d->m_frame );
01021     d->m_sizeStopButton = new QPushButton( i18n("Stop"), d->m_frame );
01022     connect( d->m_sizeDetermineButton, SIGNAL( clicked() ), this, SLOT( slotSizeDetermine() ) );
01023     connect( d->m_sizeStopButton, SIGNAL( clicked() ), this, SLOT( slotSizeStop() ) );
01024     sizelay->addWidget(d->m_sizeDetermineButton, 0);
01025     sizelay->addWidget(d->m_sizeStopButton, 0);
01026     sizelay->addStretch(10); // so that the buttons don't grow horizontally
01027 
01028     // auto-launch for local dirs only, and not for '/'
01029     if ( isLocal && !hasRoot )
01030     {
01031       d->m_sizeDetermineButton->setText( i18n("Refresh") );
01032       slotSizeDetermine();
01033     }
01034     else
01035       d->m_sizeStopButton->setEnabled( false );
01036   }
01037 
01038   if (!d->bMultiple && item.isLink()) {
01039     l = new QLabel(i18n("Points to:"), d->m_frame );
01040     grid->addWidget(l, curRow, 0);
01041 
01042     l = new KSqueezedTextLabel(item.linkDest(), d->m_frame );
01043     l->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard);
01044     grid->addWidget(l, curRow++, 2);
01045   }
01046 
01047   if (!d->bMultiple) // Dates for multiple don't make much sense...
01048   {
01049     KDateTime dt = item.time(KFileItem::CreationTime);
01050     if ( !dt.isNull() )
01051     {
01052       l = new QLabel(i18n("Created:"), d->m_frame );
01053       grid->addWidget(l, curRow, 0);
01054 
01055       l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
01056       grid->addWidget(l, curRow++, 2);
01057     }
01058 
01059     dt = item.time(KFileItem::ModificationTime);
01060     if ( !dt.isNull() )
01061     {
01062       l = new QLabel(i18n("Modified:"), d->m_frame );
01063       grid->addWidget(l, curRow, 0);
01064 
01065       l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
01066       grid->addWidget(l, curRow++, 2);
01067     }
01068 
01069     dt = item.time(KFileItem::AccessTime);
01070     if ( !dt.isNull() )
01071     {
01072       l = new QLabel(i18n("Accessed:"), d->m_frame );
01073       grid->addWidget(l, curRow, 0);
01074 
01075       l = new QLabel(KGlobal::locale()->formatDateTime(dt), d->m_frame );
01076       grid->addWidget(l, curRow++, 2);
01077     }
01078   }
01079 
01080   if ( isLocal && hasDirs )  // only for directories
01081   {
01082     sep = new KSeparator( Qt::Horizontal, d->m_frame);
01083     grid->addWidget(sep, curRow, 0, 1, 3);
01084     ++curRow;
01085 
01086     KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath( url.path() );
01087     if (mp) {
01088       if (mp->mountPoint() != "/")
01089       {
01090           l = new QLabel(i18n("Mounted on:"), d->m_frame );
01091           grid->addWidget(l, curRow, 0);
01092 
01093           l = new KSqueezedTextLabel( mp->mountPoint(), d->m_frame );
01094           l->setTextInteractionFlags(Qt::TextSelectableByMouse|Qt::TextSelectableByKeyboard);
01095           grid->addWidget( l, curRow++, 2 );
01096       }
01097 
01098       l = new QLabel(i18n("Free disk space:"), d->m_frame );
01099       grid->addWidget(l, curRow, 0);
01100 
01101       d->m_freeSpaceLabel = new QLabel( d->m_frame );
01102       grid->addWidget( d->m_freeSpaceLabel, curRow++, 2 );
01103 
01104       KDiskFreeSpace * job = new KDiskFreeSpace;
01105       connect(job, SIGNAL(foundMountPoint(QString, quint64, quint64, quint64)),
01106               this, SLOT(slotFoundMountPoint(QString, quint64, quint64, quint64)));
01107       job->readDF( mp->mountPoint() );
01108     }
01109   }
01110 
01111   vbl->addStretch(1);
01112 }
01113 
01114 // QString KFilePropsPlugin::tabName () const
01115 // {
01116 //   return i18n ("&General");
01117 // }
01118 
01119 void KFilePropsPlugin::setFileNameReadOnly( bool ro )
01120 {
01121   if ( d->m_lined )
01122   {
01123     d->m_lined->setReadOnly( ro );
01124     if (ro)
01125     {
01126        // Don't put the initial focus on the line edit when it is ro
01127        properties->setButtonFocus(KDialog::Ok);
01128     }
01129   }
01130 }
01131 
01132 void KFilePropsPlugin::slotEditFileType()
01133 {
01134 #ifdef Q_WS_X11
01135   QString mime;
01136   if ( d->mimeType == KMimeType::defaultMimeType() ) {
01137     int pos = d->oldFileName.lastIndexOf( '.' );
01138     if ( pos != -1 )
01139     mime = '*' + d->oldFileName.mid(pos);
01140     else
01141     mime = '*';
01142   }
01143   else
01144     mime = d->mimeType;
01145     //TODO: wrap for win32 or mac?
01146   QString keditfiletype = QString::fromLatin1("keditfiletype");
01147   KRun::runCommand( keditfiletype
01148                     + " --parent " + QString::number( (ulong)properties->topLevelWidget()->winId())
01149                     + ' ' + KShell::quoteArg(mime),
01150                     keditfiletype, keditfiletype /*unused*/, properties->topLevelWidget());
01151 #endif
01152 }
01153 
01154 void KFilePropsPlugin::slotIconChanged()
01155 {
01156   d->bIconChanged = true;
01157   emit changed();
01158 }
01159 
01160 void KFilePropsPlugin::nameFileChanged(const QString &text )
01161 {
01162   properties->enableButtonOk(!text.isEmpty());
01163   emit changed();
01164 }
01165 
01166 void KFilePropsPlugin::determineRelativePath( const QString & path )
01167 {
01168     // now let's make it relative
01169     d->m_sRelativePath = KGlobal::dirs()->relativeLocation("apps", path);
01170     if (d->m_sRelativePath.startsWith('/'))
01171     {
01172         d->m_sRelativePath =KGlobal::dirs()->relativeLocation("xdgdata-apps", path);
01173         if (d->m_sRelativePath.startsWith('/'))
01174             d->m_sRelativePath.clear();
01175         else
01176             d->m_sRelativePath = path;
01177     }
01178 }
01179 
01180 void KFilePropsPlugin::slotFoundMountPoint( const QString&,
01181                         quint64 kibSize,
01182                         quint64 /*kibUsed*/,
01183                         quint64 kibAvail )
01184 {
01185     d->m_freeSpaceLabel->setText(
01186     i18nc("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)",
01187      KIO::convertSizeFromKiB(kibAvail),
01188      KIO::convertSizeFromKiB(kibSize),
01189       100 - (int)(100.0 * kibAvail / kibSize) ));
01190 }
01191 
01192 void KFilePropsPlugin::slotDirSizeUpdate()
01193 {
01194     KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
01195     KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles();
01196     KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs();
01197     d->m_sizeLabel->setText(
01198         i18n("Calculating... %1 (%2)\n%3, %4",
01199              KIO::convertSize(totalSize),
01200              totalSize,
01201              i18np("1 file", "%1 files", totalFiles),
01202              i18np("1 sub-folder", "%1 sub-folders", totalSubdirs)));
01203 }
01204 
01205 void KFilePropsPlugin::slotDirSizeFinished( KJob * job )
01206 {
01207   if (job->error())
01208     d->m_sizeLabel->setText( job->errorString() );
01209   else
01210   {
01211     KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
01212     KIO::filesize_t totalFiles = d->dirSizeJob->totalFiles();
01213     KIO::filesize_t totalSubdirs = d->dirSizeJob->totalSubdirs();
01214     d->m_sizeLabel->setText( QString::fromLatin1("%1 (%2)\n%3, %4")
01215               .arg(KIO::convertSize(totalSize))
01216               .arg(KGlobal::locale()->formatNumber(totalSize, 0))
01217         .arg(i18np("1 file","%1 files",totalFiles))
01218         .arg(i18np("1 sub-folder","%1 sub-folders",totalSubdirs)));
01219   }
01220   d->m_sizeStopButton->setEnabled(false);
01221   // just in case you change something and try again :)
01222   d->m_sizeDetermineButton->setText( i18n("Refresh") );
01223   d->m_sizeDetermineButton->setEnabled(true);
01224   d->dirSizeJob = 0;
01225   delete d->dirSizeUpdateTimer;
01226   d->dirSizeUpdateTimer = 0;
01227 }
01228 
01229 void KFilePropsPlugin::slotSizeDetermine()
01230 {
01231   d->m_sizeLabel->setText( i18n("Calculating...") );
01232   kDebug(250) << " KFilePropsPlugin::slotSizeDetermine() properties->item()=" <<  properties->item();
01233   kDebug(250) << " URL=" << properties->item().url().url();
01234 
01235   d->dirSizeJob = KIO::directorySize( properties->items() );
01236   d->dirSizeUpdateTimer = new QTimer(this);
01237   connect( d->dirSizeUpdateTimer, SIGNAL( timeout() ),
01238            SLOT( slotDirSizeUpdate() ) );
01239   d->dirSizeUpdateTimer->start(500);
01240   connect( d->dirSizeJob, SIGNAL( result( KJob * ) ),
01241            SLOT( slotDirSizeFinished( KJob * ) ) );
01242   d->m_sizeStopButton->setEnabled(true);
01243   d->m_sizeDetermineButton->setEnabled(false);
01244 
01245   // also update the "Free disk space" display
01246   if ( d->m_freeSpaceLabel )
01247   {
01248     bool isLocal;
01249     const KFileItem item = properties->item();
01250     KUrl url = item.mostLocalUrl( isLocal );
01251     KMountPoint::Ptr mp = KMountPoint::currentMountPoints().findByPath( url.path() );
01252     if (mp) {
01253       KDiskFreeSpace * job = new KDiskFreeSpace;
01254       connect(job, SIGNAL(foundMountPoint(QString, quint64, quint64, quint64)),
01255               this, SLOT(slotFoundMountPoint(QString, quint64, quint64, quint64)));
01256       job->readDF( mp->mountPoint() );
01257     }
01258   }
01259 }
01260 
01261 void KFilePropsPlugin::slotSizeStop()
01262 {
01263   if ( d->dirSizeJob )
01264   {
01265     KIO::filesize_t totalSize = d->dirSizeJob->totalSize();
01266     d->m_sizeLabel->setText(i18n("At least %1",
01267                            KIO::convertSize(totalSize)));
01268     d->dirSizeJob->kill();
01269     d->dirSizeJob = 0;
01270   }
01271   if ( d->dirSizeUpdateTimer )
01272     d->dirSizeUpdateTimer->stop();
01273 
01274   d->m_sizeStopButton->setEnabled(false);
01275   d->m_sizeDetermineButton->setEnabled(true);
01276 }
01277 
01278 KFilePropsPlugin::~KFilePropsPlugin()
01279 {
01280   delete d;
01281 }
01282 
01283 bool KFilePropsPlugin::supports( const KFileItemList& /*_items*/ )
01284 {
01285   return true;
01286 }
01287 
01288 void KFilePropsPlugin::applyChanges()
01289 {
01290   if ( d->dirSizeJob )
01291     slotSizeStop();
01292 
01293   kDebug(250) << "KFilePropsPlugin::applyChanges";
01294 
01295   if (qobject_cast<QLineEdit*>(d->nameArea))
01296   {
01297     QString n = ((QLineEdit *) d->nameArea)->text();
01298     // Remove trailing spaces (#4345)
01299     while ( n[n.length()-1].isSpace() )
01300       n.truncate( n.length() - 1 );
01301     if ( n.isEmpty() )
01302     {
01303       KMessageBox::sorry( properties, i18n("The new file name is empty."));
01304       properties->abortApplying();
01305       return;
01306     }
01307 
01308     // Do we need to rename the file ?
01309     kDebug(250) << "oldname = " << d->oldName;
01310     kDebug(250) << "newname = " << n;
01311     if ( d->oldName != n || d->m_bFromTemplate ) { // true for any from-template file
01312       KIO::Job * job = 0L;
01313       KUrl oldurl = properties->kurl();
01314 
01315       QString newFileName = KIO::encodeFileName(n);
01316       if (d->bDesktopFile && !newFileName.endsWith(".desktop") && !newFileName.endsWith(".kdelnk"))
01317          newFileName += ".desktop";
01318 
01319       // Tell properties. Warning, this changes the result of properties->kurl() !
01320       properties->rename( newFileName );
01321 
01322       // Update also relative path (for apps and mimetypes)
01323       if ( !d->m_sRelativePath.isEmpty() )
01324         determineRelativePath( properties->kurl().path() );
01325 
01326       kDebug(250) << "New URL = " << properties->kurl().url();
01327       kDebug(250) << "old = " << oldurl.url();
01328 
01329       // Don't remove the template !!
01330       if ( !d->m_bFromTemplate ) // (normal renaming)
01331         job = KIO::move( oldurl, properties->kurl() );
01332       else // Copying a template
01333         job = KIO::copy( oldurl, properties->kurl() );
01334 
01335       connect( job, SIGNAL( result( KJob * ) ),
01336                SLOT( slotCopyFinished( KJob * ) ) );
01337       connect( job, SIGNAL( renamed( KIO::Job *, const KUrl &, const KUrl & ) ),
01338                SLOT( slotFileRenamed( KIO::Job *, const KUrl &, const KUrl & ) ) );
01339       // wait for job
01340       QEventLoop eventLoop;
01341       connect(this, SIGNAL(leaveModality()),
01342               &eventLoop, SLOT(quit()));
01343       eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
01344       return;
01345     }
01346     properties->updateUrl(properties->kurl());
01347     // Update also relative path (for apps and mimetypes)
01348     if ( !d->m_sRelativePath.isEmpty() )
01349       determineRelativePath( properties->kurl().path() );
01350   }
01351 
01352   // No job, keep going
01353   slotCopyFinished( 0L );
01354 }
01355 
01356 void KFilePropsPlugin::slotCopyFinished( KJob * job )
01357 {
01358   kDebug(250) << "KFilePropsPlugin::slotCopyFinished";
01359   if (job)
01360   {
01361     // allow apply() to return
01362     emit leaveModality();
01363     if ( job->error() )
01364     {
01365         job->uiDelegate()->showErrorMessage();
01366         // Didn't work. Revert the URL to the old one
01367         properties->updateUrl( static_cast<KIO::CopyJob*>(job)->srcUrls().first() );
01368         properties->abortApplying(); // Don't apply the changes to the wrong file !
01369         return;
01370     }
01371   }
01372 
01373   assert( !properties->item().isNull() );
01374   assert( !properties->item().url().isEmpty() );
01375 
01376   // Save the file where we can -> usually in ~/.kde/...
01377   if (d->bDesktopFile && !d->m_sRelativePath.isEmpty())
01378   {
01379     kDebug(250) << "KFilePropsPlugin::slotCopyFinished " << d->m_sRelativePath;
01380     KUrl newURL;
01381     newURL.setPath( KDesktopFile::locateLocal(d->m_sRelativePath) );
01382     kDebug(250) << "KFilePropsPlugin::slotCopyFinished path=" << newURL.path();
01383     properties->updateUrl( newURL );
01384   }
01385 
01386   if ( d->bKDesktopMode && d->bDesktopFile ) {
01387       // Renamed? Update Name field
01388       if ( d->oldFileName != properties->kurl().fileName() || d->m_bFromTemplate ) {
01389           KDesktopFile config( properties->kurl().path() );
01390           KConfigGroup cg = config.desktopGroup();
01391           QString nameStr = nameFromFileName(properties->kurl().fileName());
01392           cg.writeEntry( "Name", nameStr );
01393           cg.writeEntry( "Name", nameStr, KConfigGroup::Persistent|KConfigGroup::Localized);
01394       }
01395   }
01396 }
01397 
01398 void KFilePropsPlugin::applyIconChanges()
01399 {
01400   KIconButton *iconButton = qobject_cast<KIconButton*>(d->iconArea);
01401   if ( !iconButton || !d->bIconChanged )
01402     return;
01403   // handle icon changes - only local files (or pseudo-local) for now
01404   // TODO: Use KTempFile and KIO::file_copy with overwrite = true
01405   KUrl url = properties->kurl();
01406   url = KIO::NetAccess::mostLocalUrl( url, properties );
01407   if ( url.isLocalFile()) {
01408     QString path;
01409 
01410     if (S_ISDIR(properties->item().mode()))
01411     {
01412       path = url.path(KUrl::AddTrailingSlash) + QString::fromLatin1(".directory");
01413       // don't call updateUrl because the other tabs (i.e. permissions)
01414       // apply to the directory, not the .directory file.
01415     }
01416     else
01417       path = url.path();
01418 
01419     // Get the default image
01420     QString str = KMimeType::findByUrl( url,
01421                                         properties->item().mode(),
01422                                         true )->iconName();
01423     // Is it another one than the default ?
01424     QString sIcon;
01425     if ( str != iconButton->icon() )
01426       sIcon = iconButton->icon();
01427     // (otherwise write empty value)
01428 
01429     kDebug(250) << "**" << path << "**";
01430     QFile f( path );
01431 
01432     // If default icon and no .directory file -> don't create one
01433     if ( !sIcon.isEmpty() || f.exists() )
01434     {
01435         if ( !f.open( QIODevice::ReadWrite ) ) {
01436           KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not "
01437                       "have sufficient access to write to <b>%1</b>.</qt>", path));
01438           return;
01439         }
01440         f.close();
01441 
01442         KDesktopFile cfg(path);
01443         kDebug(250) << "sIcon = " << (sIcon);
01444         kDebug(250) << "str = " << (str);
01445         cfg.desktopGroup().writeEntry( "Icon", sIcon );
01446         cfg.sync();
01447     }
01448   }
01449 }
01450 
01451 void KFilePropsPlugin::slotFileRenamed( KIO::Job *, const KUrl &, const KUrl & newUrl )
01452 {
01453   // This is called in case of an existing local file during the copy/move operation,
01454   // if the user chooses Rename.
01455   properties->updateUrl( newUrl );
01456 }
01457 
01458 void KFilePropsPlugin::postApplyChanges()
01459 {
01460   // Save the icon only after applying the permissions changes (#46192)
01461   applyIconChanges();
01462 
01463   const KFileItemList items = properties->items();
01464   const KUrl::List lst = items.urlList();
01465   org::kde::KDirNotify::emitFilesChanged( lst.toStringList() );
01466 }
01467 
01468 class KFilePermissionsPropsPlugin::KFilePermissionsPropsPluginPrivate
01469 {
01470 public:
01471   KFilePermissionsPropsPluginPrivate()
01472   {
01473   }
01474   ~KFilePermissionsPropsPluginPrivate()
01475   {
01476   }
01477 
01478   QFrame *m_frame;
01479   QCheckBox *cbRecursive;
01480   QLabel *explanationLabel;
01481   KComboBox *ownerPermCombo, *groupPermCombo, *othersPermCombo;
01482   QCheckBox *extraCheckbox;
01483   mode_t partialPermissions;
01484   KFilePermissionsPropsPlugin::PermissionsMode pmode;
01485   bool canChangePermissions;
01486   bool isIrregular;
01487   bool hasExtendedACL;
01488   KACL extendedACL;
01489   KACL defaultACL;
01490   bool fileSystemSupportsACLs;
01491 
01492   KComboBox *grpCombo;
01493 
01494   KLineEdit *usrEdit;
01495   KLineEdit *grpEdit;
01496 
01497   // Old permissions
01498   mode_t permissions;
01499   // Old group
01500   QString strGroup;
01501   // Old owner
01502   QString strOwner;
01503 };
01504 
01505 #define UniOwner    (S_IRUSR|S_IWUSR|S_IXUSR)
01506 #define UniGroup    (S_IRGRP|S_IWGRP|S_IXGRP)
01507 #define UniOthers   (S_IROTH|S_IWOTH|S_IXOTH)
01508 #define UniRead     (S_IRUSR|S_IRGRP|S_IROTH)
01509 #define UniWrite    (S_IWUSR|S_IWGRP|S_IWOTH)
01510 #define UniExec     (S_IXUSR|S_IXGRP|S_IXOTH)
01511 #define UniSpecial  (S_ISUID|S_ISGID|S_ISVTX)
01512 
01513 // synced with PermissionsTarget
01514 const mode_t KFilePermissionsPropsPlugin::permissionsMasks[3] = {UniOwner, UniGroup, UniOthers};
01515 const mode_t KFilePermissionsPropsPlugin::standardPermissions[4] = { 0, UniRead, UniRead|UniWrite, (mode_t)-1 };
01516 
01517 // synced with PermissionsMode and standardPermissions
01518 const char *KFilePermissionsPropsPlugin::permissionsTexts[4][4] = {
01519   { I18N_NOOP("Forbidden"),
01520     I18N_NOOP("Can Read"),
01521     I18N_NOOP("Can Read & Write"),
01522     0 },
01523   { I18N_NOOP("Forbidden"),
01524     I18N_NOOP("Can View Content"),
01525     I18N_NOOP("Can View & Modify Content"),
01526     0 },
01527   { 0, 0, 0, 0}, // no texts for links
01528   { I18N_NOOP("Forbidden"),
01529     I18N_NOOP("Can View Content & Read"),
01530     I18N_NOOP("Can View/Read & Modify/Write"),
01531     0 }
01532 };
01533 
01534 
01535 KFilePermissionsPropsPlugin::KFilePermissionsPropsPlugin( KPropertiesDialog *_props )
01536   : KPropertiesDialogPlugin( _props ),d(new KFilePermissionsPropsPluginPrivate)
01537 {
01538   d->cbRecursive = 0L;
01539   d->grpCombo = 0L; d->grpEdit = 0;
01540   d->usrEdit = 0L;
01541   QString path = properties->kurl().path(KUrl::RemoveTrailingSlash);
01542   QString fname = properties->kurl().fileName();
01543   bool isLocal = properties->kurl().isLocalFile();
01544   bool isTrash = ( properties->kurl().protocol().toLower() == "trash" );
01545   bool IamRoot = (geteuid() == 0);
01546 
01547   const KFileItem item = properties->item();
01548   bool isLink = item.isLink();
01549   bool isDir = item.isDir(); // all dirs
01550   bool hasDir = item.isDir(); // at least one dir
01551   d->permissions = item.permissions(); // common permissions to all files
01552   d->partialPermissions = d->permissions; // permissions that only some files have (at first we take everything)
01553   d->isIrregular = isIrregular(d->permissions, isDir, isLink);
01554   d->strOwner = item.user();
01555   d->strGroup = item.group();
01556   d->hasExtendedACL = item.ACL().isExtended() || item.defaultACL().isValid();
01557   d->extendedACL = item.ACL();
01558   d->defaultACL = item.defaultACL();
01559   d->fileSystemSupportsACLs = false;
01560 
01561   if ( properties->items().count() > 1 )
01562   {
01563     // Multiple items: see what they have in common
01564     const KFileItemList items = properties->items();
01565     KFileItemList::const_iterator it = items.begin();
01566     const KFileItemList::const_iterator kend = items.end();
01567     for ( ++it /*no need to check the first one again*/ ; it != kend; ++it )
01568     {
01569       const KUrl url = (*it).url();
01570       if (!d->isIrregular)
01571     d->isIrregular |= isIrregular((*it).permissions(),
01572                       (*it).isDir() == isDir,
01573                       (*it).isLink() == isLink);
01574       d->hasExtendedACL = d->hasExtendedACL || (*it).hasExtendedACL();
01575       if ( (*it).isLink() != isLink )
01576         isLink = false;
01577       if ( (*it).isDir() != isDir )
01578         isDir = false;
01579       hasDir |= (*it).isDir();
01580       if ( (*it).permissions() != d->permissions )
01581       {
01582         d->permissions &= (*it).permissions();
01583         d->partialPermissions |= (*it).permissions();
01584       }
01585       if ( (*it).user() != d->strOwner )
01586         d->strOwner.clear();
01587       if ( (*it).group() != d->strGroup )
01588         d->strGroup.clear();
01589     }
01590   }
01591 
01592   if (isLink)
01593     d->pmode = PermissionsOnlyLinks;
01594   else if (isDir)
01595     d->pmode = PermissionsOnlyDirs;
01596   else if (hasDir)
01597     d->pmode = PermissionsMixed;
01598   else
01599     d->pmode = PermissionsOnlyFiles;
01600 
01601   // keep only what's not in the common permissions
01602   d->partialPermissions = d->partialPermissions & ~d->permissions;
01603 
01604   bool isMyFile = false;
01605 
01606   if (isLocal && !d->strOwner.isEmpty()) { // local files, and all owned by the same person
01607     struct passwd *myself = getpwuid( geteuid() );
01608     if ( myself != 0L )
01609     {
01610       isMyFile = (d->strOwner == QString::fromLocal8Bit(myself->pw_name));
01611     } else
01612       kWarning() << "I don't exist ?! geteuid=" << geteuid();
01613   } else {
01614     //We don't know, for remote files, if they are ours or not.
01615     //So we let the user change permissions, and
01616     //KIO::chmod will tell, if he had no right to do it.
01617     isMyFile = true;
01618   }
01619 
01620   d->canChangePermissions = (isMyFile || IamRoot) && (!isLink);
01621 
01622 
01623   // create GUI
01624 
01625   d->m_frame = new QFrame();
01626   properties->addPage( d->m_frame, i18n("&Permissions") );
01627 
01628   QBoxLayout *box = new QVBoxLayout( d->m_frame );
01629   box->setMargin( 0 );
01630   box->setSpacing( KDialog::spacingHint() );
01631 
01632   QWidget *l;
01633   QLabel *lbl;
01634   QGroupBox *gb;
01635   QGridLayout *gl;
01636   QPushButton* pbAdvancedPerm = 0;
01637 
01638   /* Group: Access Permissions */
01639   gb = new QGroupBox ( i18n("Access Permissions"), d->m_frame );
01640   box->addWidget (gb);
01641 
01642   gl = new QGridLayout (gb);
01643   gl->setSpacing(KDialog::spacingHint());
01644   gl->setMargin(KDialog::marginHint());
01645   gl->setColumnStretch(1, 1);
01646 
01647   l = d->explanationLabel = new QLabel( "", gb );
01648   if (isLink)
01649     d->explanationLabel->setText(i18np("This file is a link and does not have permissions.",
01650                       "All files are links and do not have permissions.",
01651                       properties->items().count()));
01652   else if (!d->canChangePermissions)
01653     d->explanationLabel->setText(i18n("Only the owner can change permissions."));
01654   gl->addWidget(l, 0, 0, 1, 2);
01655 
01656   lbl = new QLabel( i18n("O&wner:"), gb);
01657   gl->addWidget(lbl, 1, 0);
01658   l = d->ownerPermCombo = new KComboBox(gb);
01659   lbl->setBuddy(l);
01660   gl->addWidget(l, 1, 1);
01661   connect(l, SIGNAL( activated(int) ), this, SIGNAL( changed() ));
01662   l->setWhatsThis(i18n("Specifies the actions that the owner is allowed to do."));
01663 
01664   lbl = new QLabel( i18n("Gro&up:"), gb);
01665   gl->addWidget(lbl, 2, 0);
01666   l = d->groupPermCombo = new KComboBox(gb);
01667   lbl->setBuddy(l);
01668   gl->addWidget(l, 2, 1);
01669   connect(l, SIGNAL( activated(int) ), this, SIGNAL( changed() ));
01670   l->setWhatsThis(i18n("Specifies the actions that the members of the group are allowed to do."));
01671 
01672   lbl = new QLabel( i18n("O&thers:"), gb);
01673   gl->addWidget(lbl, 3, 0);
01674   l = d->othersPermCombo = new KComboBox(gb);
01675   lbl->setBuddy(l);
01676   gl->addWidget(l, 3, 1);
01677   connect(l, SIGNAL( activated(int) ), this, SIGNAL( changed() ));
01678   l->setWhatsThis(i18n("Specifies the actions that all users, who are neither "
01679               "owner nor in the group, are allowed to do."));
01680 
01681   if (!isLink) {
01682     l = d->extraCheckbox = new QCheckBox(hasDir ?
01683                      i18n("Only own&er can rename and delete folder content") :
01684                      i18n("Is &executable"),
01685                      gb );
01686     connect( d->extraCheckbox, SIGNAL( clicked() ), this, SIGNAL( changed() ) );
01687     gl->addWidget(l, 4, 1);
01688     l->setWhatsThis(hasDir ? i18n("Enable this option to allow only the folder's owner to "
01689                      "delete or rename the contained files and folders. Other "
01690                      "users can only add new files, which requires the 'Modify "
01691                      "Content' permission.")
01692             : i18n("Enable this option to mark the file as executable. This only makes "
01693                "sense for programs and scripts. It is required when you want to "
01694                "execute them."));
01695 
01696     QLayoutItem *spacer = new QSpacerItem(0, 20, QSizePolicy::Minimum, QSizePolicy::Expanding);
01697     gl->addItem(spacer, 5, 0, 1, 3);
01698 
01699     pbAdvancedPerm = new QPushButton(i18n("A&dvanced Permissions"), gb);
01700     gl->addWidget(pbAdvancedPerm, 6, 0, 1, 2, Qt::AlignRight);
01701     connect(pbAdvancedPerm, SIGNAL( clicked() ), this, SLOT( slotShowAdvancedPermissions() ));
01702   }
01703   else
01704     d->extraCheckbox = 0;
01705 
01706 
01707   /**** Group: Ownership ****/
01708   gb = new QGroupBox ( i18n("Ownership"), d->m_frame );
01709   box->addWidget (gb);
01710 
01711   gl = new QGridLayout (gb);
01712   gl->setSpacing(KDialog::spacingHint());
01713   gl->setMargin(KDialog::marginHint());
01714   gl->addItem(new QSpacerItem(0, 10), 0, 0);
01715 
01716   /*** Set Owner ***/
01717   l = new QLabel( i18n("User:"), gb );
01718   gl->addWidget (l, 1, 0);
01719 
01720   /* GJ: Don't autocomplete more than 1000 users. This is a kind of random
01721    * value. Huge sites having 10.000+ user have a fair chance of using NIS,
01722    * (possibly) making this unacceptably slow.
01723    * OTOH, it is nice to offer this functionality for the standard user.
01724    */
01725   int i, maxEntries = 1000;
01726   struct passwd *user;
01727   struct group *ge;
01728 
01729   /* File owner: For root, offer a KLineEdit with autocompletion.
01730    * For a user, who can never chown() a file, offer a QLabel.
01731    */
01732   if (IamRoot && isLocal)
01733   {
01734     d->usrEdit = new KLineEdit( gb );
01735     KCompletion *kcom = d->usrEdit->completionObject();
01736     kcom->setOrder(KCompletion::Sorted);
01737     setpwent();
01738     for (i=0; ((user = getpwent()) != 0L) && (i < maxEntries); i++)
01739       kcom->addItem(QString::fromLatin1(user->pw_name));
01740     endpwent();
01741     d->usrEdit->setCompletionMode((i < maxEntries) ? KGlobalSettings::CompletionAuto :
01742                                KGlobalSettings::CompletionNone);
01743     d->usrEdit->setText(d->strOwner);
01744     gl->addWidget(d->usrEdit, 1, 1);
01745     connect( d->usrEdit, SIGNAL( textChanged( const QString & ) ),
01746              this, SIGNAL( changed() ) );
01747   }
01748   else
01749   {
01750     l = new QLabel(d->strOwner, gb);
01751     gl->addWidget(l, 1, 1);
01752   }
01753 
01754   /*** Set Group ***/
01755 
01756   QStringList groupList;
01757   QByteArray strUser;
01758   user = getpwuid(geteuid());
01759   if (user != 0L)
01760     strUser = user->pw_name;
01761 
01762 #ifdef Q_OS_UNIX
01763   setgrent();
01764   for (i=0; ((ge = getgrent()) != 0L) && (i < maxEntries); i++)
01765   {
01766     if (IamRoot)
01767       groupList += QString::fromLatin1(ge->gr_name);
01768     else
01769     {
01770       /* pick the groups to which the user belongs */
01771       char ** members = ge->gr_mem;
01772       char * member;
01773       while ((member = *members) != 0L) {
01774         if (strUser == member) {
01775           groupList += QString::fromLocal8Bit(ge->gr_name);
01776           break;
01777         }
01778         ++members;
01779       }
01780     }
01781   }
01782   endgrent();
01783 #endif //Q_OS_UNIX
01784 
01785   /* add the effective Group to the list .. */
01786   ge = getgrgid (getegid());
01787   if (ge) {
01788     QString name = QString::fromLatin1(ge->gr_name);
01789     if (name.isEmpty())
01790       name.setNum(ge->gr_gid);
01791     if (groupList.indexOf(name) == -1)
01792       groupList += name;
01793   }
01794 
01795   bool isMyGroup = groupList.contains(d->strGroup);
01796 
01797   /* add the group the file currently belongs to ..
01798    * .. if its not there already
01799    */
01800   if (!isMyGroup)
01801     groupList += d->strGroup;
01802 
01803   l = new QLabel( i18n("Group:"), gb );
01804   gl->addWidget (l, 2, 0);
01805 
01806   /* Set group: if possible to change:
01807    * - Offer a KLineEdit for root, since he can change to any group.
01808    * - Offer a KComboBox for a normal user, since he can change to a fixed
01809    *   (small) set of groups only.
01810    * If not changeable: offer a QLabel.
01811    */
01812   if (IamRoot && isLocal)
01813   {
01814     d->grpEdit = new KLineEdit(gb);
01815     KCompletion *kcom = new KCompletion;
01816     kcom->setItems(groupList);
01817     d->grpEdit->setCompletionObject(kcom, true);
01818     d->grpEdit->setAutoDeleteCompletionObject( true );
01819     d->grpEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
01820     d->grpEdit->setText(d->strGroup);
01821     gl->addWidget(d->grpEdit, 2, 1);
01822     connect( d->grpEdit, SIGNAL( textChanged( const QString & ) ),
01823              this, SIGNAL( changed() ) );
01824   }
01825   else if ((groupList.count() > 1) && isMyFile && isLocal)
01826   {
01827     d->grpCombo = new KComboBox(gb);
01828     d->grpCombo->setObjectName(QLatin1String("combogrouplist"));
01829     d->grpCombo->addItems(groupList);
01830     d->grpCombo->setCurrentIndex(groupList.indexOf(d->strGroup));
01831     gl->addWidget(d->grpCombo, 2, 1);
01832     connect( d->grpCombo, SIGNAL( activated( int ) ),
01833              this, SIGNAL( changed() ) );
01834   }
01835   else
01836   {
01837     l = new QLabel(d->strGroup, gb);
01838     gl->addWidget(l, 2, 1);
01839   }
01840 
01841   gl->setColumnStretch(2, 10);
01842 
01843   // "Apply recursive" checkbox
01844   if ( hasDir && !isLink && !isTrash  )
01845   {
01846       d->cbRecursive = new QCheckBox( i18n("Apply changes to all subfolders and their contents"), d->m_frame );
01847       connect( d->cbRecursive, SIGNAL( clicked() ), this, SIGNAL( changed() ) );
01848       box->addWidget( d->cbRecursive );
01849   }
01850 
01851   updateAccessControls();
01852 
01853 
01854   if ( isTrash )
01855   {
01856       //don't allow to change properties for file into trash
01857       enableAccessControls(false);
01858       if ( pbAdvancedPerm)
01859           pbAdvancedPerm->setEnabled(false);
01860   }
01861 
01862   box->addStretch (10);
01863 }
01864 
01865 #ifdef HAVE_POSIX_ACL
01866 static bool fileSystemSupportsACL( const QByteArray& path )
01867 {
01868     bool fileSystemSupportsACLs = false;
01869 #ifdef Q_OS_FREEBSD
01870     struct statfs buf;
01871     fileSystemSupportsACLs = ( statfs( path.data(), &buf ) == 0 ) && ( buf.f_flags & MNT_ACLS );
01872 #else
01873     fileSystemSupportsACLs =
01874       getxattr( path.data(), "system.posix_acl_access", NULL, 0 ) >= 0 || errno == ENODATA;
01875 #endif
01876     return fileSystemSupportsACLs;
01877 }
01878 #endif
01879 
01880 
01881 void KFilePermissionsPropsPlugin::slotShowAdvancedPermissions() {
01882 
01883   bool isDir = (d->pmode == PermissionsOnlyDirs) || (d->pmode == PermissionsMixed);
01884   KDialog dlg( properties );
01885   dlg.setModal( true );
01886   dlg.setCaption( i18n("Advanced Permissions") );
01887   dlg.setButtons( KDialog::Ok | KDialog::Cancel );
01888 
01889   QLabel *l, *cl[3];
01890   QGroupBox *gb;
01891   QGridLayout *gl;
01892 
01893   QWidget *mainw = new QWidget( &dlg );
01894   QVBoxLayout *vbox = new QVBoxLayout(mainw);
01895   // Group: Access Permissions
01896   gb = new QGroupBox ( i18n("Access Permissions"), mainw );
01897   vbox->addWidget(gb);
01898 
01899   gl = new QGridLayout (gb);
01900   gl->setSpacing(KDialog::spacingHint());
01901   gl->setMargin(KDialog::marginHint());
01902   gl->addItem(new QSpacerItem(0, 10), 0, 0);
01903 
01904   QVector<QWidget*> theNotSpecials;
01905 
01906   l = new QLabel(i18n("Class"), gb );
01907   gl->addWidget(l, 1, 0);
01908   theNotSpecials.append( l );
01909 
01910   if (isDir)
01911     l = new QLabel( i18n("Show\nEntries"), gb );
01912   else
01913     l = new QLabel( i18n("Read"), gb );
01914   gl->addWidget (l, 1, 1);
01915   theNotSpecials.append( l );
01916   QString readWhatsThis;
01917   if (isDir)
01918     readWhatsThis = i18n("This flag allows viewing the content of the folder.");
01919   else
01920     readWhatsThis = i18n("The Read flag allows viewing the content of the file.");
01921   l->setWhatsThis(readWhatsThis);
01922 
01923   if (isDir)
01924     l = new QLabel( i18n("Write\nEntries"), gb );
01925   else
01926     l = new QLabel( i18n("Write"), gb );
01927   gl->addWidget (l, 1, 2);
01928   theNotSpecials.append( l );
01929   QString writeWhatsThis;
01930   if (isDir)
01931     writeWhatsThis = i18n("This flag allows adding, renaming and deleting of files. "
01932               "Note that deleting and renaming can be limited using the Sticky flag.");
01933   else
01934     writeWhatsThis = i18n("The Write flag allows modifying the content of the file.");
01935   l->setWhatsThis(writeWhatsThis);
01936 
01937   QString execWhatsThis;
01938   if (isDir) {
01939     l = new QLabel( i18nc("Enter folder", "Enter"), gb );
01940     execWhatsThis = i18n("Enable this flag to allow entering the folder.");
01941   }
01942   else {
01943     l = new QLabel( i18n("Exec"), gb );
01944     execWhatsThis = i18n("Enable this flag to allow executing the file as a program.");
01945   }
01946   l->setWhatsThis(execWhatsThis);
01947   theNotSpecials.append( l );
01948   // GJ: Add space between normal and special modes
01949   QSize size = l->sizeHint();
01950   size.setWidth(size.width() + 15);
01951   l->setFixedSize(size);
01952   gl->addWidget (l, 1, 3);
01953 
01954   l = new QLabel( i18n("Special"), gb );
01955   gl->addWidget(l, 1, 4, 1, 2);
01956   QString specialWhatsThis;
01957   if (isDir)
01958     specialWhatsThis = i18n("Special flag. Valid for the whole folder, the exact "
01959                 "meaning of the flag can be seen in the right hand column.");
01960   else
01961     specialWhatsThis = i18n("Special flag. The exact meaning of the flag can be seen "
01962                 "in the right hand column.");
01963   l->setWhatsThis(specialWhatsThis);
01964 
01965   cl[0] = new QLabel( i18n("User"), gb );
01966   gl->addWidget (cl[0], 2, 0);
01967   theNotSpecials.append( cl[0] );
01968 
01969   cl[1] = new QLabel( i18n("Group"), gb );
01970   gl->addWidget (cl[1], 3, 0);
01971   theNotSpecials.append( cl[1] );
01972 
01973   cl[2] = new QLabel( i18n("Others"), gb );
01974   gl->addWidget (cl[2], 4, 0);
01975   theNotSpecials.append( cl[2] );
01976 
01977   l = new QLabel(i18n("Set UID"), gb);
01978   gl->addWidget(l, 2, 5);
01979   QString setUidWhatsThis;
01980   if (isDir)
01981     setUidWhatsThis = i18n("If this flag is set, the owner of this folder will be "
01982                "the owner of all new files.");
01983   else
01984     setUidWhatsThis = i18n("If this file is an executable and the flag is set, it will "
01985                "be executed with the permissions of the owner.");
01986   l->setWhatsThis(setUidWhatsThis);
01987 
01988   l = new QLabel(i18n("Set GID"), gb);
01989   gl->addWidget(l, 3, 5);
01990   QString setGidWhatsThis;
01991   if (isDir)
01992     setGidWhatsThis = i18n("If this flag is set, the group of this folder will be "
01993                "set for all new files.");
01994   else
01995     setGidWhatsThis = i18n("If this file is an executable and the flag is set, it will "
01996                "be executed with the permissions of the group.");
01997   l->setWhatsThis(setGidWhatsThis);
01998 
01999   l = new QLabel(i18nc("File permission", "Sticky"), gb);
02000   gl->addWidget(l, 4, 5);
02001   QString stickyWhatsThis;
02002   if (isDir)
02003     stickyWhatsThis = i18n("If the Sticky flag is set on a folder, only the owner "
02004                "and root can delete or rename files. Otherwise everybody "
02005                "with write permissions can do this.");
02006   else
02007     stickyWhatsThis = i18n("The Sticky flag on a file is ignored on Linux, but may "
02008                "be used on some systems");
02009   l->setWhatsThis(stickyWhatsThis);
02010 
02011   mode_t aPermissions, aPartialPermissions;
02012   mode_t dummy1, dummy2;
02013 
02014   if (!d->isIrregular) {
02015     switch (d->pmode) {
02016     case PermissionsOnlyFiles:
02017       getPermissionMasks(aPartialPermissions,
02018              dummy1,
02019              aPermissions,
02020              dummy2);
02021       break;
02022     case PermissionsOnlyDirs:
02023     case PermissionsMixed:
02024       getPermissionMasks(dummy1,
02025              aPartialPermissions,
02026              dummy2,
02027              aPermissions);
02028       break;
02029     case PermissionsOnlyLinks:
02030       aPermissions = UniRead | UniWrite | UniExec | UniSpecial;
02031       aPartialPermissions = 0;
02032       break;
02033     }
02034   }
02035   else {
02036     aPermissions = d->permissions;
02037     aPartialPermissions = d->partialPermissions;
02038   }
02039 
02040   // Draw Checkboxes
02041   QCheckBox *cba[3][4];
02042   for (int row = 0; row < 3 ; ++row) {
02043     for (int col = 0; col < 4; ++col) {
02044       QCheckBox *cb = new QCheckBox(gb);
02045       if ( col != 3 ) theNotSpecials.append( cb );
02046       cba[row][col] = cb;
02047       cb->setChecked(aPermissions & fperm[row][col]);
02048       if ( aPartialPermissions & fperm[row][col] )
02049       {
02050         cb->setTristate();
02051         cb->setCheckState(Qt::PartiallyChecked);
02052       }
02053       else if (d->cbRecursive && d->cbRecursive->isChecked())
02054     cb->setTristate();
02055 
02056       cb->setEnabled( d->canChangePermissions );
02057       gl->addWidget (cb, row+2, col+1);
02058       switch(col) {
02059       case 0:
02060     cb->setWhatsThis(readWhatsThis);
02061     break;
02062       case 1:
02063     cb->setWhatsThis(writeWhatsThis);
02064     break;
02065       case 2:
02066     cb->setWhatsThis(execWhatsThis);
02067     break;
02068       case 3:
02069     switch(row) {
02070     case 0:
02071       cb->setWhatsThis(setUidWhatsThis);
02072       break;
02073     case 1:
02074       cb->setWhatsThis(setGidWhatsThis);
02075       break;
02076     case 2:
02077       cb->setWhatsThis(stickyWhatsThis);
02078       break;
02079     }
02080     break;
02081       }
02082     }
02083   }
02084   gl->setColumnStretch(6, 10);
02085 
02086 #ifdef HAVE_POSIX_ACL
02087   KACLEditWidget *extendedACLs = 0;
02088 
02089   // FIXME make it work with partial entries
02090   if ( properties->items().count() == 1 ) {
02091     QByteArray path = QFile::encodeName( properties->item().url().path() );
02092     d->fileSystemSupportsACLs = fileSystemSupportsACL( path );
02093   }
02094   if ( d->fileSystemSupportsACLs  ) {
02095     std::for_each( theNotSpecials.begin(), theNotSpecials.end(), std::mem_fun( &QWidget::hide ) );
02096     extendedACLs = new KACLEditWidget( mainw );
02097     vbox->addWidget(extendedACLs);
02098     if ( d->extendedACL.isValid() && d->extendedACL.isExtended() )
02099       extendedACLs->setACL( d->extendedACL );
02100     else
02101       extendedACLs->setACL( KACL( aPermissions ) );
02102 
02103     if ( d->defaultACL.isValid() )
02104       extendedACLs->setDefaultACL( d->defaultACL );
02105 
02106     if ( properties->items().first().isDir() )
02107       extendedACLs->setAllowDefaults( true );
02108   }
02109 #endif
02110   dlg.setMainWidget( mainw );
02111   if (dlg.exec() != KDialog::Accepted)
02112     return;
02113 
02114   mode_t andPermissions = mode_t(~0);
02115   mode_t orPermissions = 0;
02116   for (int row = 0; row < 3; ++row)
02117     for (int col = 0; col < 4; ++col) {
02118       switch (cba[row][col]->checkState())
02119       {
02120       case Qt::Checked:
02121         orPermissions |= fperm[row][col];
02122     //fall through
02123       case Qt::Unchecked:
02124         andPermissions &= ~fperm[row][col];
02125         break;
02126       default: // NoChange
02127         break;
02128       }
02129     }
02130 
02131   d->isIrregular = false;
02132   const KFileItemList items = properties->items();
02133   KFileItemList::const_iterator it = items.begin();
02134   const KFileItemList::const_iterator kend = items.end();
02135   for ( ; it != kend; ++it ) {
02136     if (isIrregular(((*it).permissions() & andPermissions) | orPermissions,
02137             (*it).isDir(), (*it).isLink())) {
02138       d->isIrregular = true;
02139       break;
02140     }
02141   }
02142 
02143   d->permissions = orPermissions;
02144   d->partialPermissions = andPermissions;
02145 
02146 #ifdef HAVE_POSIX_ACL
02147   // override with the acls, if present
02148   if ( extendedACLs ) {
02149     d->extendedACL = extendedACLs->getACL();
02150     d->defaultACL = extendedACLs->getDefaultACL();
02151     d->hasExtendedACL = d->extendedACL.isExtended() || d->defaultACL.isValid();
02152     d->permissions = d->extendedACL.basePermissions();
02153     d->permissions |= ( andPermissions | orPermissions ) & ( S_ISUID|S_ISGID|S_ISVTX );
02154   }
02155 #endif
02156 
02157   updateAccessControls();
02158   emit changed();
02159 }
02160 
02161 // QString KFilePermissionsPropsPlugin::tabName () const
02162 // {
02163 //   return i18n ("&Permissions");
02164 // }
02165 
02166 KFilePermissionsPropsPlugin::~KFilePermissionsPropsPlugin()
02167 {
02168   delete d;
02169 }
02170 
02171 bool KFilePermissionsPropsPlugin::supports( const KFileItemList& /*_items*/ )
02172 {
02173   return true;
02174 }
02175 
02176 // sets a combo box in the Access Control frame
02177 void KFilePermissionsPropsPlugin::setComboContent(QComboBox *combo, PermissionsTarget target,
02178                           mode_t permissions, mode_t partial) {
02179   combo->clear();
02180   if (d->pmode == PermissionsOnlyLinks) {
02181     combo->addItem(i18n("Link"));
02182     combo->setCurrentIndex(0);
02183     return;
02184   }
02185 
02186   mode_t tMask = permissionsMasks[target];
02187   int textIndex;
02188   for (textIndex = 0; standardPermissions[textIndex] != (mode_t)-1; textIndex++)
02189     if ((standardPermissions[textIndex]&tMask) == (permissions&tMask&(UniRead|UniWrite)))
02190       break;
02191   Q_ASSERT(standardPermissions[textIndex] != (mode_t)-1); // must not happen, would be irreglar
02192 
02193   for (int i = 0; permissionsTexts[(int)d->pmode][i]; i++)
02194     combo->addItem(i18n(permissionsTexts[(int)d->pmode][i]));
02195 
02196   if (partial & tMask & ~UniExec) {
02197     combo->addItem(i18n("Varying (No Change)"));
02198     combo->setCurrentIndex(3);
02199   }
02200   else
02201     combo->setCurrentIndex(textIndex);
02202 }
02203 
02204 // permissions are irregular if they cant be displayed in a combo box.
02205 bool KFilePermissionsPropsPlugin::isIrregular(mode_t permissions, bool isDir, bool isLink) {
02206   if (isLink)                             // links are always ok
02207     return false;
02208 
02209   mode_t p = permissions;
02210   if (p & (S_ISUID | S_ISGID))  // setuid/setgid -> irregular
02211     return true;
02212   if (isDir) {
02213     p &= ~S_ISVTX;          // ignore sticky on dirs
02214 
02215     // check supported flag combinations
02216     mode_t p0 = p & UniOwner;
02217     if ((p0 != 0) && (p0 != (S_IRUSR | S_IXUSR)) && (p0 != UniOwner))
02218       return true;
02219     p0 = p & UniGroup;
02220     if ((p0 != 0) && (p0 != (S_IRGRP | S_IXGRP)) && (p0 != UniGroup))
02221       return true;
02222     p0 = p & UniOthers;
02223     if ((p0 != 0) && (p0 != (S_IROTH | S_IXOTH)) && (p0 != UniOthers))
02224       return true;
02225     return false;
02226   }
02227   if (p & S_ISVTX) // sticky on file -> irregular
02228     return true;
02229 
02230   // check supported flag combinations
02231   mode_t p0 = p & UniOwner;
02232   bool usrXPossible = !p0; // true if this file could be an executable
02233   if (p0 & S_IXUSR) {
02234     if ((p0 == S_IXUSR) || (p0 == (S_IWUSR | S_IXUSR)))
02235       return true;
02236     usrXPossible = true;
02237   }
02238   else if (p0 == S_IWUSR)
02239     return true;
02240 
02241   p0 = p & UniGroup;
02242   bool grpXPossible = !p0; // true if this file could be an executable
02243   if (p0 & S_IXGRP) {
02244     if ((p0 == S_IXGRP) || (p0 == (S_IWGRP | S_IXGRP)))
02245       return true;
02246     grpXPossible = true;
02247   }
02248   else if (p0 == S_IWGRP)
02249     return true;
02250   if (p0 == 0)
02251     grpXPossible = true;
02252 
02253   p0 = p & UniOthers;
02254   bool othXPossible = !p0; // true if this file could be an executable
02255   if (p0 & S_IXOTH) {
02256     if ((p0 == S_IXOTH) || (p0 == (S_IWOTH | S_IXOTH)))
02257       return true;
02258     othXPossible = true;
02259   }
02260   else if (p0 == S_IWOTH)
02261     return true;
02262 
02263   // check that there either all targets are executable-compatible, or none
02264   return (p & UniExec) && !(usrXPossible && grpXPossible && othXPossible);
02265 }
02266 
02267 // enables/disabled the widgets in the Access Control frame
02268 void KFilePermissionsPropsPlugin::enableAccessControls(bool enable) {
02269     d->ownerPermCombo->setEnabled(enable);
02270     d->groupPermCombo->setEnabled(enable);
02271     d->othersPermCombo->setEnabled(enable);
02272     if (d->extraCheckbox)
02273       d->extraCheckbox->setEnabled(enable);
02274         if ( d->cbRecursive )
02275             d->cbRecursive->setEnabled(enable);
02276 }
02277 
02278 // updates all widgets in the Access Control frame
02279 void KFilePermissionsPropsPlugin::updateAccessControls() {
02280   setComboContent(d->ownerPermCombo, PermissionsOwner,
02281           d->permissions, d->partialPermissions);
02282   setComboContent(d->groupPermCombo, PermissionsGroup,
02283           d->permissions, d->partialPermissions);
02284   setComboContent(d->othersPermCombo, PermissionsOthers,
02285           d->permissions, d->partialPermissions);
02286 
02287   switch(d->pmode) {
02288   case PermissionsOnlyLinks:
02289     enableAccessControls(false);
02290     break;
02291   case PermissionsOnlyFiles:
02292     enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
02293     if (d->canChangePermissions)
02294       d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
02295                    i18np("This file uses advanced permissions",
02296                       "These files use advanced permissions.",
02297                       properties->items().count()) : "");
02298     if (d->partialPermissions & UniExec) {
02299       d->extraCheckbox->setTristate();
02300       d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
02301     }
02302     else {
02303       d->extraCheckbox->setTristate(false);
02304       d->extraCheckbox->setChecked(d->permissions & UniExec);
02305     }
02306     break;
02307   case PermissionsOnlyDirs:
02308     enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
02309     // if this is a dir, and we can change permissions, don't dis-allow
02310     // recursive, we can do that for ACL setting.
02311     if ( d->cbRecursive )
02312        d->cbRecursive->setEnabled( d->canChangePermissions && !d->isIrregular );
02313 
02314     if (d->canChangePermissions)
02315       d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
02316                    i18np("This folder uses advanced permissions.",
02317                       "These folders use advanced permissions.",
02318                       properties->items().count()) : "");
02319     if (d->partialPermissions & S_ISVTX) {
02320       d->extraCheckbox->setTristate();
02321       d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
02322     }
02323     else {
02324       d->extraCheckbox->setTristate(false);
02325       d->extraCheckbox->setChecked(d->permissions & S_ISVTX);
02326     }
02327     break;
02328   case PermissionsMixed:
02329     enableAccessControls(d->canChangePermissions && !d->isIrregular && !d->hasExtendedACL);
02330     if (d->canChangePermissions)
02331       d->explanationLabel->setText(d->isIrregular || d->hasExtendedACL ?
02332                    i18n("These files use advanced permissions.") : "");
02333     break;
02334     if (d->partialPermissions & S_ISVTX) {
02335       d->extraCheckbox->setTristate();
02336       d->extraCheckbox->setCheckState(Qt::PartiallyChecked);
02337     }
02338     else {
02339       d->extraCheckbox->setTristate(false);
02340       d->extraCheckbox->setChecked(d->permissions & S_ISVTX);
02341     }
02342     break;
02343   }
02344 }
02345 
02346 // gets masks for files and dirs from the Access Control frame widgets
02347 void KFilePermissionsPropsPlugin::getPermissionMasks(mode_t &andFilePermissions,
02348                              mode_t &andDirPermissions,
02349                              mode_t &orFilePermissions,
02350                              mode_t &orDirPermissions) {
02351   andFilePermissions = mode_t(~UniSpecial);
02352   andDirPermissions = mode_t(~(S_ISUID|S_ISGID));
02353   orFilePermissions = 0;
02354   orDirPermissions = 0;
02355   if (d->isIrregular)
02356     return;
02357 
02358   mode_t m = standardPermissions[d->ownerPermCombo->currentIndex()];
02359   if (m != (mode_t) -1) {
02360     orFilePermissions |= m & UniOwner;
02361     if ((m & UniOwner) &&
02362     ((d->pmode == PermissionsMixed) ||
02363      ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked))))
02364       andFilePermissions &= ~(S_IRUSR | S_IWUSR);
02365     else {
02366       andFilePermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
02367       if ((m & S_IRUSR) && (d->extraCheckbox->checkState() == Qt::Checked))
02368     orFilePermissions |= S_IXUSR;
02369     }
02370 
02371     orDirPermissions |= m & UniOwner;
02372     if (m & S_IRUSR)
02373     orDirPermissions |= S_IXUSR;
02374     andDirPermissions &= ~(S_IRUSR | S_IWUSR | S_IXUSR);
02375   }
02376 
02377   m = standardPermissions[d->groupPermCombo->currentIndex()];
02378   if (m != (mode_t) -1) {
02379     orFilePermissions |= m & UniGroup;
02380     if ((m & UniGroup) &&
02381     ((d->pmode == PermissionsMixed) ||
02382      ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked))))
02383       andFilePermissions &= ~(S_IRGRP | S_IWGRP);
02384     else {
02385       andFilePermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
02386       if ((m & S_IRGRP) && (d->extraCheckbox->checkState() == Qt::Checked))
02387     orFilePermissions |= S_IXGRP;
02388     }
02389 
02390     orDirPermissions |= m & UniGroup;
02391     if (m & S_IRGRP)
02392     orDirPermissions |= S_IXGRP;
02393     andDirPermissions &= ~(S_IRGRP | S_IWGRP | S_IXGRP);
02394   }
02395 
02396   m = d->othersPermCombo->currentIndex() >= 0 ? standardPermissions[d->othersPermCombo->currentIndex()] : (mode_t)-1;
02397   if (m != (mode_t) -1) {
02398     orFilePermissions |= m & UniOthers;
02399     if ((m & UniOthers) &&
02400     ((d->pmode == PermissionsMixed) ||
02401      ((d->pmode == PermissionsOnlyFiles) && (d->extraCheckbox->checkState() == Qt::PartiallyChecked))))
02402       andFilePermissions &= ~(S_IROTH | S_IWOTH);
02403     else {
02404       andFilePermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH);
02405       if ((m & S_IROTH) && (d->extraCheckbox->checkState() == Qt::Checked))
02406     orFilePermissions |= S_IXOTH;
02407     }
02408 
02409     orDirPermissions |= m & UniOthers;
02410     if (m & S_IROTH)
02411     orDirPermissions |= S_IXOTH;
02412     andDirPermissions &= ~(S_IROTH | S_IWOTH | S_IXOTH);
02413   }
02414 
02415   if (((d->pmode == PermissionsMixed) || (d->pmode == PermissionsOnlyDirs)) &&
02416       (d->extraCheckbox->checkState() != Qt::PartiallyChecked)) {
02417     andDirPermissions &= ~S_ISVTX;
02418     if (d->extraCheckbox->checkState() == Qt::Checked)
02419       orDirPermissions |= S_ISVTX;
02420   }
02421 }
02422 
02423 void KFilePermissionsPropsPlugin::applyChanges()
02424 {
02425   mode_t orFilePermissions;
02426   mode_t orDirPermissions;
02427   mode_t andFilePermissions;
02428   mode_t andDirPermissions;
02429 
02430   if (!d->canChangePermissions)
02431     return;
02432 
02433   if (!d->isIrregular)
02434     getPermissionMasks(andFilePermissions,
02435                andDirPermissions,
02436                orFilePermissions,
02437                orDirPermissions);
02438   else {
02439     orFilePermissions = d->permissions;
02440     andFilePermissions = d->partialPermissions;
02441     orDirPermissions = d->permissions;
02442     andDirPermissions = d->partialPermissions;
02443   }
02444 
02445   QString owner, group;
02446   if (d->usrEdit)
02447     owner = d->usrEdit->text();
02448   if (d->grpEdit)
02449     group = d->grpEdit->text();
02450   else if (d->grpCombo)
02451     group = d->grpCombo->currentText();
02452 
02453   if (owner == d->strOwner)
02454       owner.clear(); // no change
02455 
02456   if (group == d->strGroup)
02457       group.clear();
02458 
02459   bool recursive = d->cbRecursive && d->cbRecursive->isChecked();
02460   bool permissionChange = false;
02461 
02462   KFileItemList files, dirs;
02463   const KFileItemList items = properties->items();
02464   KFileItemList::const_iterator it = items.begin();
02465   const KFileItemList::const_iterator kend = items.end();
02466   for ( ; it != kend; ++it ) {
02467     if ((*it).isDir()) {
02468       dirs.append(*it);
02469       if ((*it).permissions() != (((*it).permissions() & andDirPermissions) | orDirPermissions))
02470     permissionChange = true;
02471     }
02472     else if ((*it).isFile()) {
02473       files.append(*it);
02474       if ((*it).permissions() != (((*it).permissions() & andFilePermissions) | orFilePermissions))
02475     permissionChange = true;
02476     }
02477   }
02478 
02479   const bool ACLChange = ( d->extendedACL !=  properties->item().ACL() );
02480   const bool defaultACLChange = ( d->defaultACL != properties->item().defaultACL() );
02481 
02482   if ( owner.isEmpty() && group.isEmpty() && !recursive
02483       && !permissionChange && !ACLChange && !defaultACLChange )
02484     return;
02485 
02486     KIO::Job * job;
02487     if (files.count() > 0) {
02488       job = KIO::chmod( files, orFilePermissions, ~andFilePermissions,
02489             owner, group, false );
02490     if ( ACLChange && d->fileSystemSupportsACLs )
02491       job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" );
02492     if ( defaultACLChange && d->fileSystemSupportsACLs )
02493       job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" );
02494 
02495       connect( job, SIGNAL( result( KJob * ) ),
02496            SLOT( slotChmodResult( KJob * ) ) );
02497       QEventLoop eventLoop;
02498       connect(this, SIGNAL(leaveModality()),
02499               &eventLoop, SLOT(quit()));
02500       eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
02501     }
02502     if (dirs.count() > 0) {
02503       job = KIO::chmod( dirs, orDirPermissions, ~andDirPermissions,
02504             owner, group, recursive );
02505     if ( ACLChange && d->fileSystemSupportsACLs )
02506       job->addMetaData( "ACL_STRING", d->extendedACL.isValid()?d->extendedACL.asString():"ACL_DELETE" );
02507     if ( defaultACLChange && d->fileSystemSupportsACLs )
02508       job->addMetaData( "DEFAULT_ACL_STRING", d->defaultACL.isValid()?d->defaultACL.asString():"ACL_DELETE" );
02509 
02510       connect( job, SIGNAL( result( KJob * ) ),
02511            SLOT( slotChmodResult( KJob * ) ) );
02512       QEventLoop eventLoop;
02513       connect(this, SIGNAL(leaveModality()),
02514               &eventLoop, SLOT(quit()));
02515       eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
02516     }
02517 }
02518 
02519 void KFilePermissionsPropsPlugin::slotChmodResult( KJob * job )
02520 {
02521   kDebug(250) << "KFilePermissionsPropsPlugin::slotChmodResult";
02522   if (job->error())
02523       job->uiDelegate()->showErrorMessage();
02524   // allow apply() to return
02525   emit leaveModality();
02526 }
02527 
02528 
02529 
02530 
02531 class KUrlPropsPlugin::KUrlPropsPluginPrivate
02532 {
02533 public:
02534   KUrlPropsPluginPrivate()
02535   {
02536   }
02537   ~KUrlPropsPluginPrivate()
02538   {
02539   }
02540 
02541   QFrame *m_frame;
02542   KUrlRequester *URLEdit;
02543   QString URLStr;
02544 };
02545 
02546 KUrlPropsPlugin::KUrlPropsPlugin( KPropertiesDialog *_props )
02547   : KPropertiesDialogPlugin( _props ),d(new KUrlPropsPluginPrivate)
02548 {
02549     d->m_frame = new QFrame();
02550     properties->addPage(d->m_frame, i18n("U&RL"));
02551     QVBoxLayout *layout = new QVBoxLayout(d->m_frame);
02552     layout->setMargin(0);
02553     layout->setSpacing(KDialog::spacingHint());
02554 
02555     QLabel *l;
02556     l = new QLabel( d->m_frame );
02557     l->setObjectName( QLatin1String( "Label_1" ) );
02558     l->setText( i18n("URL:") );
02559     layout->addWidget(l);
02560 
02561     d->URLEdit = new KUrlRequester( d->m_frame );
02562     layout->addWidget(d->URLEdit);
02563 
02564     KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
02565     if (url.isLocalFile()) {
02566         QString path = url.path();
02567 
02568         QFile f( path );
02569         if ( !f.open( QIODevice::ReadOnly ) ) {
02570             return;
02571         }
02572         f.close();
02573 
02574         KDesktopFile config( path );
02575         const KConfigGroup dg = config.desktopGroup();
02576         d->URLStr = dg.readPathEntry( "URL", QString() );
02577 
02578         if (!d->URLStr.isEmpty()) {
02579             d->URLEdit->setUrl( KUrl(d->URLStr) );
02580         }
02581     }
02582 
02583     connect( d->URLEdit, SIGNAL( textChanged( const QString & ) ),
02584             this, SIGNAL( changed() ) );
02585 
02586     layout->addStretch (1);
02587 }
02588 
02589 KUrlPropsPlugin::~KUrlPropsPlugin()
02590 {
02591   delete d;
02592 }
02593 
02594 // QString KUrlPropsPlugin::tabName () const
02595 // {
02596 //   return i18n ("U&RL");
02597 // }
02598 
02599 bool KUrlPropsPlugin::supports( const KFileItemList& _items )
02600 {
02601   if ( _items.count() != 1 )
02602     return false;
02603   const KFileItem item = _items.first();
02604   // check if desktop file
02605   if (!item.isDesktopFile())
02606     return false;
02607 
02608   // open file and check type
02609   bool isLocal;
02610   KUrl url = item.mostLocalUrl(isLocal);
02611   if (!isLocal) {
02612       return false;
02613   }
02614 
02615   KDesktopFile config( url.path() );
02616   return config.hasLinkType();
02617 }
02618 
02619 void KUrlPropsPlugin::applyChanges()
02620 {
02621   KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
02622   if (!url.isLocalFile()) {
02623     //FIXME: 4.2 add this: KMessageBox::sorry(0, i18n("Could not save properties. Only entries on local file systems are supported."));
02624     return;
02625   }
02626 
02627   QString path = url.path();
02628 
02629   QFile f( path );
02630   if ( !f.open( QIODevice::ReadWrite ) ) {
02631     KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
02632                 "sufficient access to write to <b>%1</b>.</qt>", path));
02633     return;
02634   }
02635   f.close();
02636 
02637   KDesktopFile config( path );
02638   KConfigGroup dg = config.desktopGroup();
02639   dg.writeEntry( "Type", QString::fromLatin1("Link"));
02640   dg.writePathEntry( "URL", d->URLEdit->url().url() );
02641   // Users can't create a Link .desktop file with a Name field,
02642   // but distributions can. Update the Name field in that case.
02643   if ( dg.hasKey("Name") )
02644   {
02645     QString nameStr = nameFromFileName(properties->kurl().fileName());
02646     dg.writeEntry( "Name", nameStr );
02647     dg.writeEntry( "Name", nameStr, KConfigBase::Persistent|KConfigBase::Localized );
02648 
02649   }
02650 }
02651 
02652 
02653 /* ----------------------------------------------------
02654  *
02655  * KDevicePropsPlugin
02656  *
02657  * -------------------------------------------------- */
02658 
02659 class KDevicePropsPlugin::KDevicePropsPluginPrivate
02660 {
02661 public:
02662   KDevicePropsPluginPrivate()
02663   {
02664   }
02665   ~KDevicePropsPluginPrivate()
02666   {
02667   }
02668 
02669   QFrame *m_frame;
02670   QStringList mountpointlist;
02671   QLabel *m_freeSpaceText;
02672   QLabel *m_freeSpaceLabel;
02673   QProgressBar *m_freeSpaceBar;
02674 
02675   KComboBox* device;
02676   QLabel* mountpoint;
02677   QCheckBox* readonly;
02678   KIconButton* unmounted;
02679 
02680   QStringList m_devicelist;
02681 };
02682 
02683 KDevicePropsPlugin::KDevicePropsPlugin( KPropertiesDialog *_props ) : KPropertiesDialogPlugin( _props ),d(new KDevicePropsPluginPrivate)
02684 {
02685   d->m_frame = new QFrame();
02686   properties->addPage(d->m_frame, i18n("De&vice"));
02687 
02688   QStringList devices;
02689   const KMountPoint::List mountPoints = KMountPoint::possibleMountPoints();
02690 
02691   for(KMountPoint::List::ConstIterator it = mountPoints.begin();
02692       it != mountPoints.end(); ++it)
02693   {
02694      const KMountPoint::Ptr mp = (*it);
02695      QString mountPoint = mp->mountPoint();
02696      QString device = mp->mountedFrom();
02697      kDebug()<<"mountPoint :"<<mountPoint<<" device :"<<device<<" mp->mountType() :"<<mp->mountType();
02698 
02699      if ((mountPoint != "-") && (mountPoint != "none") && !mountPoint.isEmpty()
02700           && device != "none")
02701      {
02702         devices.append( device + QString::fromLatin1(" (")
02703                         + mountPoint + QString::fromLatin1(")") );
02704         d->m_devicelist.append(device);
02705         d->mountpointlist.append(mountPoint);
02706      }
02707   }
02708 
02709   QGridLayout *layout = new QGridLayout( d->m_frame );
02710 
02711   layout->setMargin(0);
02712   layout->setSpacing(KDialog::spacingHint());
02713   layout->setColumnStretch(1, 1);
02714 
02715   QLabel* label;
02716   label = new QLabel( d->m_frame );
02717   label->setText( devices.count() == 0 ?
02718                       i18n("Device (/dev/fd0):") : // old style
02719                       i18n("Device:") ); // new style (combobox)
02720   layout->addWidget(label, 0, 0);
02721 
02722   d->device = new KComboBox( d->m_frame );
02723   d->device->setObjectName( QLatin1String( "ComboBox_device" ) );
02724   d->device->setEditable( true );
02725   d->device->addItems( devices );
02726   layout->addWidget(d->device, 0, 1);
02727   connect( d->device, SIGNAL( activated( int ) ),
02728            this, SLOT( slotActivated( int ) ) );
02729 
02730   d->readonly = new QCheckBox( d->m_frame );
02731   d->readonly->setObjectName( QLatin1String( "CheckBox_readonly" ) );
02732   d->readonly->setText(  i18n("Read only") );
02733   layout->addWidget(d->readonly, 1, 1);
02734 
02735   label = new QLabel( d->m_frame );
02736   label->setText( i18n("File system:") );
02737   layout->addWidget(label, 2, 0);
02738 
02739   QLabel *fileSystem = new QLabel( d->m_frame );
02740   layout->addWidget(fileSystem, 2, 1);
02741 
02742   label = new QLabel( d->m_frame );
02743   label->setText( devices.count()==0 ?
02744                       i18n("Mount point (/mnt/floppy):") : // old style
02745                       i18n("Mount point:")); // new style (combobox)
02746   layout->addWidget(label, 3, 0);
02747 
02748   d->mountpoint = new QLabel( d->m_frame );
02749   d->mountpoint->setObjectName( QLatin1String( "LineEdit_mountpoint" ) );
02750 
02751   layout->addWidget(d->mountpoint, 3, 1);
02752 
02753   // show disk free
02754   d->m_freeSpaceText = new QLabel(i18n("Free disk space:"), d->m_frame );
02755   layout->addWidget(d->m_freeSpaceText, 4, 0);
02756 
02757   d->m_freeSpaceLabel = new QLabel( d->m_frame );
02758   layout->addWidget( d->m_freeSpaceLabel, 4, 1 );
02759 
02760   d->m_freeSpaceBar = new QProgressBar( d->m_frame );
02761   d->m_freeSpaceBar->setObjectName( "freeSpaceBar" );
02762   layout->addWidget(d->m_freeSpaceBar, 5, 0, 1, 2);
02763 
02764   // we show it in the slot when we know the values
02765   d->m_freeSpaceText->hide();
02766   d->m_freeSpaceLabel->hide();
02767   d->m_freeSpaceBar->hide();
02768 
02769   KSeparator* sep = new KSeparator( Qt::Horizontal, d->m_frame);
02770   layout->addWidget(sep, 6, 0, 1, 2);
02771 
02772   d->unmounted = new KIconButton( d->m_frame );
02773   int bsize = 66 + 2 * d->unmounted->style()->pixelMetric(QStyle::PM_ButtonMargin);
02774   d->unmounted->setFixedSize(bsize, bsize);
02775   d->unmounted->setIconType(KIconLoader::Desktop, KIconLoader::Device);
02776   layout->addWidget(d->unmounted, 7, 0);
02777 
02778   label = new QLabel( i18n("Unmounted Icon"),  d->m_frame );
02779   layout->addWidget(label, 7, 1);
02780 
02781   layout->setRowStretch(8, 1);
02782 
02783   KUrl url = KIO::NetAccess::mostLocalUrl( _props->kurl(), _props );
02784   if (!url.isLocalFile()) {
02785     return;
02786   }
02787   QString path = url.path();
02788 
02789   QFile f( path );
02790   if ( !f.open( QIODevice::ReadOnly ) )
02791     return;
02792   f.close();
02793 
02794   const KDesktopFile _config( path );
02795   const KConfigGroup config = _config.desktopGroup();
02796   QString deviceStr = config.readEntry( "Dev" );
02797   QString mountPointStr = config.readEntry( "MountPoint" );
02798   bool ro = config.readEntry( "ReadOnly", false );
02799   QString unmountedStr = config.readEntry( "UnmountIcon" );
02800 
02801   fileSystem->setText(config.readEntry("FSType"));
02802 
02803   d->device->setEditText( deviceStr );
02804   if ( !deviceStr.isEmpty() ) {
02805     // Set default options for this device (first matching entry)
02806     int index = d->m_devicelist.indexOf(deviceStr);
02807     if (index != -1)
02808     {
02809       //kDebug(250) << "found it " << index;
02810       slotActivated( index );
02811     }
02812   }
02813 
02814   if ( !mountPointStr.isEmpty() )
02815   {
02816     d->mountpoint->setText( mountPointStr );
02817     updateInfo();
02818   }
02819 
02820   d->readonly->setChecked( ro );
02821 
02822   if ( unmountedStr.isEmpty() )
02823     unmountedStr = KMimeType::defaultMimeTypePtr()->iconName(); // default icon
02824 
02825   d->unmounted->setIcon( unmountedStr );
02826 
02827   connect( d->device, SIGNAL( activated( int ) ),
02828            this, SIGNAL( changed() ) );
02829   connect( d->device, SIGNAL( textChanged( const QString & ) ),
02830            this, SIGNAL( changed() ) );
02831   connect( d->readonly, SIGNAL( toggled( bool ) ),
02832            this, SIGNAL( changed() ) );
02833   connect( d->unmounted, SIGNAL( iconChanged( const QString& ) ),
02834            this, SIGNAL( changed() ) );
02835 
02836   connect( d->device, SIGNAL( textChanged( const QString & ) ),
02837            this, SLOT( slotDeviceChanged() ) );
02838 }
02839 
02840 KDevicePropsPlugin::~KDevicePropsPlugin()
02841 {
02842   delete d;
02843 }
02844 
02845 // QString KDevicePropsPlugin::tabName () const
02846 // {
02847 //   return i18n ("De&vice");
02848 // }
02849 
02850 void KDevicePropsPlugin::updateInfo()
02851 {
02852   // we show it in the slot when we know the values
02853   d->m_freeSpaceText->hide();
02854   d->m_freeSpaceLabel->hide();
02855   d->m_freeSpaceBar->hide();
02856 
02857   if ( !d->mountpoint->text().isEmpty() )
02858   {
02859     KDiskFreeSpace * job = new KDiskFreeSpace;
02860     connect(job, SIGNAL(foundMountPoint(QString, quint64, quint64, quint64)),
02861             this, SLOT(slotFoundMountPoint(QString, quint64, quint64, quint64)));
02862 
02863     job->readDF( d->mountpoint->text() );
02864   }
02865 }
02866 
02867 void KDevicePropsPlugin::slotActivated( int index )
02868 {
02869   // Update mountpoint so that it matches the device that was selected in the combo
02870   d->device->setEditText( d->m_devicelist[index] );
02871   d->mountpoint->setText( d->mountpointlist[index] );
02872 
02873   updateInfo();
02874 }
02875 
02876 void KDevicePropsPlugin::slotDeviceChanged()
02877 {
02878   // Update mountpoint so that it matches the typed device
02879   int index = d->m_devicelist.indexOf( d->device->currentText() );
02880   if ( index != -1 )
02881     d->mountpoint->setText( d->mountpointlist[index] );
02882   else
02883     d->mountpoint->setText( QString() );
02884 
02885   updateInfo();
02886 }
02887 
02888 void KDevicePropsPlugin::slotFoundMountPoint( const QString&,
02889                                               quint64 kibSize,
02890                                               quint64 /*kibUsed*/,
02891                                               quint64 kibAvail )
02892 {
02893   d->m_freeSpaceText->show();
02894   d->m_freeSpaceLabel->show();
02895 
02896   int percUsed = 100 - (int)(100.0 * kibAvail / kibSize);
02897 
02898   d->m_freeSpaceLabel->setText(
02899       i18nc("Available space out of total partition size (percent used)", "%1 out of %2 (%3% used)",
02900        KIO::convertSizeFromKiB(kibAvail),
02901        KIO::convertSizeFromKiB(kibSize),
02902         100 - (int)(100.0 * kibAvail / kibSize) ));
02903 
02904   d->m_freeSpaceBar->setRange(0, 100);
02905   d->m_freeSpaceBar->setValue(percUsed);
02906   d->m_freeSpaceBar->show();
02907 }
02908 
02909 bool KDevicePropsPlugin::supports( const KFileItemList& _items )
02910 {
02911   if ( _items.count() != 1 )
02912     return false;
02913   const KFileItem item = _items.first();
02914   // check if desktop file
02915   if (!item.isDesktopFile())
02916     return false;
02917 
02918   // open file and check type
02919   bool isLocal;
02920   KUrl url = item.mostLocalUrl(isLocal);
02921   if (!isLocal) {
02922       return false;
02923   }
02924 
02925   KDesktopFile config( url.path() );
02926   return config.hasDeviceType();
02927 }
02928 
02929 void KDevicePropsPlugin::applyChanges()
02930 {
02931   KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
02932   if ( !url.isLocalFile() )
02933     return;
02934   QString path = url.path();
02935 
02936   QFile f( path );
02937   if ( !f.open( QIODevice::ReadWrite ) )
02938   {
02939     KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have sufficient "
02940                 "access to write to <b>%1</b>.</qt>", path));
02941     return;
02942   }
02943   f.close();
02944 
02945   KDesktopFile _config( path );
02946   KConfigGroup config = _config.desktopGroup();
02947   config.writeEntry( "Type", QString::fromLatin1("FSDevice") );
02948 
02949   config.writeEntry( "Dev", d->device->currentText() );
02950   config.writeEntry( "MountPoint", d->mountpoint->text() );
02951 
02952   config.writeEntry( "UnmountIcon", d->unmounted->icon() );
02953   kDebug(250) << "d->unmounted->icon() = " << d->unmounted->icon();
02954 
02955   config.writeEntry( "ReadOnly", d->readonly->isChecked() );
02956 
02957   config.sync();
02958 }
02959 
02960 
02961 /* ----------------------------------------------------
02962  *
02963  * KDesktopPropsPlugin
02964  *
02965  * -------------------------------------------------- */
02966 
02967 class KDesktopPropsPlugin::KDesktopPropsPluginPrivate
02968 {
02969 public:
02970   KDesktopPropsPluginPrivate()
02971     : w( new Ui_KPropertiesDesktopBase )
02972     , m_frame( new QFrame() )
02973   {
02974   }
02975   ~KDesktopPropsPluginPrivate()
02976   {
02977     delete w;
02978   }
02979   Ui_KPropertiesDesktopBase* w;
02980   QWidget *m_frame;
02981 
02982   QString m_origCommandStr;
02983   QString m_terminalOptionStr;
02984   QString m_suidUserStr;
02985   QString m_dbusStartupType;
02986   QString m_dbusServiceName;
02987   bool m_terminalBool;
02988   bool m_suidBool;
02989   bool m_startupBool;
02990   bool m_systrayBool;
02991 };
02992 
02993 KDesktopPropsPlugin::KDesktopPropsPlugin( KPropertiesDialog *_props )
02994   : KPropertiesDialogPlugin( _props ), d( new KDesktopPropsPluginPrivate )
02995 {
02996   properties->addPage(d->m_frame, i18n("&Application"));
02997 
02998   d->w->setupUi(d->m_frame);
02999 
03000   bool bKDesktopMode = (qApp->objectName() == "kdesktop");
03001 
03002   if (bKDesktopMode)
03003   {
03004     // Hide Name entry
03005     d->w->nameEdit->hide();
03006     d->w->nameLabel->hide();
03007   }
03008 
03009   d->w->pathEdit->setMode(KFile::Directory | KFile::LocalOnly);
03010   d->w->pathEdit->lineEdit()->setAcceptDrops(false);
03011 
03012   connect( d->w->nameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
03013   connect( d->w->genNameEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
03014   connect( d->w->commentEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
03015   connect( d->w->commandEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
03016   connect( d->w->pathEdit, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
03017 
03018   connect( d->w->browseButton, SIGNAL( clicked() ), this, SLOT( slotBrowseExec() ) );
03019   connect( d->w->addFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotAddFiletype() ) );
03020   connect( d->w->delFiletypeButton, SIGNAL( clicked() ), this, SLOT( slotDelFiletype() ) );
03021   connect( d->w->advancedButton, SIGNAL( clicked() ), this, SLOT( slotAdvanced() ) );
03022 
03023   // now populate the page
03024 
03025   KUrl url = KIO::NetAccess::mostLocalUrl( _props->kurl(), _props );
03026   if (!url.isLocalFile()) {
03027     return;
03028   }
03029   QString path = url.path();
03030 
03031   QFile f( path );
03032   if ( !f.open( QIODevice::ReadOnly ) )
03033     return;
03034   f.close();
03035 
03036   KDesktopFile  _config( path );
03037   KConfigGroup config = _config.desktopGroup();
03038   QString nameStr = _config.readName();
03039   QString genNameStr = _config.readGenericName();
03040   QString commentStr = _config.readComment();
03041   QString commandStr = config.readEntry( "Exec", QString() );
03042   if (commandStr.startsWith(QLatin1String("ksystraycmd ")))
03043   {
03044     commandStr.remove(0, 12);
03045     d->m_systrayBool = true;
03046   }
03047   else
03048     d->m_systrayBool = false;
03049 
03050   d->m_origCommandStr = commandStr;
03051   QString pathStr = config.readEntry( "Path", QString() ); // not readPathEntry, see kservice.cpp
03052   d->m_terminalBool = config.readEntry( "Terminal", false );
03053   d->m_terminalOptionStr = config.readEntry( "TerminalOptions" );
03054   d->m_suidBool = config.readEntry( "X-KDE-SubstituteUID", false );
03055   d->m_suidUserStr = config.readEntry( "X-KDE-Username" );
03056   if( config.hasKey( "StartupNotify" ))
03057     d->m_startupBool = config.readEntry( "StartupNotify", true );
03058   else
03059     d->m_startupBool = config.readEntry( "X-KDE-StartupNotify", true );
03060   d->m_dbusStartupType = config.readEntry("X-DBUS-StartupType").toLower();
03061   //Compatibility
03062   if( d->m_dbusStartupType.isEmpty() && config.hasKey("X-DCOP-ServiceType"))
03063          d->m_dbusStartupType = config.readEntry("X-DCOP-ServiceType").toLower();
03064   // ### should there be a GUI for this setting?
03065   // At least we're copying it over to the local file, to avoid side effects (#157853)
03066   d->m_dbusServiceName = config.readEntry("X-DBUS-ServiceName");
03067 
03068   const QStringList mimeTypes = config.readXdgListEntry( "MimeType" );
03069 
03070   if ( nameStr.isEmpty() || bKDesktopMode ) {
03071     // We'll use the file name if no name is specified
03072     // because we _need_ a Name for a valid file.
03073     // But let's do it in apply, not here, so that we pick up the right name.
03074     setDirty();
03075   }
03076   if ( !bKDesktopMode )
03077     d->w->nameEdit->setText(nameStr);
03078 
03079   d->w->genNameEdit->setText( genNameStr );
03080   d->w->commentEdit->setText( commentStr );
03081   d->w->commandEdit->setText( commandStr );
03082   d->w->pathEdit->lineEdit()->setText( pathStr );
03083 
03084   // was: d->w->filetypeList->setFullWidth(true);
03085 //  d->w->filetypeList->header()->setStretchEnabled(true, d->w->filetypeList->columns()-1);
03086 
03087   KMimeType::Ptr defaultMimetype = KMimeType::defaultMimeTypePtr();
03088   for(QStringList::ConstIterator it = mimeTypes.begin();
03089       it != mimeTypes.end(); )
03090   {
03091     KMimeType::Ptr p = KMimeType::mimeType(*it, KMimeType::ResolveAliases);
03092     ++it;
03093     QString preference;
03094     if (it != mimeTypes.end())
03095     {
03096        bool numeric;
03097        (*it).toInt(&numeric);
03098        if (numeric)
03099        {
03100          preference = *it;
03101          ++it;
03102        }
03103     }
03104     if (p)
03105     {
03106       QTreeWidgetItem *item = new QTreeWidgetItem();
03107       item->setText(0, p->name());
03108       item->setText(1, p->comment());
03109       item->setText(2, preference);
03110       d->w->filetypeList->addTopLevelItem(item);
03111     }
03112   }
03113   d->w->filetypeList->resizeColumnToContents(0);
03114 
03115 }
03116 
03117 KDesktopPropsPlugin::~KDesktopPropsPlugin()
03118 {
03119   delete d;
03120 }
03121 
03122 void KDesktopPropsPlugin::slotAddFiletype()
03123 {
03124     KMimeTypeChooserDialog dlg( i18n("Add File Type for %1", properties->kurl().fileName()),
03125                                 i18n("Select one or more file types to add:"),
03126                                 QStringList(), // no preselected mimetypes
03127                                 QString(),
03128                                 QStringList(),
03129                                 KMimeTypeChooser::Comments|KMimeTypeChooser::Patterns,
03130                                 d->m_frame );
03131 
03132     if (dlg.exec() == KDialog::Accepted)
03133     {
03134         foreach(const QString &mimetype, dlg.chooser()->mimeTypes())
03135         {
03136             KMimeType::Ptr p = KMimeType::mimeType(mimetype);
03137             if (!p)
03138                 continue;
03139 
03140             bool found = false;
03141             int count = d->w->filetypeList->topLevelItemCount();
03142             for (int i = 0; !found && i < count; ++i) {
03143               if (d->w->filetypeList->topLevelItem(i)->text(0) == mimetype) {
03144                 found = true;
03145               }
03146             }
03147             if (!found) {
03148               QTreeWidgetItem *item = new QTreeWidgetItem();
03149               item->setText(0, p->name());
03150               item->setText(1, p->comment());
03151               d->w->filetypeList->addTopLevelItem(item);
03152             }
03153             d->w->filetypeList->resizeColumnToContents(0);
03154         }
03155     }
03156     emit changed();
03157 }
03158 
03159 void KDesktopPropsPlugin::slotDelFiletype()
03160 {
03161     QTreeWidgetItem *cur = d->w->filetypeList->currentItem();
03162     if (cur) {
03163       delete cur;
03164       emit changed();
03165     }
03166 }
03167 
03168 void KDesktopPropsPlugin::checkCommandChanged()
03169 {
03170   if (KRun::binaryName(d->w->commandEdit->text(), true) !=
03171       KRun::binaryName(d->m_origCommandStr, true))
03172   {
03173     d->m_origCommandStr = d->w->commandEdit->text();
03174     d->m_dbusStartupType.clear(); // Reset
03175     d->m_dbusServiceName.clear();
03176   }
03177 }
03178 
03179 void KDesktopPropsPlugin::applyChanges()
03180 {
03181   kDebug(250) << "KDesktopPropsPlugin::applyChanges";
03182 
03183   KUrl url = KIO::NetAccess::mostLocalUrl( properties->kurl(), properties );
03184   if (!url.isLocalFile()) {
03185     //FIXME: 4.2 add this: KMessageBox::sorry(0, i18n("Could not save properties. Only entries on local file systems are supported."));
03186     return;
03187   }
03188   QString path = url.path();
03189 
03190   QFile f( path );
03191 
03192   if ( !f.open( QIODevice::ReadWrite ) ) {
03193     KMessageBox::sorry( 0, i18n("<qt>Could not save properties. You do not have "
03194                 "sufficient access to write to <b>%1</b>.</qt>", path));
03195     return;
03196   }
03197   f.close();
03198 
03199   // If the command is changed we reset certain settings that are strongly
03200   // coupled to the command.
03201   checkCommandChanged();
03202 
03203   KDesktopFile _config( path );
03204   KConfigGroup config = _config.desktopGroup();
03205   config.writeEntry( "Type", QString::fromLatin1("Application"));
03206   config.writeEntry( "Comment", d->w->commentEdit->text() );
03207   config.writeEntry( "Comment", d->w->commentEdit->text(), KConfigGroup::Persistent|KConfigGroup::Localized ); // for compat
03208   config.writeEntry( "GenericName", d->w->genNameEdit->text() );
03209   config.writeEntry( "GenericName", d->w->genNameEdit->text(), KConfigGroup::Persistent|KConfigGroup::Localized ); // for compat
03210 
03211   if (d->m_systrayBool)
03212     config.writeEntry( "Exec", d->w->commandEdit->text().prepend("ksystraycmd ") );
03213   else
03214     config.writeEntry( "Exec", d->w->commandEdit->text() );
03215   config.writeEntry( "Path", d->w->pathEdit->lineEdit()->text() ); // not writePathEntry, see kservice.cpp
03216 
03217   // Write mimeTypes
03218   QStringList mimeTypes;
03219   int count = d->w->filetypeList->topLevelItemCount();
03220   for (int i = 0; i < count; ++i) {
03221     QTreeWidgetItem *item = d->w->filetypeList->topLevelItem(i);
03222     QString preference = item->text(2);
03223     mimeTypes.append(item->text(0));
03224     if (!preference.isEmpty())
03225        mimeTypes.append(preference);
03226   }
03227 
03228   kDebug() << mimeTypes;
03229   config.writeXdgListEntry( "MimeType", mimeTypes );
03230 
03231   if ( !d->w->nameEdit->isHidden() ) {
03232       QString nameStr = d->w->nameEdit->text();
03233       config.writeEntry( "Name", nameStr );
03234       config.writeEntry( "Name", nameStr, KConfigGroup::Persistent|KConfigGroup::Localized );
03235   }
03236 
03237   config.writeEntry("Terminal", d->m_terminalBool);
03238   config.writeEntry("TerminalOptions", d->m_terminalOptionStr);
03239   config.writeEntry("X-KDE-SubstituteUID", d->m_suidBool);
03240   config.writeEntry("X-KDE-Username", d->m_suidUserStr);
03241   config.writeEntry("StartupNotify", d->m_startupBool);
03242   config.writeEntry("X-DBUS-StartupType", d->m_dbusStartupType);
03243   config.writeEntry("X-DBUS-ServiceName", d->m_dbusServiceName);
03244   config.sync();
03245 
03246   // KSycoca update needed?
03247   QString sycocaPath = KGlobal::dirs()->relativeLocation("apps", path);
03248   bool updateNeeded = !sycocaPath.startsWith('/');
03249   if (!updateNeeded)
03250   {
03251      sycocaPath = KGlobal::dirs()->relativeLocation("xdgdata-apps", path);
03252      updateNeeded = !sycocaPath.startsWith('/');
03253   }
03254   if (updateNeeded)
03255      KBuildSycocaProgressDialog::rebuildKSycoca(d->m_frame);
03256 }
03257 
03258 
03259 void KDesktopPropsPlugin::slotBrowseExec()
03260 {
03261   KUrl f = KFileDialog::getOpenUrl( KUrl(),
03262                                       QString(), d->m_frame );
03263   if ( f.isEmpty() )
03264     return;
03265 
03266   if ( !f.isLocalFile()) {
03267     KMessageBox::sorry(d->m_frame, i18n("Only executables on local file systems are supported."));
03268     return;
03269   }
03270 
03271   QString path = f.path();
03272   path = KShell::quoteArg( path );
03273   d->w->commandEdit->setText( path );
03274 }
03275 
03276 void KDesktopPropsPlugin::slotAdvanced()
03277 {
03278   KDialog dlg( d->m_frame );
03279   dlg.setObjectName( "KPropertiesDesktopAdv" );
03280   dlg.setModal( true );
03281   dlg.setCaption( i18n("Advanced Options for %1", properties->kurl().fileName()) );
03282   dlg.setButtons( KDialog::Ok | KDialog::Cancel );
03283   dlg.setDefaultButton( KDialog::Ok );
03284   Ui_KPropertiesDesktopAdvBase w;
03285   w.setupUi(dlg.mainWidget());
03286 
03287   // If the command is changed we reset certain settings that are strongly
03288   // coupled to the command.
03289   checkCommandChanged();
03290 
03291   // check to see if we use konsole if not do not add the nocloseonexit
03292   // because we don't know how to do this on other terminal applications
03293   KConfigGroup confGroup( KGlobal::config(), QString::fromLatin1("General") );
03294   QString preferredTerminal = confGroup.readPathEntry("TerminalApplication",
03295                           QString::fromLatin1("konsole"));
03296 
03297   bool terminalCloseBool = false;
03298 
03299   if (preferredTerminal == "konsole")
03300   {
03301      terminalCloseBool = (d->m_terminalOptionStr.contains( "--noclose" ) > 0);
03302      w.terminalCloseCheck->setChecked(terminalCloseBool);
03303      d->m_terminalOptionStr.remove( "--noclose");
03304   }
03305   else
03306   {
03307      w.terminalCloseCheck->hide();
03308   }
03309 
03310   w.terminalCheck->setChecked(d->m_terminalBool);
03311   w.terminalEdit->setText(d->m_terminalOptionStr);
03312   w.terminalCloseCheck->setEnabled(d->m_terminalBool);
03313   w.terminalEdit->setEnabled(d->m_terminalBool);
03314   w.terminalEditLabel->setEnabled(d->m_terminalBool);
03315 
03316   w.suidCheck->setChecked(d->m_suidBool);
03317   w.suidEdit->setText(d->m_suidUserStr);
03318   w.suidEdit->setEnabled(d->m_suidBool);
03319   w.suidEditLabel->setEnabled(d->m_suidBool);
03320 
03321   w.startupInfoCheck->setChecked(d->m_startupBool);
03322   w.systrayCheck->setChecked(d->m_systrayBool);
03323 
03324   if (d->m_dbusStartupType == "unique")
03325     w.dbusCombo->setCurrentIndex(2);
03326   else if (d->m_dbusStartupType == "multi")
03327     w.dbusCombo->setCurrentIndex(1);
03328   else if (d->m_dbusStartupType == "wait")
03329     w.dbusCombo->setCurrentIndex(3);
03330   else
03331     w.dbusCombo->setCurrentIndex(0);
03332 
03333   // Provide username completion up to 1000 users.
03334   KCompletion *kcom = new KCompletion;
03335   kcom->setOrder(KCompletion::Sorted);
03336   struct passwd *pw;
03337   int i, maxEntries = 1000;
03338   setpwent();
03339   for (i=0; ((pw = getpwent()) != 0L) && (i < maxEntries); i++)
03340     kcom->addItem(QString::fromLatin1(pw->pw_name));
03341   endpwent();
03342   if (i < maxEntries)
03343   {
03344     w.suidEdit->setCompletionObject(kcom, true);
03345     w.suidEdit->setAutoDeleteCompletionObject( true );
03346     w.suidEdit->setCompletionMode(KGlobalSettings::CompletionAuto);
03347   }
03348   else
03349   {
03350     delete kcom;
03351   }
03352 
03353   connect( w.terminalEdit, SIGNAL( textChanged( const QString & ) ),
03354            this, SIGNAL( changed() ) );
03355   connect( w.terminalCloseCheck, SIGNAL( toggled( bool ) ),
03356            this, SIGNAL( changed() ) );
03357   connect( w.terminalCheck, SIGNAL( toggled( bool ) ),
03358            this, SIGNAL( changed() ) );
03359   connect( w.suidCheck, SIGNAL( toggled( bool ) ),
03360            this, SIGNAL( changed() ) );
03361   connect( w.suidEdit, SIGNAL( textChanged( const QString & ) ),
03362            this, SIGNAL( changed() ) );
03363   connect( w.startupInfoCheck, SIGNAL( toggled( bool ) ),
03364            this, SIGNAL( changed() ) );
03365   connect( w.systrayCheck, SIGNAL( toggled( bool ) ),
03366            this, SIGNAL( changed() ) );
03367   connect( w.dbusCombo, SIGNAL( activated( int ) ),
03368            this, SIGNAL( changed() ) );
03369 
03370   if ( dlg.exec() == QDialog::Accepted )
03371   {
03372     d->m_terminalOptionStr = w.terminalEdit->text().trimmed();
03373     d->m_terminalBool = w.terminalCheck->isChecked();
03374     d->m_suidBool = w.suidCheck->isChecked();
03375     d->m_suidUserStr = w.suidEdit->text().trimmed();
03376     d->m_startupBool = w.startupInfoCheck->isChecked();
03377     d->m_systrayBool = w.systrayCheck->isChecked();
03378 
03379     if (w.terminalCloseCheck->isChecked())
03380     {
03381       d->m_terminalOptionStr.append(" --noclose");
03382     }
03383 
03384     switch(w.dbusCombo->currentIndex())
03385     {
03386       case 1:  d->m_dbusStartupType = "multi"; break;
03387       case 2:  d->m_dbusStartupType = "unique"; break;
03388       case 3:  d->m_dbusStartupType = "wait"; break;
03389       default: d->m_dbusStartupType = "none"; break;
03390     }
03391   }
03392 }
03393 
03394 bool KDesktopPropsPlugin::supports( const KFileItemList& _items )
03395 {
03396     if ( _items.count() != 1 ) {
03397         return false;
03398     }
03399 
03400     const KFileItem item = _items.first();
03401 
03402     // check if desktop file
03403     if (!item.isDesktopFile()) {
03404         return false;
03405     }
03406 
03407     // open file and check type
03408     bool isLocal;
03409     KUrl url = item.mostLocalUrl( isLocal );
03410     if (!isLocal) {
03411         return false;
03412     }
03413 
03414     KDesktopFile config( url.path() );
03415     return config.hasApplicationType() &&
03416            KAuthorized::authorize("run_desktop_files") &&
03417            KAuthorized::authorize("shell_access");
03418 }
03419 
03420 #include "kpropertiesdialog.moc"
03421 #include "kpropertiesdialog_p.moc"
03422 

KIO

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal