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

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
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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