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

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
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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