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

KDE's Doxygen guidelines are available online.

KIO

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

kdelibs-4.8.3 API Reference

Skip menu "kdelibs-4.8.3 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal