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

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
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
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